Dismissible和FutureBuilder无法一起使用

这是一个复杂的问题,但我会尽力解释。

我的项目使用了sqflite数据库。该特定页面根据数据库中的数据返回Dismissible小部件列表。这就是我读取数据的方式:

class TasklistState extends State<Tasklist> {
  DBProvider dbProvider = new DBProvider();
  Future<List<Task>> allTasks;

  @override
  void initState() {
    allTasks = dbProvider.getallTasks();
    super.initState();
  }

  void update(){
    setState(() {
      allTasks = dbProvider.getallTasks();
    });
  }
  //build
}

Tasklist小部件将返回一个带有FutureBuilder的页面,该页面将使用数据库中的数据构建一个ListView.builder。 ListView生成Dismissible小部件。关闭Dismissible小部件将更新数据库中的一行,并再次读取数据以更新列表。

TasklistState的构建方法

  @override
  Widget build(BuildContext context) {
    return ListView(
      physics: const AlwaysScrollableScrollPhysics(),children: <Widget>[
        //other widgets such as a title for the list
        ),FutureBuilder(
          future: allTasks,builder: (context,snapshot){
            if(snapshot.hasError){
              return Text("Data has error.");
            } else if (!snapshot.hasData){
              return Center(
                child: CircularProgressIndicator(),);
            } else {
              return pendingList(Task.filterByDone(false,Task.filterByDate(Datetime.now,snapshot.data))); //filters the data to match current day
            }
          },),//other widgets
      ],);
  }

待处理列表

Widget pendingList(List<Task> tasks){
    //some code to return a Text widget if "tasks" is empty
    return ListView.separated(
      separatorBuilder: (context,index){
        return Divider(height: 2.0);
      },itemCount: tasks.length,physics: const NeverScrollableScrollPhysics(),shrinkWrap: true,itemBuilder: (context,index){
        return Dismissible(
          //dismissible backgrounds,other non-required parameters
          key: Key(UniqueKey().toString()),onDismissed: (direction) async {
            Task toRemove = tasks[index]; //save the dismissed task for the upcoming operations
            int removeIndex = tasks.indexWhere((task) => task.id == toRemove.id);
            tasks.removeAt(removeIndex); //remove the dismissed task
            if(direction == DismissDirection.endToStart) {
              ratetask(toRemove).then((value) => update()); //ratetask is a function that updates the task,it is removed from the list
            }
            if(direction == DismissDirection.startToEnd) {
              dbProvider.update(/*code to update selected task*/).then((value) => update());
            }

          },child: ListTile(
            //ListTile details
          ),);
      },);
  }

这是问题所在(可能是一种错误的解释,我还是很新的):

关闭窗口小部件实际上会将其从列表中删除。用户解雇任务后,将从列表中“视觉上”删除该任务,并调用update()方法,该方法调用setState()。调用setState()会导致FutureBuilder再次生成,但是dbProvider.getallTasks()调用在FutureBuilder再次生成时尚未完成。因此,FutureBuilder传递旧快照,这将导致ListView使用刚刚被关闭的Task重新构建。这会导致被解雇的ListTile在被解雇后立即出现,这看起来令人毛骨悚然和错误。

我不知道该如何解决。任何帮助将不胜感激。

sothis2000 回答:Dismissible和FutureBuilder无法一起使用

我遇到了完全相同的问题,我正在使用可与Futures一起使用的sqflite,所以我最终将FutureBuilderDismissible一起用于ListView。被驳回的清单项目将删除,然后重新出现一帧,然后再次消失。我碰到了这个问题:

https://groups.google.com/forum/#!topic/flutter-dev/pC48MMVKJGc

建议从快照数据本身中删除列表项:

return FutureBuilder<List<FolderModel>>(
      future: Provider.of<FoldersProvider>(context).getFolders(),builder: (context,snapshot) {
        if (!snapshot.hasData) {
          return Center(
            child: CircularProgressIndicator(),);
        }
        return ListView.builder(
          itemCount: snapshot.data.length,itemBuilder: (context,i) {
            final folder = snapshot.data[i];
            return Dismissible(
              onDismissed: (direction) {
                snapshot.data.removeAt(i); // to avoid weird future builder issue with dismissible
                Provider.of<FoldersProvider>(context,listen: false).deleteFolder(folder.id);
              },background: Card(
                margin: EdgeInsets.symmetric(vertical: 8),elevation: 1,child: Container(
                  alignment: AlignmentDirectional.centerStart,color: Theme.of(context).accentColor,child: Padding(
                    padding: EdgeInsets.fromLTRB(15.0,0.0,0.0),child: Icon(
                      Icons.delete,color: Colors.white,),key: UniqueKey(),direction: DismissDirection.startToEnd,child: Card(
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(15),margin: EdgeInsets.symmetric(vertical: 5,horizontal: 10),child: ListTile(
                  title: Text(folder.folderName),leading: Column(
                    mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[
                      Icon(
                        Icons.folder,],subtitle: folder.numberOfLists != 1
                      ? Text('${folder.numberOfLists} items')
                      : Text('${folder.numberOfLists} item'),onTap: () {},);
          },);
      },);

又低又看,它奏效了!对代码的更改最少:)

,

通过不使用FutureBuilder并在查询完成后调用setState,找到了解决方法。

状态现在包含OR Meetings.ChairpersonID = Users.UserID,而不是Future<List<Task>>,它被声明为空。

List<Task>

class TaskListState extends State<TaskList> { DBProvider dbProvider = new DBProvider(); DateTime now = DateTime.now(); List<Task> todayTasks; //build } 函数的更改如下

update()

这样,我的小部件不会在将来完成之前重建,这就是我遇到的问题。

我完全删除了FutureBuilder,Listview.builder只是根据状态存储的List进行构建。

void update() async {
    Future<List<Task>> futureTasks = dbProvider.getByDate(now); //first make the query
    futureTasks.then((data){
      List<Task> tasks = new List<Task>();
      for(int i = 0; i < data.length; i++) {
        print(data[i].name);
        tasks.add(data[i]);
      }
      setState(() {
        todayTasks = tasks; //then setState and rebuild the widget
      });
    });
  }

这种方法完全解决了我的问题,我认为它比使用FutureBuilder更好,以防将来必须再次完成小部件构建之前完成

本文链接:https://www.f2er.com/3129449.html

大家都在问