如何访问 showModalBottomSheet 中提供的 (Provider.of()) 值?

How to access Provided (Provider.of()) value inside showModalBottomSheet?

我在小部件树中有一个 FloatingActionButton,它有一个来自 flutter_blocBlocProvider。像这样:

BlocProvider(
  builder: (context) {
    SomeBloc someBloc = SomeBloc();
    someBloc.dispatch(SomeEvent());

    return someBloc;
  },
  child: Scaffold(
    body: ...
    floatingActionButton: FloatingActionButton(
      onPressed: _openFilterSchedule,
      child: Icon(Icons.filter_list),
    ),
  )
);

打开模态底部 sheet:

void _openFilterSchedule() {
    showModalBottomSheet<void>(
      context: context,
      builder: (BuildContext context) {
        return TheBottomSheet();
      },
    );
  }

我正在尝试使用 TheBottomSheet 中的 BlocProvider.of<SomeBloc>(context) 访问 SomeBloc,但出现以下错误:

BlocProvider.of() called with a context that does not contain a Bloc of type SomeBloc.

我已尝试使用 中描述的解决方案,但仅适用于 BottomSheet 而不适用于 ModalBottomSheet


注意:这不限于 BlocProviderflutter_blocprovider 包中的任何 Provider 都具有相同的行为。

如何在 showModalBottomSheet 中访问 BlocProvider.of<SomeBloc>(context)

如果无法做到这一点,如何使 解决方案适应模态底部 Sheet?

您应该将 Scaffold 小部件及其子部件拆分为另一个 StatefulWidget

来自单个 Widget

class MainScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      builder: (context) {
        SomeBloc someBloc = SomeBloc();
        someBloc.dispatch(SomeEvent());
        return someBloc;
      },
      child: Scaffold(
        body: ...
        floatingActionButton: FloatingActionButton(
          onPressed: _openFilterSchedule,
          child: Icon(Icons.filter_list),
        ),
      )
    );
  }
}

拆分成这两个widget

class MainScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      builder: (context) {
        SomeBloc someBloc = SomeBloc();
        someBloc.dispatch(SomeEvent());
        return someBloc;
      },
      child: Screen(),
    );
  }
}

和..

class Screen extends StatelessWidget {

  void _openFilterSchedule() {
    showModalBottomSheet<void>(
      context: context,
      builder: (BuildContext context) {
        return TheBottomSheet();
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ...
      floatingActionButton: FloatingActionButton(
        onPressed: _openFilterSchedule,
        child: Icon(Icons.filter_list),
      ),
    );
  }
}

InheritedWidgets 和 Provider 的范围都在小部件树中。无法在该树之外访问它们。

事实是,使用 showDialog 和类似的功能,对话框位于不同的小部件树中——它可能无法访问所需的提供程序。

因此有必要在新的小部件树中添加所需的提供程序:

void myShowDialog() {
  final myModel = Provider.of<MyModel>(context, listen: false);
  showDialog(
    context: context,
    builder: (_) {
      return Provider.value(value: myModel, child: SomeDialog());
    },
  );
}

您需要将 Provider 移动到顶层(MaterialApp)

根据图片,对话框小部件位于 MaterialApp 下,所以这就是您使用错误上下文的原因

我找到了一个解决方案,只需 return 带有 StatefulBuilder 的 showModalBottomSheet 并使用模态表生成器的上下文传递给您的提供商。下面是我的代码片段:

Future<Widget> showModal(int qty, Product product) async {
    return await showModalBottomSheet(
        isScrollControlled: true,
        backgroundColor: Colors.transparent,
        context: context,
        builder: (BuildContext ctx) {
          return StatefulBuilder(builder: (ctx, state) {
             return Container(
                  child: RaisedButton(
                          onPressed: () {
                             Product prod = Product(product.id, 
                                         product.sku, product.name, qty);
                             Provider.of<CartProvider>(ctx, listen: 
                                        false).addCart(prod);}),);
    }
  }
);
}

提供商在 showModalBottomSheet (Bottom-Sheet)

void myBottomSheet() {
  final myModel = Provider.of<MyModel>(context, listen: false);
  showModalBottomShee(
    context: context,
    builder: (_) {
      return ListenableProvider.value(
        value: myModel,
        child: Text(myModel.txtValue),
      );
    },
  );
}

在处理 showModelBottomSheet 时遇到了同样的问题,因为它恰好在不同的(上下文)小部件树中工作,所以我必须将我的状态升级到应用程序的状态,以便我可以使用上下文访问我的提供者。

TLDR:确保导入语句的大小写与项目的文件夹大小写匹配。

我在调试同样的错误时遇到了另一个怪癖。我有几个都在工作的提供程序,包括在 showModalBottomSheets 中,但是一个不工作。在梳理了整个小部件树之后,在没有发现任何差异的情况下,我发现我在 problem-child 通知程序的一个导入语句中将文件夹的首字母大写了。我认为这混淆了编译器并导致它抛出 Could not find the correct Provider above this widget 错误。

确保导入语句的大小写与文件夹名称匹配后,我的提供商问题就解决了。希望这会让一些人头疼。

没有找到添加多个提供值的明确解释,我想在这里分享以供参考。

   await showMobileModals(
    isDismissible: false,
    context: context,
    child: MultiProvider(
      providers: [
        Provider.value(
          value: provided_one,
        ),
        Provider.value(
          value: provided_two,
        ),
        Provider.value(
          value: provided_three,
        ),
      ],
      child: Container(),
    ),
  );