【问题标题】:Could not find the correct Provider<X> above this ModalBottomSheet Widget在此 ModalBottomSheet 小部件上方找不到正确的 Provider<X>
【发布时间】:2020-07-26 06:45:28
【问题描述】:

我是 Flutter 的新手,我正在尝试了解如何在用户 Moor 将一些数据保存到 sqlite 表中的应用程序中使用提供者状态管理。我的应用程序是一个任务记录应用程序。当我打开底部工作表添加任务时,我的小部件树中出现上述错误。我正在使用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);
  }
}

错误建议检查。

- 您尝试读取的提供程序位于不同的路径中。

我没有将提供者提供给 s 路由,而是作为直接父视图。

- 您使用了 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);

  }

}

【问题讨论】:

  • 将您的提供者置于 MaterialApp 之上
  • 你能解释一下为什么需要这种方法来实现吗?如果我这样做,那不是所有屏幕都通用吗?这种方式不是所有的 Viemodel 对象都会在应用程序启动时创建。不过,我的提供程序位于小部件树的顶部,因此它应该可以正常工作。但在这种情况下不是。

标签: flutter dart flutter-provider


【解决方案1】:

虽然您在由provider 包裹的Scaffold 中调用showMaterialModalBottomSheet,但provider 并不高于TaskViewScaffoldmodalBottomSheet。为什么?

您尝试读取的提供程序位于不同的路径中。

因此,modalBottomSheet 似乎位于另一个 route 上,上面没有 provider。如果您查看showModalBottomSheet 的实现,您会看到:

return Navigator.of(context, rootNavigator: useRootNavigator).push(_ModalBottomSheetRoute&lt;T&gt;(....);

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

Provider 默认使用lazy loading。因此,对象是在需要时创建的,而不是在应用程序启动时创建的。但是,如果您不希望这种行为,您可以单独设置lazy: false。更多信息请查看offical docs

【讨论】:

  • 哦,这解释了错误。我假设从这个答案中你对颤振有更多的经验。请解释一下。那么,一般来说,在现实世界的应用开发中,是不是所有的provider都提供给material app呢?就像在这里使用MultiProvider pub.dev/packages/provider/example。我的问题是它不是在应用启动时创建所有提供程序(ViewModel)对象并产生性能问题吗?
  • 没有。 provider 的一个特点是它是延迟加载的,即它会在需要时创建一个对象,而不是在应用程序启动时。如果您转到您提供的链接中的自述文件部分,您会发现指定了“延迟加载”。 @ChathurangaShanJayarathna
  • 所以这是正确的方法。是的,看过那部分,但没有在我的脑海中点击它。谢谢您的帮助。我必须等待 16 小时才能提供赏金。我会给的。
猜你喜欢
  • 2021-07-16
  • 1970-01-01
  • 2021-08-24
  • 2021-10-27
  • 2021-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-04
相关资源
最近更新 更多