DevBoi

[Flutter] FutureBuilder setState 동작 이슈 본문

[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에 대한 조건을 분기 처리하여, 로딩중일때와 에러일때를 구분하여 핸들링 할 수 있다.

반응형

'[Mobile] > [Flutter]' 카테고리의 다른 글

[Flutter] Boiler Template 적용시  (0) 2023.09.09
[Flutter] Getx로 전역 Provider 설정  (0) 2023.09.07
[Flutter] Dialog 콜백 값 핸들링  (0) 2023.09.02
[Flutter] initState 이전 데이터 세팅  (0) 2023.09.02
[Flutter] Chip 사용  (0) 2023.08.28