[Mobile]/[Flutter]
[Flutter] FutureBuilder setState 동작 이슈
HiSmith
2023. 9. 3. 19:41
반응형
리스트뷰는 문제가 없는데, Future로 관리를 하고싶은, 함수에 대해서는 동작을 하지 않는 이슈가 있다.
예를 들면 데이터가 업데이트되어 다시 위젯으로 돌아왔을때 해당 아이템에 대한 갱신이 되지않았다.
이문제로 꽤 긴시간을 보내면서 정리를 하게 된다.
child: FutureBuilder(
future: groupList,
builder: (context,AsyncSnapshot snapshot){
if (snapshot.hasData && snapshot.connectionState == ConnectionState.done) {
print(snapshot.toString());
return ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 20),
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
MyGroupItem item = snapshot.data[index];
return MyGroupWidget(myGroupItem: item, refreshListFunc: _fetchData);
},);
}
else
{
return Container(
child: Center(
child: Text("Loading..."),
),
);
}
}
),
일단, 추가해준 부분은 snapshot의 커넥션 상태를 추가로 확인하는 것이다.
ConnectionState.done일때만 해당 데이터를 가져와서 뿌려주고, 아닐때는 막아준다.
class MyGroupWidget extends StatefulWidget {
MyGroupItem myGroupItem;
final Function() refreshListFunc;
MyGroupWidget({Key? key,required this.myGroupItem,required this.refreshListFunc});
@override
_MyGroupWidgetState createState() => _MyGroupWidgetState(myGroupItem: myGroupItem);
}
위에서 해당 리프레쉬 하는 함수에 대한 펑션자체를 인자로 넘겨서
다시 부모 위젯에 이벤트를 발생시킨다.
_fetchData() async {
setState((){
groupList = getMyGroupListApi(userId);
});
}
위의 비동기 이벤트 메소드는 아래와 같다.
전체 소스는 아래와 같다
import 'package:async/async.dart';
import 'package:goodshot/view/group/dto/MyGroupItem.dart';
import 'package:goodshot/view/group/http/group-httpapi.dart';
import 'package:flutter/material.dart';
import 'package:goodshot/view/group/http/my-group-httpapi.dart';
import 'package:goodshot/view/group/item/get-group-item.dart';
import 'package:goodshot/view/group/item/my-group-widget.dart';
import 'package:goodshot/view/group/screen/group-list.dart';
import 'package:goodshot/view/group/widget/create-group-popup.dart';
class MyGroupList extends StatefulWidget {
final String userId;
MyGroupList({required this.userId});
@override
_MyGroupListState createState() => _MyGroupListState(userId: userId, groupList: getMyGroupListApi(userId));
}
class _MyGroupListState extends State<MyGroupList> {
final AsyncMemoizer _memoizer = AsyncMemoizer();
String userId;
Future<List<MyGroupItem>> groupList;
_MyGroupListState({required this.userId,required this.groupList});
@override
void initState() {
super.initState();
groupList = getMyGroupListApi(userId);
}
@override
Widget build(BuildContext context) {
_fetchData() async {
setState((){
groupList = getMyGroupListApi(userId);
});
}
return Scaffold(
appBar: AppBar(
centerTitle: true,
actions:[
//드롭 다운 지역 검색 가능하도록?
]
),
body: Container(
child: FutureBuilder(
future: groupList,
builder: (context,AsyncSnapshot snapshot){
if (snapshot.hasData && snapshot.connectionState == ConnectionState.done) {
print(snapshot.toString());
return ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 20),
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
MyGroupItem item = snapshot.data[index];
return MyGroupWidget(myGroupItem: item, refreshListFunc: _fetchData);
},);
}
else
{
return Container(
child: Center(
child: Text("Loading..."),
),
);
}
}
),
),
floatingActionButton: FloatingActionButton(
child: Icon(
Icons.add,
color: Colors.white,
),
backgroundColor: Colors.green,
onPressed: () {
showDialog(
context: context,
barrierDismissible: true, // 바깥 영역 터치시 닫을지 여부
builder: (BuildContext context) {
return AlertDialog(
content: CreateGroupPopUp(userId: userId),
insetPadding: const EdgeInsets.fromLTRB(0,80,0, 80),
);
}
).then((value) => {
setState((){
groupList= getMyGroupListApi(userId);
})
});
},
),
);
}
}
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:goodshot/view/group/dto/MyGroupItem.dart';
import 'package:goodshot/view/group/http/group-join-httpapi.dart';
import 'package:goodshot/view/group/widget/chip-builder.dart';
import 'package:goodshot/view/group/widget/group-detail-popup.dart';
import 'package:goodshot/view/group/widget/my-group-popup.dart';
import 'package:intl/intl.dart';
class MyGroupWidget extends StatefulWidget {
MyGroupItem myGroupItem;
final Function() refreshListFunc;
MyGroupWidget({Key? key,required this.myGroupItem,required this.refreshListFunc});
@override
_MyGroupWidgetState createState() => _MyGroupWidgetState(myGroupItem: myGroupItem);
}
class _MyGroupWidgetState extends State<MyGroupWidget> {
MyGroupItem myGroupItem;
_MyGroupWidgetState({required this.myGroupItem});
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 1.1),
child: InkWell(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
ListTile(
leading: CircleAvatar(
radius: 25,
backgroundImage: NetworkImage("${myGroupItem.thumbnailImageUrl}"),
),
contentPadding: EdgeInsets.all(0),
title: Text("${myGroupItem.title}",),
subtitle: Text("${myGroupItem.nickname}",),
isThreeLine: false,
trailing:
Text("${myGroupItem.region}"),
),
Wrap(
alignment: WrapAlignment.start,
spacing: 1.0,
runSpacing: 1.0,
children: <Widget>[
Chipbuilder.build("${myGroupItem.gender}",Colors.lightGreen),
Chipbuilder.build("인원 ${myGroupItem.memberCnt}명", Colors.lightGreen),
Chipbuilder.build(
"${"${myGroupItem.date}".substring(5,7)}월"
"${"${myGroupItem.date}".substring(8,10)}일 "
"${"${myGroupItem.date}".substring(11,13)}시"
"${"${myGroupItem.date}".substring(14,16)}분",
Colors.lightGreen),
Chipbuilder.build("${myGroupItem.place}",Colors.lightGreen)
],
),
Divider(thickness: 1,),
],
),
onTap: () {
print(myGroupItem.groupNo);
showDialog(
barrierDismissible: false,
context: context, builder: (BuildContext context){
return MyGroupDetailPopup(myGroupItem: myGroupItem,sourceId: myGroupItem.sourceId);
}
).then((value) => setState((){
print('시작');
widget.refreshListFunc.call();
}));
},
),
);
}
}
class MyGroupItem {
var groupNo;
var title;
var content;
String date;
var memberCnt;
var gender;
List<String> region;
List<String> introduce_tag;
var place;
var userId;
var nickname;
var thumbnailImageUrl;
var sourceId;
MyGroupItem({
required this.groupNo,
required this.title,
required this.content,
required this.date,
required this.memberCnt,
required this.gender,
required this.region,
required this.introduce_tag,
required this.place,
required this.userId,
required this.nickname,
required this.thumbnailImageUrl,
required this.sourceId,
});
factory MyGroupItem.fromJson(dynamic json, String? userId){
MyGroupItem groupItem = MyGroupItem(
groupNo: json?['group_no'],
title: json?['title'],
content: json?['content'],
date: json?['date'],
memberCnt: json?['memberCnt'],
gender: json?['gender'],
region: List<String>.from(json?['region']),
introduce_tag: List<String>.from(json?['introduce_tag']),
place: json?['place'],
userId: json?['userId'],
thumbnailImageUrl: json?['thumbnailImageUrl'],
nickname: json?['nickname'],
sourceId: userId,
);
return groupItem;
}
factory MyGroupItem.fromMap(Map<dynamic, dynamic> map, String userId){
print('gd');
MyGroupItem groupItem = MyGroupItem(
groupNo: map['group_no'],
nickname: map['user']['nickname'],
thumbnailImageUrl: map['user']['thumbnailImageUrl'],
title: map['title'].toString(),
content: map['content'].toString(),
date: DateTime.tryParse((map['date'])).toString(),
memberCnt: map['memberCnt'],
gender: map['gender'],
region: List<String>.from(map['region']),
introduce_tag: List<String>.from(map['introduce_tag']),
place: map['place'],
userId: map['user']['userId'],
sourceId: userId,
);
return groupItem;
}
}
위와 같이하면, 네비게이션 위젯 레이어 스택에 2개이상이 쌓였다가 다시 돌아왔을때
부모 위젯의 이벤트를 발생시켜, 해당 리스트뷰에 대한 아이템 갱신을 할 수 있다는 이점이 있다.
또한 ConnectionState에 대한 조건을 분기 처리하여, 로딩중일때와 에러일때를 구분하여 핸들링 할 수 있다.
반응형