1. Future와 FutureBuilder
Future
와FutureBuilder
는 비동기 프로그래밍에서 매우 중요한 역할을 하는 Flutter 위젯입니다. 각각의 기능과 역할을 비교해서 설명해보겠습니다.
1. Future
Future<T>
는 비동기 작업의 결과를 나타내는 객체입니다.Future
는 비동기 작업이 끝난 후 성공적으로 완료되면 값을 반환하고, 실패하면 예외를 던집니다. 비동기 작업이 완료될 때까지 애플리케이션의 나머지 작업은 계속 진행될 수 있습니다.
예시:
Future<String> fetchData() async {
await Future.delayed(Duration(seconds: 2)); // 2초 기다리기
return "데이터 로드 완료";
}
void main() async {
String result = await fetchData(); // 작업 완료를 대기
print(result); // "데이터 로드 완료" 출력
}
이 예시에서는
Future
가 2초 동안 대기한 후 결과를 반환합니다. 결과가 준비되면 해당 결과를 받아 처리할 수 있습니다.2. FutureBuilder
FutureBuilder
는 비동기 작업을 처리하고 그 결과를 UI에 반영하는 위젯입니다. 즉, 비동기 작업(Future
)의 상태를 감지하고, 그 상태에 따라 UI를 업데이트하는 역할을 합니다.
FutureBuilder
는 Future
의 상태에 따라 다음과 같은 UI 상태를 처리합니다:- 초기 상태 (ConnectionState.none):
Future
가 아직 실행되지 않았을 때.
- 대기 상태 (ConnectionState.waiting):
Future
가 실행 중일 때.
- 완료 상태 (ConnectionState.done):
Future
가 완료되었을 때.
FutureBuilder
예시:
class MyWidget extends StatelessWidget {
Future<String> fetchData() async {
await Future.delayed(Duration(seconds: 2)); // 2초 대기
return "데이터 로드 완료";
}
@override
Widget build(BuildContext context) {
return FutureBuilder<String>(
future: fetchData(), // 비동기 작업을 넘김
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator(); // 대기 중일 때
} else if (snapshot.hasError) {
return Text("오류 발생: ${snapshot.error}");
} else {
return Text("결과: ${snapshot.data}"); // 데이터가 준비되었을 때
}
},
);
}
}
이 예시에서
FutureBuilder
는 fetchData()
함수를 호출하여 Future<String>
을 생성하고, 이 작업이 완료되면 UI에 데이터를 반영합니다. snapshot
객체는 Future
의 상태를 나타내며, 그에 따라 UI
를 적절하게 업데이트합니다:- 대기 중:
CircularProgressIndicator
를 통해 로딩 상태를 보여줍니다.
- 오류 발생: 오류 메시지를 출력합니다.
- 데이터가 준비됨: 데이터를 화면에 출력합니다.
차이점 요약
Future
는 비동기 작업의 결과를 나타내는 객체로, 비동기 작업이 끝나면 값을 반환하거나 예외를 던집니다.
FutureBuilder
는Future
의 상태에 따라 UI를 동적으로 업데이트할 수 있는 위젯입니다. 주로 네트워크 요청이나 긴 작업의 결과를 UI에 반영할 때 사용됩니다.
따라서
FutureBuilder
는 Flutter의 UI 상태를 관리하고 업데이트하는 데 매우 유용한 도구입니다. 반면, Future
는 단순히 비동기 작업의 결과를 관리하는 역할을 합니다.2. 수업 예제 코드 F페이지 개선하기
- main 페이지 로딩
- A 페이지로 이동
- A 페이지에서
HomeRepository
의fetchNumber()
메서드를 호출해 데이터를 가져옴
- A 페이지가 로딩될 때, 데이터를 받아오는 처리 로직이 없기 때문에 페이지가 로딩 없이 바로 출력됨
이를 개선하기 위해 F 페이지에서는 비동기 처리 로직을 추가하여, 데이터를 받아오는 동안 로딩 상태를 표시하고, 값이 준비된 후에 화면을 업데이트하는 방식으로 구현되었습니다.
Future
타입의 데이터를 받아오려면 FutureBuilder
위젯을 사용하는 것이 적절합니다.FutureBuilder
는future
와builder
라는 두 가지 주요 속성을 가지고 있습니다.future:
비동기 작업을 수행하는Future
객체를 전달합니다.builder:
future
가 완료되면 이 빌더 함수가 실행됩니다. 여기서snapshot.data
를 통해 비동기 작업의 결과를 받을 수 있습니다.
다음과 같은 작업을 해보겠습니다.
- 먼저, main에서 A 페이지 로딩 문제를 확인합니다. 이때 데이터를 받아오지 못하고 바로 화면이 그려지는 문제를 분석합니다.
- 이후, F 페이지를 로딩하여 비동기 작업의 결과가 올바르게 반영되는지 확인합니다.
- F 페이지를 개선해나가면서 비동기 데이터 로딩 처리와 관련된 세부 사항을 추가적으로 조정합니다.
1. main.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:homeapp/a_page.dart';
void main() {
runApp(ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
//A페이지를 그리는데 A페이지에 가보면 HomeRepository에서 Future.delayed로 인해 3초 뒤에 int값이 리턴된다.
//즉, int값을 받기 전에 A페이지를 그렸기 때문에 제대로 출력되지 않았다.
home: APage(),
);
}
}
2. a_page.dart
import 'package:flutter/material.dart';
import 'package:homeapp/home_repository.dart';
class APage extends StatelessWidget {
const APage({super.key});
@override
Widget build(BuildContext context) {
//fetchNumber의 리턴 타입은 Future이다.
return Scaffold(body: Text("넘버 : ${HomeRepository().fetchNumber()}"));
}
}
3. home_repository.dart
// 통신 담당
class HomeRepository {
//Futre는 promise다. 약속을 전달.
//Future.delayed는 쓰레드의 sleep과 같다.
//MVVM 패턴은 상태관리를 하는 것이고 View 관리를 따로 할 필요 없이
//상태 변경을 해주면 이를 구독중인 View들이 바뀌는 것이다.
//우선 통신으로 연습하기 전에 Future.delayed로 통신한다 가정하고 연습을 해볼 것이다.
Future<int> fetchNumber() {
return Future.delayed(Duration(seconds: 3), () => 3);
}
int fetchNumberV2() {
return 5;
}
}
4. f_page.dart
A 페이지에서 로딩을 처리하지 못하는 문제를 발견했으므로, 이를 개선한 F 페이지로 실행해 확인해본다.
import 'package:flutter/material.dart';
import 'package:homeapp/home_repository.dart';
class FPage extends StatelessWidget {
const FPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(children: [
Container(
color: Colors.yellow,
height: 300,
),
FutureBuilder(
//future가 완료되면 builder가 실행된다.
future: HomeRepository().fetchNumber(),
builder: (context, snapshot) => Text(
"넘버 : ${snapshot.data}",
style: TextStyle(fontSize: 30),
),
),
]),
);
}
}
builder의 화살표 함수를 익명 함수로 변경하고, 값이 들어오면 해당 값을 출력하며, null일 때는 로딩 중임을 나타내는 Circle Progress Indicator가 돌아가도록 개선하였다.
이때 builder에서 데이터는
snapshot.data
로 전달된다.5. f_page.dart(로딩 화면 개선된 f_page)
import 'package:flutter/material.dart';
import 'package:homeapp/home_repository.dart';
class FPage extends StatelessWidget {
const FPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Container(
color: Colors.yellow,
height: 300,
),
FutureBuilder(
//future가 완료되면 builder가 실행된다.
future: HomeRepository().fetchNumber(),
builder: (context, snapshot) {
//데이터가 들어올 때
if (snapshot.hasData) {
return Center(
child: Text(
"${snapshot.data}",
style: TextStyle(fontSize: 30),
),
);
} else {
return CircularProgressIndicator();
}
},
),
],
),
);
}
}
Share article