[이론] FutureBuilder 와 StreamBuilder
들어가기 앞서...
비동기 통신을 하면서 FutureBuilder 와 StreamBuilder 를 사용하게되는데, 무엇인지와 사용법을 알아보자!
본문으로...
FutureBuilder 배우기
1. 기본 세팅!
import 'dart:math';
import 'package:flutter/material.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final textStyle = TextStyle(
fontSize: 16.0,
);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(8.0),
child: FutureBuilder(
builder: (context, snapshot) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'FutureBuilder',
style: textStyle.copyWith(
fontWeight: FontWeight.w700,
fontSize: 20.0,
),
),
Text(
'ConState: ${snapshot.connectionState}',
style: textStyle,
),
Text(
'Data: ${snapshot.data}',
style: textStyle,
),
Text(
'Error: ${snapshot.error}',
style: textStyle,
),
],
);
},
),
),
);
}
Future<int> getNumber() async {
await Future.delayed(Duration(seconds: 3));
final random = Random();
return random.nextInt(100);
}
}
1) ConState: ConnectionState.none?
FutureBuilder(future: )가 없으면 none 으로 출력된다.
FutureBuilder(
future: getNumber(),
)
connectionState 가 박뀔때마다 builder 함수가 새로 불린다는것이다. 그렇기 떄문에 우리가 setState()함수를 사용해서 build 함수를 실행 하지 않고도! 화면의 변화를 FutureBuilder 가 자동으로 캐치해준다.
2) 여기서 setState 함수 버튼을 만든다면?
ElevatedButton(
onPressed: (){
setState(){}
},
child: Text('setState'),
)
저 버튼을 누르면 SetState 함수가 실행되면서 빨간색 영역인 최상단의 build 함수가 재실행된다. 그렇다면 최상단의 build 함수가 호출되고, 그 안의 FutureBuilder 의 build 함수가 다시 실행이 될것이다.
그렇다면 setState 함수를 누르면 그전에 있던 데이터가 null 로 되고, 그 상태에서 data 를 가져올까?
아니다! FutureBuilder 는 이전 데이터를 기억하는 캐싱데이터를 가져온다. FutureBuilder 가 실행이 되고, 기존값을 기억한다음 바뀐값으로 저장을 한다.
저번에 FutureBUilder 를 사용하면서, 유저에게 별로 좋지 않은 부분으로 코딩한 부분이있다.
바로 waiting 이라고 한다면? 로딩바를 보여주기!
if(snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
결과)
이렇게 진행을 한다면 유저는 이 앱이 굉장히 느리다고 생각을 할 것이다.
하지만, 이렇게 로딩바를 보여줘야할때가 있다!
그럴때는 밑의 if 문을 사용하면된다. 그러면 처음 build 함수를 실행할때만 로딩인디케이터가 돌아가고, setState 가 실행되었을때는 로딩인디케이터가 돌아가지 않는것을 볼 수 있다.
if(!snapshot.hasData){
return Center(
child: CircularProgressIndicator();
)
}
에러를 출력해보자!
Future<int> getNumber() async {
...
throw Exception('에러가 발생했습니다.');
...
}
connectionState.done
실제로 데이터를 잘받으면 !
Data : 랜덤숫자
Error: null
에러가 난다면!
Data: null
Error : 에러 문구
이것을 활용해서 작성가능하다.
if(snapshot.hasData){
// 데이터가 있을때 위젯 렝더링
}
if(snapshot.hasError){
// 에러가 났을때 위젯 렌더링
}
// 로딩 중일때 위젯 렌더링
StreamBuilder 에 대해 배우자!
class _HomeScreenState extends State<HomeScreen> {
...
child: StreamBuilder(
stream: streamNumbers(),
),
),
);
}
Stream<int> streamNumbers() async* {
for (int i = 0; i < 10; i ++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
}
watting : Stream 을 기다리는 상태
active : Stream에서 계속 값을 받고 있을때!(Stream 이 완전히 끝나기 전 상태)
done : Stream이 완전히 끝난 상태
StreamBuilder 또한 캐싱된 데이터를 가져온다. setState 한다고 해서 data 값이 null 로 가지 않는다.
그리고 함수를 dispose() 해줘야하는데, Future, Stream은 자동으로 닫아주는 장점이있다.
결과)
참고!
FutureBuilder 와 Streambuilder 는 제너릭을 넣어줄수 있다. 그 타입은 snapshot 에 들어가는 데이터 타입을 넣어주면된다.
child: StreamBuilder<int>(
stream: streamNumbers(),
builder: (context, AsyncSnapshot<int> snapshot) {
...
}
)
child: FutureBUilder<String>(
future: getNumber(),
builder: (context, AsyncSnapshot<String> snapshot) {
...
}
)
Future<int> getNumber() async {
...
}
Stream<int> streamNumbers() async* {
...
}
에러를 던져보자!
5일때 에러 출력
Stream<int> streamNumbers() async* {
for (int i = 0; i < 10; i ++) {
if(i ==5) {
throw Exception('i = 5');
}
await Future.delayed(Duration(seconds: 1));
yield i;
}
정리
FutureBuilder
예 ) 갤러리에서 이미지나 비디오를 가져올때, 일회성 api 통신시 사용
StreamBuilder
예 ) 음악 재생, 타이머 재생, 지도 위치 업데이트 데이터를 여러번을 걸쳐 통신시 사용
둘다 공통적으로 error, data 를 받는다.
builder 의 snapshot 통해 현재 builder 의 상태를 알아 조건에 맞게 분기처리 가능하다.
출처
FutureBuilder class - widgets library - Dart API
Widget that builds itself based on the latest snapshot of interaction with a Future. The future must have been obtained earlier, e.g. during State.initState, State.didUpdateWidget, or State.didChangeDependencies. It must not be created during the State.bui
api.flutter.dev
StreamBuilder class - widgets library - Dart API
Widget that builds itself based on the latest snapshot of interaction with a Stream. Widget rebuilding is scheduled by each interaction, using State.setState, but is otherwise decoupled from the timing of the stream. The builder is called at the discretion
api.flutter.dev
끝으로...
- FutureBuilder 와 StreamBuilder 를 알았다.
