16. Flutter 수업- Future와 FutureBuilder

Future,FutureBuilder
홍윤's avatar
Oct 09, 2024
16. Flutter 수업- Future와 FutureBuilder
 

1. Future와 FutureBuilder

💡
  • FutureFutureBuilder비동기 프로그래밍에서 매우 중요한 역할을 하는 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를 업데이트하는 역할을 합니다.
FutureBuilderFuture의 상태에 따라 다음과 같은 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}"); // 데이터가 준비되었을 때 } }, ); } }
이 예시에서 FutureBuilderfetchData() 함수를 호출하여 Future<String>을 생성하고, 이 작업이 완료되면 UI에 데이터를 반영합니다. snapshot 객체는 Future의 상태를 나타내며, 그에 따라 UI를 적절하게 업데이트합니다:
  • 대기 중: CircularProgressIndicator를 통해 로딩 상태를 보여줍니다.
  • 오류 발생: 오류 메시지를 출력합니다.
  • 데이터가 준비됨: 데이터를 화면에 출력합니다.

차이점 요약

  • Future는 비동기 작업의 결과를 나타내는 객체로, 비동기 작업이 끝나면 값을 반환하거나 예외를 던집니다.
  • FutureBuilderFuture의 상태에 따라 UI를 동적으로 업데이트할 수 있는 위젯입니다. 주로 네트워크 요청이나 긴 작업의 결과를 UI에 반영할 때 사용됩니다.
따라서 FutureBuilder는 Flutter의 UI 상태를 관리하고 업데이트하는 데 매우 유용한 도구입니다. 반면, Future는 단순히 비동기 작업의 결과를 관리하는 역할을 합니다.
 
 

2. 수업 예제 코드 F페이지 개선하기

💡
  1. main 페이지 로딩
  1. A 페이지로 이동
  1. A 페이지에서 HomeRepositoryfetchNumber() 메서드를 호출해 데이터를 가져옴
  1. A 페이지가 로딩될 때, 데이터를 받아오는 처리 로직이 없기 때문에 페이지가 로딩 없이 바로 출력
이를 개선하기 위해 F 페이지에서는 비동기 처리 로직을 추가하여, 데이터를 받아오는 동안 로딩 상태를 표시하고, 값이 준비된 후에 화면을 업데이트하는 방식으로 구현되었습니다.
Future 타입의 데이터를 받아오려면 FutureBuilder 위젯을 사용하는 것이 적절합니다.
  • FutureBuilderfuturebuilder라는 두 가지 주요 속성을 가지고 있습니다.
    • future: 비동기 작업을 수행하는 Future 객체를 전달합니다.
    • builder: future가 완료되면 이 빌더 함수가 실행됩니다. 여기서 snapshot.data를 통해 비동기 작업의 결과를 받을 수 있습니다.
다음과 같은 작업을 해보겠습니다.
  1. 먼저, main에서 A 페이지 로딩 문제를 확인합니다. 이때 데이터를 받아오지 못하고 바로 화면이 그려지는 문제를 분석합니다.
  1. 이후, F 페이지를 로딩하여 비동기 작업의 결과가 올바르게 반영되는지 확인합니다.
  1. 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

Uni