简体   繁体   English

如何将 Provider 用于异步数据? (扑)

[英]How can I use Provider for async data? (Flutter)

Background背景

I am building super simple TODO list app for practicing Flutter.我正在构建超级简单的 TODO 列表应用程序来练习 Flutter。 If you input a string and click "add", then the text is shown in the list on the upper part.如果您输入一个字符串并单击“添加”,则该文本将显示在上方的列表中。

Question问题

When and how should I call model.getTasks() (= update data asynchronously) and model.refresh() (= notifyListeners )?我应该何时以及如何调用model.getTasks() (= 异步更新数据)和model.refresh() (= notifyListeners )?

I find the list is not updated immediately because I don't use await for model.getTasks() .我发现该列表没有立即更新,因为我不使用await for model.getTasks() I tried these but I would like to know if there are better ways.我尝试了这些,但我想知道是否有更好的方法。

  • Use await model.getTasks() and make build an async function.使用await model.getTasks()build异步 function。 --> Compile error --> 编译错误
  • Add await model.getTasks() right before model.refresh() . await model.getTasks() model.refresh() --> model.tasks is not updated before rendering ListView when opening the app. --> model.tasks在打开应用程序时渲染ListView之前不会更新。 model.getTasks() are called twice when creating new tasks. model.getTasks()在创建新任务时被调用两次。

Screenshot截屏

应用 TODO 列表页面

Code details代码详情

  • model.tasks is List<Task> , which is to store tasks fetched from DB ( sqflite ). model.tasksList<Task> ,用于存储从 DB ( sqflite ) 获取的任务。
  • model.getTasks() is an async function to fetch data from DB and overwrite model.tasks . model.getTasks()是一个异步 function 从 DB 获取数据并覆盖model.tasks
  • model.refresh() is only for calling notifyListeners() . model.refresh()仅用于调用notifyListeners()
  • TodoListPage is the listener of the model's notifyListeners . TodoListPage是模型的notifyListeners的监听器。
class TodoListPage extends StatelessWidget {
  final TextEditingController _textFieldController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    print('TodoListPage.build');
    final model = Provider.of<TodoListModel>(context);
    model.getTasks();

    Future createTask() async {
      TaskData taskData = await DatabaseHandler()
          .insertTask(new TaskData(name: _textFieldController.text));
      model.refresh();
    }

    return Scaffold(
      appBar: AppBar(
        title: Text(
          'TODO List',
        ),
      ),
      body: Column(
        children: <Widget>[
          Container(
            height: 150,
            child: ListView.builder(
              itemBuilder: (BuildContext context, int index) {
                return Text(model.tasks[index].name);
              },
              itemCount: model.tasks.length,
            ),
          ),
          Text(
            'Add',
          ),
          Form(
            child: Column(
              children: <Widget>[
                TextFormField(
                  controller: _textFieldController,
                ),
                RaisedButton(
                  onPressed: () {
                    print('onPressed');
                    print(_textFieldController.text);
                    createTask();
                  },
                  child: Text('New'),
                ),
              ],
            ),
          )
        ],
      ),
    );
  }
}

You can use FutureBuilder inside a StatefulWidget :您可以在StatefulWidget中使用FutureBuilder

class TodoListPage extends StatefulWidget {
  @override 
  _TodoListPageState createState() => _TodoListPageState();
}

class _TodoListPageState extends State<TodoListPage> {
  TodoListModel model;
  Future<List<Tasks>> _getTasks;

  @override
  void didChangeDependencies() {
    model = Provider.of<TodoListModel>(context);
    _getTasks = model.getTasks();
  }

  @override
  Widget build() {
    FutureBuilder<List<Tasks>>(
      future: _getTasks,
      builder: (context, snapshot) {
        if (snapshot.connectionState != ConnectionState.done) {
          return Text("Loading tasks...");
        }

        final tasks = snapshot.data;

        // ...
      }
    );
  }
}

And in your model:在您的 model 中:

Future<List<Tasks>> getTasks() async {
  if (tasks == null) {
    tasks = await loadFromDatabase();
  }

  return tasks;
}

when you set the ChangeNotifierProvider , it calls in the TodoListModel() only one time refreshTasks() that's similar to your model.getTastks() in the Widget build()当您设置ChangeNotifierProvider时,它只会在TodoListModel()中调用一次refreshTasks() ,这与 Widget build() 中的 model.getTastks() 类似

then i place a Consumer as deep in the Widget tree as possible and this Consumer gets everytime newbuild when i Press the RaiseButton or on notifyListeners();然后我将一个消费者尽可能深地放置在小部件树中,当我按下 RaiseButton 或notifyListeners();

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class TodoList extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
        providers: [
          ChangeNotifierProvider(create: (context) => TodoListModel()),
      ],
    child: TodoListPage()
    );
  }
}


class TodoListPage extends StatelessWidget {
  final TextEditingController _textFieldController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    print('TodoListPage.build');

    // listen: false, if the provider sends e notify, this part is not refreshing
    final model = Provider.of<TodoListModel>(context, listen: false);

    // i place this future in the TodoListModel Class
    /*Future createTask() async {
      //TaskData taskData = await DatabaseHandler().insertTask(new TaskData(name: _textFieldController.text));

      model.refreshTasks();
    }*/

    return Scaffold(
      appBar: AppBar(
        title: Text(
          'TODO List',
        ),
      ),
      body: Column(
        children: <Widget>[
          Container(
            height: 150,
            child: new Consumer<TodoListModel>(
                builder: (context, prov2, child) {

                  // this part is updating every notifyListeners
                  //      it's better when you place the consumer,as deep as possible in the widget tree
                  print("Consumer build");

                  return ListView.builder(
                    itemBuilder: (BuildContext context, int index) {
                      return Text(model.tasks[index].name);
                    },
                    itemCount: model.tasks.length,
                  );
                },
            ),
          ),
          Text(
            'Add',
          ),
          Form(
            child: Column(
              children: <Widget>[
                TextFormField(
                  controller: _textFieldController,
                ),
                RaisedButton(
                  onPressed: () {
                    print('onPressed');
                    //print(_textFieldController.text);
                    //createTask();
                    model.createTask(new TaskData(name: _textFieldController.text));
                  },
                  child: Text('New'),
                ),
              ],
            ),
          )
        ],
      ),
    );
  }
}

class TaskData {
  final String name;

  TaskData({this.name});
}

class TodoListModel extends ChangeNotifier {

  // this Function only calls when the Provider is set at ChangeNotifierProvider like initTodoListModel
  TodoListModel () {
    print("TodoListModel Class");

    refreshTasks();
  }

  // define TaskData, with getter
  List<TaskData> _tasks = [new TaskData(name: "test"), new TaskData(name: "test2")];
  List<TaskData> get tasks => _tasks;

  // getTasks async
  Future refreshTasks() async {

    // fetch something in the Database
    // _tasks = await await DatabaseHandler().getTasks();

    notifyListeners();

  }

  Future createTask(TaskData newTask) async {

    // add Task with DatabseHandler, or in this example a simple list.add
    _tasks.add(newTask);

    //refreshing with notifyListeners
    refreshTasks();
  }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM