在此 ModalBottomSheet 小部件上方找不到正确的 Provider<X>

Could not find the correct Provider<X> above this ModalBottomSheet Widget

我是 Flutter 的新手,我正在尝试了解如何在用户 Moor 将一些数据保存到 sqlite table 的应用程序中使用提供者状态管理。我的应用程序是一个任务记录应用程序。当我打开底部 sheet 添加任务时,我的小部件树中出现上述错误。我正在使用 provider: ^4.3.1

class TaskView extends StatelessWidget {
  DateTime selectedDate = DateTime.now();

  @override
  Widget build(BuildContext context) {
    return Provider<TaskViewModel>(
        create: (_) => TaskViewModel(),
        child: Scaffold(
            appBar: AppBar(
              title: Text('Tasks'),
            ),
            body: Text("Temporary body!"),
            floatingActionButton: FloatingActionButton(
              onPressed: () {
                showMaterialModalBottomSheet(
                  context: context,
                  builder: (context, scrollController) => Container(
                    child: bottomSheet(context),
                  ),
                );
              },
              child: Icon(
                Icons.add,
                color: Colors.white,
              ),
              backgroundColor: Colors.blueAccent,
            )
        )
    );
  }

  Widget bottomSheet(BuildContext context) {
    return Padding(
      padding: EdgeInsets.only(
          left: 16.0,
          top: 16.0,
          right: 16.0,
          bottom: 16.0 + MediaQuery.of(context).viewInsets.bottom),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          TextField(
            decoration: InputDecoration(
              border: OutlineInputBorder(),
              labelText: 'Task',
            ),
          ),
          SizedBox(height: 8),
          TextField(
            readOnly: true,
            decoration: InputDecoration(
              border: OutlineInputBorder(),
              suffixIcon: IconButton(
                icon: Icon(Icons.date_range, color: Colors.grey),
                onPressed: () => _selectDate(context),
              ),
              labelText: 'Date',
            ),
          ),
          SizedBox(height: 8),
          Align(
              alignment: Alignment.topRight,
              child: context.watch<TaskViewModel>().state == ViewState.IDLE
                  ? FlatButton(
                      child: Text("Save"),
                      color: Colors.blueAccent,
                      textColor: Colors.white,
                      onPressed: () => _onClickInsertTask(context))
                  : _loadingButtonChild(context))
        ],
      ),
    );
  }

  Widget _loadingButtonChild(BuildContext context) {
    return Container(
      height: 20,
      width: 20,
      margin: EdgeInsets.all(5),
      child: CircularProgressIndicator(
          strokeWidth: 2,
          valueColor: AlwaysStoppedAnimation<Color>(Colors.white)),
    );
  }

  /// This function is responsible for displaying the date picker when user click
  /// on task due date inputFiled
  Future<Null> _selectDate(BuildContext context) async {
    final DateTime picked = await showDatePicker(
        context: context,
        initialDate: selectedDate,
        firstDate: DateTime(2015, 8),
        lastDate: DateTime(2101));

    if (picked != null && picked != selectedDate) {
      print("Date selected ${selectedDate.toString()}");
    }
  }

  /// This function is responsible for triggering insert task block event
  void _onClickInsertTask(BuildContext context) {

    var insertTask = TaskData(task: "task", dueDate: selectedDate);
    context.read<TaskViewModel>().insertTask(insertTask);
  }
}

错误建议检查

- 您尝试阅读的提供商在不同的路线上。

我没有给provider to s route,而是作为直接父视图。

- 您使用的 BuildContext 是您尝试读取的提供商的祖先。

我不明白这是什么意思,但我按照错误中的建议进行了修复。如下图

@override
  Widget build(BuildContext context) {
    return Provider<TaskViewModel>(
        create: (_) => TaskViewModel(),
        builder: (context, child) => Scaffold(
            appBar: AppBar(
              title: Text('Tasks'),
            ),
            body: Text("Temporary body!"),
            floatingActionButton: FloatingActionButton(
              onPressed: () {
                showMaterialModalBottomSheet(
                  context: context,
                  builder: (context, scrollController) => Container(
                    child: bottomSheet(context),
                  ),
                );
              },
              child: Icon(
                Icons.add,
                color: Colors.white,
              ),
              backgroundColor: Colors.blueAccent,
            )));
  }

仍然得到同样的错误。这里要注意的另一件事是错误提示如下。

Widget build(BuildContext context) {
    return Provider<Example>(
      create: (_) => Example(),
      // we use `builder` to obtain a new `BuildContext` that has access to the provider
      builder: (context) {
        // No longer throws
        return Text(context.watch<Example>()),
      }
    ),
  }

但是我找不到 builder: (context) 所以我使用了 builder: (context, child)。请让我知道我应该改变什么才能让它工作。谢谢。

编辑:

基础模型

class BaseViewModel extends ChangeNotifier {

  ViewState _state = ViewState.IDLE;

  ViewState get state => _state;

  void setState(ViewState viewState) {
    _state = viewState;
    notifyListeners();
  }
}

TaskViewModel

class TaskViewModel extends BaseViewModel{

  final TaskRepository _repository = TaskRepository();

  Resource<int> insertTaskStatus;

  Future<void> insertTask(TaskData task) async {

    setState(ViewState.PROCESSING);
    var tasksCompanion = TasksCompanion(task: Value(task.task),dueDate: Value(task.dueDate));
    insertTaskStatus  = await  _repository.insertTask(tasksCompanion);
    setState(ViewState.COMPLETED);

  }

}

虽然你在 provider 包裹的 Scaffold 中调用 showMaterialModalBottomSheet,但 provider 并不在 TaskView 的 [=11= 之上] 和 modalBottomSheet。为什么?

The provider you are trying to read is in a different route.

因此,modalBottomSheet 似乎在另一个 route 上,上面没有 provider。如果你看一下 showModalBottomSheet 的实现,你会看到:

return Navigator.of(context, rootNavigator: useRootNavigator).push(_ModalBottomSheetRoute<T>(....);

很明显,这是一个新的 route。因此,要访问 provider,它应该位于 routes 之上。由于 modalBottomSheet routeMaterialApp 管理,因此您必须将 provider 放在 MaterialApp 之上。

Provider 默认使用 lazy loading。因此,对象是在需要时创建的,而不是在应用程序启动时创建的。但是,如果您不希望出现这种情况,您可以单独设置 lazy: false。有关详细信息,请查看 offical docs.