일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- appbar
- Coroutines
- Navigation
- Button
- intent
- TEST
- Kotlin
- binding
- android
- 테스트
- activity
- ScrollView
- textview
- livedata
- Flutter
- Compose
- viewmodel
- 앱바
- LifeCycle
- Dialog
- drift
- 계측
- scroll
- 앱
- data
- DART
- tabbar
- 안드로이드
- textfield
- CustomScrollView
- Today
- Total
Study Record
[Flutter] FutureBuilder 본문
✍ FutureBuilder Widget
네트워크 통신이 필요한 작업 등 비동기로 작업한 이후 UI를 업데이트를 해야 할 경우가 있다면 유용한 위젯이다.
FutureBuilder<T>({
Key? key,
Future? future,
T? initialData,
required Widget Function(BuildContext, AsyncSnapshot<T>) builder,
})
future에 비동기로 할 작업을 정의하고 builder에 future에 작업이 끝나기 전 보여줄 Widet , 끝난 후 보여줄 Widget을 정의하면 된다. 데이터 타입 T는 비동기 작업 후 반환할 데이터 타입으로 initialData는 future 작업이 끝나기 전 초기 데이터를 정의할 수 있다.
예를 들어, 코드가 다음과 같으면,
Future<String> _calculation = Future<String>.delayed(
const Duration(seconds: 4),
() => 'Data Loaded',
);
FutureBuilder<String>(
future: _calculation, // a previously-obtained Future<String> or null
initialData: "error",
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
print("snapshot state : ${snapshot.connectionState.name}");
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
print("connectionState watting ${snapshot.data} , ${snapshot.hasData}");
return const _LoadingWidget();
case ConnectionState.active:
if (snapshot.hasData) {
return _SuccessWidget(successData: snapshot.data!);
} else {
return const _ErrorWidget(errorData: "active data error"); }
case ConnectionState.done:
if (snapshot.hasError || snapshot.hasData == false) {
return const _ErrorWidget(errorData: "done error");
} else {
return _SuccessWidget(successData: snapshot.data!);
}
}
},
),
snapshot 은 future 의 작업 상태(snapshot.connectionState)와 future의 반환값(snapshot.data) 정보를 알 수 있는데, connectionState는 4가지 상태를 가질 수 있다.
- ConnectionState.none : initalData만 있는 상태
- ConnectionState.waiting : future(비동기 작업)이 시작 단계에 있는 상태로 snapshot.data은 initialData인 상태이다.
- ConnectionState.active : 비동기 작업의 반환값(snapshot.data)이 null 이 아니지만 언제든 바뀔 수 있는 상태이다. StreamBuilder 에서만 사용된다.
- ConnectionState.done : 비동기 작업이 완료된 시점이며 반환값(snapshot.data)이 null 이 아니다.
또한, 비동기 작업 중 오류가 있다면 snapshot.hasError 값이 true 이며 초기 데이터(initialData)가 null 일 경우 비동기 작업의 반환값이 아직 없으면 snapshot.hasData 는 false을 리턴한다.
예시) 로딩 위젯에서 4초 뒤 성공 위젯으로 변함
import 'package:flutter/material.dart';
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({super.key});
@override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
final Future<String> _calculation = Future<String>.delayed(
const Duration(seconds: 4),
() => 'Data Loaded',
);
@override
Widget build(BuildContext context) {
print("build start");
return DefaultTextStyle(
style: Theme.of(context).textTheme.displayMedium!,
textAlign: TextAlign.center,
child: FutureBuilder<String>(
future: _calculation, // a previously-obtained Future<String> or null
initialData: "error",
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
print("snapshot state : ${snapshot.connectionState.name}");
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
print("connectionState watting ${snapshot.data} , ${snapshot.hasData}");
return const _LoadingWidget();
case ConnectionState.active:
if (snapshot.hasData) {
return _SuccessWidget(successData: snapshot.data!);
} else {
return const _ErrorWidget(errorData: "active data error");
}
case ConnectionState.done:
{
if (snapshot.hasError || snapshot.hasData == false) {
return const _ErrorWidget(errorData: "done error");
} else {
return _SuccessWidget(
successData: snapshot.data!,
);
}
}
}
},
),
);
}
}
class _SuccessWidget extends StatelessWidget {
final String successData;
const _SuccessWidget({required this.successData, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Icon(
Icons.check_circle_outline,
color: Colors.green,
size: 60,
),
Padding(
padding: const EdgeInsets.only(top: 16),
child: Text(
'Result: $successData',
style: TextStyle(color: Colors.white),
),
),
],
),
);
}
}
class _ErrorWidget extends StatelessWidget {
final String errorData;
const _ErrorWidget({required this.errorData, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Icon(
Icons.error_outline,
color: Colors.red,
size: 60,
),
Padding(
padding: const EdgeInsets.only(top: 16),
child: Text(
'Error: $errorData',
style: TextStyle(color: Colors.white),
),
),
],
),
);
}
}
class _LoadingWidget extends StatelessWidget {
const _LoadingWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
SizedBox(
width: 60,
height: 60,
child: CircularProgressIndicator(),
),
Padding(
padding: EdgeInsets.only(top: 16),
child: Text(
'Awaiting result...',
style: TextStyle(color: Colors.white),
),
),
],
),
);
}
}
+ setState() 와 FutureBuilder
StatefulWiget 에서 setState() 를 하면 build 함수가 다시 실행되면서 FutureBuilder 도 다시 만들어진다. 이 때, future 을 함수로 설정하면 비동기 작업도 다시 실행된다.
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(home: HomeScreen()),
);
}
class HomeScreen extends StatelessWidget {
HomeScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: _MyStatefulWidget(),
);
}
}
class _MyStatefulWidget extends StatefulWidget {
_MyStatefulWidget({Key? key}) : super(key: key);
@override
State<_MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<_MyStatefulWidget> {
TextStyle textStyle = TextStyle(
color: Colors.black,
fontSize: 15.0
);
Future<int> _calculation() async {
print("_calculation 실행중");
Future.delayed(Duration(seconds: 3));
return Random().nextInt(100);
}
@override
Widget build(BuildContext context) {
return DefaultTextStyle(
style: Theme.of(context).textTheme.displayMedium!,
textAlign: TextAlign.center,
child: FutureBuilder<int>(
future: _calculation(),
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text("snapshot state : ${snapshot.connectionState}", style: textStyle,),
Text("data : ${snapshot.data}", style: textStyle,),
Text("error : ${snapshot.error}", style: textStyle,),
ElevatedButton(
onPressed: () {
setState(() {});
},
child: Text("setsTate()"),
)
],
);
},
),
);
}
}
future 에 함수대신 변수로 넣으면 setState() 를 해도 비동기 작업을 반복하지 않는다...
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
class HomeScreen extends StatelessWidget {
HomeScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: _MyStatefulWidget(),
);
}
}
class _MyStatefulWidget extends StatefulWidget {
_MyStatefulWidget({Key? key}) : super(key: key);
@override
State<_MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<_MyStatefulWidget> {
TextStyle textStyle = TextStyle(
color: Colors.black,
fontSize: 15.0
);
Future<int> _calculateValue = Future.delayed(Duration(seconds: 2), (){
return Random().nextInt(1000);
});
@override
Widget build(BuildContext context) {
return DefaultTextStyle(
style: Theme.of(context).textTheme.displayMedium!,
textAlign: TextAlign.center,
child: FutureBuilder<int>(
future: _calculateValue,
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
print("snapshot ${snapshot.connectionState}");
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text("snapshot state : ${snapshot.connectionState}", style: textStyle,),
Text("data : ${snapshot.data}", style: textStyle,),
Text("error : ${snapshot.error}", style: textStyle,),
ElevatedButton(
onPressed: () {
setState(() {});
},
child: Text("setsTate()"),
)
],
);
},
),
);
}
}
'Flutter > widget' 카테고리의 다른 글
[Flutter] 가장 큰 위젯과 크기 동일하게 맞추기(IntrinsicHeight, IntrinsicWidth) (0) | 2023.02.22 |
---|---|
[Flutter] StreamBuilder (0) | 2023.02.19 |
[Flutter] AlterDialog (+ showDialog) (0) | 2023.02.18 |
[Flutter] 위젯 겹치기 (Stack, Positioned) , 배경 불투명 (0) | 2023.02.18 |
[Flutter] 영상(비디오) 재생하기(video_player, image_picker) (0) | 2023.02.17 |