ProviderNotFoundException 在构建 ConsumptionDialog 时被抛出,因为使用了不包含提供者的`BuildContext`

Posted

技术标签:

【中文标题】ProviderNotFoundException 在构建 ConsumptionDialog 时被抛出,因为使用了不包含提供者的`BuildContext`【英文标题】:ProviderNotFoundException was thrown building ConsumptionDialog because used `BuildContext` that does not include the provider 【发布时间】:2021-12-15 06:13:23 【问题描述】:

我是一名学生,我不在学校学习移动开发。如果有人能帮助我,我真的很感激。我不知道如何修复错误,我尝试了无数次。

错误:在此构建器小部件上方找不到正确的提供程序。发生这种情况是因为您使用的“BuildContext”不包括您选择的提供者。

相关的导致错误的小部件是 消费对话。此错误涉及 2 个文件。

第一个文件:consumption_dialog.dart

class ConsumptionDialog extends StatefulWidget 
  @override
  _ConsumptionDialogState createState() => _ConsumptionDialogState();


class _ConsumptionDialogState extends State<ConsumptionDialog> 
  final _form = GlobalKey<FormState>();
  String? _text;

  String? _validateText(String? value) 
    if (value == null) 
      return "2000 ml minimun";
    

    final number = int.tryParse(value);
    if (number != null && number >= 2000) 
      return null;
    

    return "2000 ml minimum";
  

  @override
  Widget build(BuildContext context) 
    final bloc = context.watch<WaterBloc>();
    return AlertDialog(
      title: Text(
        "Daily consumption",
        textAlign: TextAlign.center,
        style: TextStyle(fontWeight: FontWeight.bold),
      ),
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
      content: Form(
        key: _form,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          mainAxisSize: MainAxisSize.min,
          children: [
            Text(
              "Change your daily water consumption goal, in milliliters.",
              textAlign: TextAlign.center,
            ),
            SizedBox(height: 12),
            TextFormField(
              maxLength: 4,
              initialValue: bloc.state.recommendedMilliliters.toString(),
              keyboardType: TextInputType.number,
              onSaved: (value) => _text = value,
              validator: _validateText,
              autovalidateMode: AutovalidateMode.onUserInteraction,
              decoration: InputDecoration(
                hintText: "2000 ml",
                counterText: "",
              ),
            ),
            SizedBox(height: 24),
            PrimaryButton(
              onPressed: () 
                if (_form.currentState?.validate() ?? false) 
                  _form.currentState?.save();
                  FocusScope.of(context).unfocus();
                  context.read<WaterBloc>().setRecommendedMilliliters(
                        int.parse(_text!),
                      );
                  Navigator.of(context).pop();
                
              ,
              title: "Confirm",
            ),
            SizedBox(height: 10),
            SecondaryButton(
              onPressed: () => Navigator.of(context).pop(),
              title: "Cancel",
            ),
          ],
        ),
      ),
    );
  

内联显示错误:

  Widget build(BuildContext context) 
    final bloc = context.watch<WaterBloc>();
    return AlertDialog(

第二个文件:dialog.dart

import 'package:flutter/material.dart';
import 'package:animations/animations.dart';

import 'package:stayhydratedpal/widgets/confirmation_dialog.dart';
import 'package:stayhydratedpal/widgets/consumption_dialog.dart';

Future<bool> showConfirmationDialog(
  BuildContext context, 
  required String title,
  required String content,
) async 
  final bool confirmed = await showModal(
        context: context,
        builder: (context) 
          return ConfirmationDialog(
            title: title,
            content: content,
            onConfirm: () => Navigator.of(context).pop(true),
            onCancel: () => Navigator.of(context).pop(false),
          );
        ,
      ) ??
      false;

  return confirmed;


Future<void> showConsumptionDialog(BuildContext context) 
  return showModal(
    context: context,
    builder: (context) => ConsumptionDialog(),
  );

内联显示错误:

Future<void> showConsumptionDialog(BuildContext context) 
  return showModal(
    context: context,
    builder: (context) => ConsumptionDialog(),
  );

【问题讨论】:

【参考方案1】:

提供程序的工作方式是当您执行 Provider.of&lt;T&gt;(context)(上下文必须是您注入 T 的小部件的后代)时,它会查找树以找到您使用 Provider(create: (_)=&gt; T())(也可以是 ChangeNotifierProvider)注入的 T没关系)。导航器堆栈中的路由也不是彼此的父级 他们是

-> page1
-> page2

不是

-> page1
   -> page2

所以这意味着当你使用 Navigator 推送新页面时,Provider 将无法找到你放在 page1 上的注入提供程序。并且 showModal 使用 Navigator push 打开一个对话框,所以基本上就像任何其他路线一样,这意味着您的 ConfirmationDialog 没有找到您可能在打开它的页面中注入的 WaterBloc。

解决这个问题的一种方法是在 Navigator 上方注入 WaterBloc,MaterialApp 包含根导航器,因此在 Material App 上方注入提供程序。

另一种方法是打开对话框时可以这样做

Future<void> showConsumptionDialog(BuildContext context) 
  return showModal(
    context: context,
    builder: (_) => Provider.value(
      value: context.read<WaterBloc>(), // this context must be a descendent of the widget where you injected WaterBloc
      child: ConsumptionDialog(),
    ),
  );

一个小技巧,我建议你学习一下 Inherited Widget,如果你学得好,你可以很容易地使用 Provider,因为 Provider 只是 InheritedWidget 的一个包装器

【讨论】:

以上是关于ProviderNotFoundException 在构建 ConsumptionDialog 时被抛出,因为使用了不包含提供者的`BuildContext`的主要内容,如果未能解决你的问题,请参考以下文章

抛出 providernotfoundexception(t, context.widget.runtimetype);

ProviderNotFoundException(错误:在此主页小部件上方找不到正确的 Provider<EntryProvider>

SpringSecurity 提示ProviderNotFoundException: No AuthenticationProvider found for ****

用户身份验证失败时的 Spring Security ProviderNotFoundException

ProviderNotFoundException 在构建 ConsumptionDialog 时被抛出,因为使用了不包含提供者的`BuildContext`