본문 바로가기
카테고리 없음

[Flutter] FutureBuilder, StreamBuilder

by yuraming 2024. 6. 6.

 

https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html

https://api.flutter.dev/flutter/widgets/StreamBuilder-class.html

 

 


 

FutureBuilder

 

FutureBuilder 위젯은 Future를 취하고 Future의 상태를 기반으로 위젯 트리를 구축한다. 

데이터를 가져오는 등 비동기 작업을 한 번 수행 후, 작업 진행중, 완료, 오류 발생여부에 따라

다른 UI를 렌더링하고자하는 경우에 적합하다.

 

파일, 데이터 가져오기, http요청 등 일회성 응답에 사용

 

async/await 키워드를 사용하여 비동기를 처리하며, FutureBuilder에서 는 비동기로 처리할 타입이 된다.

builder에서는 AsyncSnapshot인자를 통해서 에 넘겨진 타입이 비동기로 처리되는 동안의 상태를 처리한다.

import 'package:flutter/material.dart';

class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}
//future안에서 값이 반환될 때마다 빌더를 새로 실행하게 됨
class _HomeScreenState extends State<HomeScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: FutureBuilder<int>(
        future: getNumber(),
        builder: (BuildContext context, AsyncSnapshot<int> snapshot){
          // snapshot.data: future에서 반환해주는 값

          print('----data----');
          print(snapshot.connectionState);
          print(snapshot.data);

          // error
          if(snapshot.hasError){
           final error = snapshot.error;

           return Center(
             child: Text('에러: $error'),
           );
          }
          
          // 데이터가 존재하는지 확인
          if(snapshot.hasData){
            final data = snapshot.data;
            return Center(
              child: Text(data.toString()),
            );
          }
          return Center(
            child: Text('데이터가 없습니다'),
          );
        },
      ),
    );
  }

  Future<int> getNumber() async {
    await Future.delayed(Duration(seconds: 3));
    final random = Random();
    throw '에러!!';
    return random.nextInt(100);

  }
}

 


 

StreamBuilder

StreamBuilder 위젯은 Stream을 가져와 Stream의 최신 스냅샷을 기반으로 위젯 트리를 다시 빌드한다.

WebSocket 또는 데이터베이스에서 실시간 데이터 수신과 같이 지속적인 업데이트에 반응하려는 경우에 사용

FutureBuilder와 달리 async 가 아닌 async* 를 사용하고, return 대신 yield를 사용한다.

 

StreamBuilder를 사용하면 setState()를 쓰지 않고도 UI를 업데이트 및 최신의 값을 가져올 수 있다.

import 'package:flutter/material.dart';

class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

//future안에서 값이 반환될 때마다 빌더를 새로 실행하게 됨
class _HomeScreenState extends State<HomeScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: StreamBuilder<int>(
        stream: streamNumber(),
        builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
          // snapshot.data // future에서 반환해주는 값을 받음

          print('----data----');
          print(snapshot.connectionState);
          print(snapshot.data);

          if (snapshot.connectionState == ConnectionState.active) {
            // 다른 위젯을 보여줌
            return SizedBox(
              width: double.infinity,
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  CircularProgressIndicator(),
                  Text(snapshot.data.toString()),
                ],
              ),
            );
          }

          // error
          if (snapshot.hasError) {
            final error = snapshot.error;

            return Center(
              child: Text('에러: $error'),
            );
          }
          // 데이터가 존재하는지 확인
          if (snapshot.hasData) {
            final data = snapshot.data;
            return Center(
              child: Text(data.toString()),
            );
          }
          return Center(
            child: Text('데이터가 없습니다'),
          );
        },
      ),
    );
  }

  Stream<int> streamNumber() async* {
    for (int i = 0; i < 10; i++) {
      await Future.delayed(Duration(seconds: 1));

      // if (i == 5) {
      //   throw '에러!!';
      // }
      yield i;
    }
  }
}

  Stream<int> streamNumber() async* {
   for(int i = 0; i < 10; i++){
     await Future.delayed(Duration(seconds: 1));

     if(i == 5){
       throw '에러!!';
     }
     yield i;
   }
  }

 

 


 

공통

  • 비동기로 데이터를 받아올 때 사용에 적절.
  • 4가지의 ConnectionState가 존재
  • ConnectionState.none - null 일 때 initialData, defaultValue 사용
  • ConnectionState.active - Stream에서만 존재 / 진행중 (null이 아님)
  • ConnectionState.waiting - 실행중
  • ConnectionState.done - Future 또는 Stream이 종료되었을 때

 

차이점

FutureBuilder

단일 비동기 작업에 사용.
Future 완성을 기준으로 1회 빌드됨.

 

StreamBuilder

지속적인 비동기 작업에 사용.
Stream의 업데이트를 기반으로 여러 번 빌드됨.

async 가 아닌 async* 를 사용.

return 이 아닌 yield를 사용.