flutter - 使用不包含FicheMvtBloc类型的Bloc的上下文调用的BlocProvider.of()

标签 flutter dart flutter-bloc

我正在使用BLoC模式开发一个新的Flutter Mobile应用程序。但是我有一个问题,我还没有找到解决方案。
第一个是我的主页(使用MultiBlocProvider)
当我按下FloatingActionButton时。
它推送一个新屏幕以添加新的“FicheMvt”
当我点击添加按钮。
它使用onSave回调函数来通知其父项新创建的“FicheMvt”
它给了我一个错误。

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

No ancestor could be found starting from the context that was passed to BlocProvider.of().

This can happen if the context you used comes from a widget above the BlocProvider.


这是主页(呈现5个选项卡主体)
class EtatCollecteScreen extends StatelessWidget {
  final FicheMvtDAO ficheMvtDAO = FicheMvtDAO();
  final FicheMvtReferenceDAO ficheMvtReferenceDAO = FicheMvtReferenceDAO();

  @override
  Widget build(BuildContext context) {
    final FicheModel fiche = ModalRoute.of(context).settings.arguments;

    return MultiBlocProvider(
      providers: [
        BlocProvider<TabEtatCollecteBloc>(
          create: (context) => TabEtatCollecteBloc(),
        ),
        BlocProvider<FicheMvtBloc>(
          create: (context) => FicheMvtBloc(
            ficheMvtDAO: ficheMvtDAO,
          )..add(FicheMvtRequested(idFiche: fiche.id)),
        ),
        BlocProvider<FicheMvtReferenceBloc>(
          create: (context) => FicheMvtReferenceBloc(
            ficheMvtReferenceDAO: ficheMvtReferenceDAO,
          )..add(FicheMvtReferenceRequested(idFiche: fiche.id)),
        ),
      ],
      child: EtatCollecteContent(
        ficheModel: fiche,
      ),
    );
  }
}


class EtatCollecteContent extends StatelessWidget {
  final FicheModel ficheModel;

  const EtatCollecteContent({Key key, @required this.ficheModel});

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<TabEtatCollecteBloc, EtatCollecteTab>(
      builder: (context, activeTab) {
        return Scaffold(
          appBar: AppBar(
            title: Text("${ficheModel.id} - ${ficheModel.description}"),
            actions: <Widget>[
              RefreshMvtButton(
                visible: activeTab == EtatCollecteTab.completed,
                ficheModel: ficheModel,
              ),
              SendMvtButton(
                visible: activeTab == EtatCollecteTab.uncommitted,
                ficheModel: ficheModel,
              ),
            ],
          ),
          body: EtatCollecteBody(
            activeTab: activeTab,
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) {
                    return FicheMvtAddScreen(onSaveCallback: (idFiche, indicateurModel, codeSite) {
                      BlocProvider.of<FicheMvtBloc>(context).add(
                        FicheMvtAdded(
                          idFiche: idFiche,
                          indicateurModel: indicateurModel,
                          codeSite: codeSite,
                        ),
                      );
                    });
                  },
                  settings: RouteSettings(
                    arguments: ficheModel,
                  ),
                ),
              );
            },
            child: Icon(Icons.add),
            tooltip: "Add",
          ),
          bottomNavigationBar: TabEtatCollecteSelector(
            activeTab: activeTab,
            onTabSelected: (tab) => BlocProvider.of<TabEtatCollecteBloc>(context).add(TabEtatCollecteUpdated(tab)),
          ),
        );
      },
    );
  }
}
这是添加新“FicheMvt”的表单的代码,其中包含另一个管理动态表单的块(FicheMvtAddBloc)。
typedef OnSaveCallback = Function(
  int idFiche,
  IndicateurModel indicateurModel,
  String codeSite,
);

class FicheMvtAddScreen extends StatelessWidget {
  final OnSaveCallback onSaveCallback;

  const FicheMvtAddScreen({Key key, @required this.onSaveCallback}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
    final FicheModel fiche = ModalRoute.of(context).settings.arguments;
    final FicheMvtRepository ficheMvtRepository = FicheMvtRepository();

    return Scaffold(
      key: _scaffoldKey,
      appBar: new AppBar(
        title: new Text("${fiche.id} - ${fiche.description}"),
      ),
      backgroundColor: Colors.white,
      body: BlocProvider<FicheMvtAddBloc>(
        create: (context) => FicheMvtAddBloc(
          ficheMvtRepository: ficheMvtRepository,
          idFiche: fiche.id,
        )..add(NewFicheMvtFormLoaded(idFiche: fiche.id)),
        child: FicheMvtAddBody(
          ficheModel: fiche,
          onSave: onSaveCallback,
        ),
      ),
    );
  }
}
这是表格的内容
class FicheMvtAddBody extends StatefulWidget {
  final FicheModel ficheModel;

  final OnSaveCallback onSave;

  @override
  _FicheMvtAddBodyState createState() => _FicheMvtAddBodyState();

  FicheMvtAddBody({Key key, @required this.ficheModel, @required this.onSave});
}

class _FicheMvtAddBodyState extends State<FicheMvtAddBody> {
  static final GlobalKey<FormState> _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    void _onIndicateurChanged(String indicateur) =>
        BlocProvider.of<FicheMvtAddBloc>(context).add(NewFicheMvtIndicateurChanged(indicateur: indicateur));
    void _onSiteChanged(String site) => BlocProvider.of<FicheMvtAddBloc>(context).add(NewFicheMvtSiteChanged(site: site));

    final FicheModel fiche = ModalRoute.of(context).settings.arguments;

    final txtIndicateur = Text("Indicateur");
    final txtSite = Text("Site");

    return BlocBuilder<FicheMvtAddBloc, FicheMvtAddState>(
      builder: (context, state) {
        return Form(
          key: _formKey,
          child: Center(
            child: ListView(
              shrinkWrap: true,
              padding: EdgeInsets.only(left: 24.0, right: 24.0),
              children: <Widget>[
                SizedBox(height: 24.0),
                txtIndicateur,
                DropdownButtonFormField<String>(
                  isExpanded: true,
                  hint: Text("Choisissez l'indicateur"),
                  value: state.indicateur?.code ?? null,
                  icon: Icon(Icons.arrow_downward),
                  iconSize: 24,
                  elevation: 16,
                  onChanged: (String newValue) {
                    _onIndicateurChanged(newValue);
                  },
                  items: state.indicateurs?.isNotEmpty == true
                      ? state.indicateurs
                          .map((CodeDescriptionModel model) => DropdownMenuItem(value: model.code, child: Text(model.description)))
                          .toList()
                      : const [],
                  validator: (value) {
                    if (value == null || value.isEmpty) {
                      return 'Entrer l\'indicateur s\'il vous plait';
                    }
                    return null;
                  },
                ),
                SizedBox(height: 24.0),
                txtSite,
                DropdownButtonFormField<String>(
                  isExpanded: true,
                  hint: Text("Choisissez le site"),
                  value: state.site?.code ?? null,
                  icon: Icon(Icons.arrow_downward),
                  iconSize: 24,
                  elevation: 16,
                  onChanged: (String newValue) {
                    _onSiteChanged(newValue);
                  },
                  items: state.sites?.isNotEmpty == true
                      ? state.sites.map((CodeDescriptionModel model) => DropdownMenuItem(value: model.code, child: Text(model.description))).toList()
                      : const [],
                  validator: (value) {
                    if (value == null || value.isEmpty) {
                      return 'Entrer le site s\'il vous plait';
                    }
                    return null;
                  },
                ),
                Padding(
                  padding: EdgeInsets.symmetric(vertical: 16.0),
                  child: RaisedButton(
                    shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(24),
                    ),
                    onPressed: () {
                      if (_formKey.currentState.validate()) {
                        

                        widget.onSave(
                          fiche.id,
                          state.indicateur,
                          state.site?.code ?? null,
                        );

            Navigator.pop(context);
                      }
                    },
                    padding: EdgeInsets.all(12),
                    color: Colors.blue,
                    child: Text('Create', style: TextStyle(color: Colors.white)),
                  ),
                )
              ],
            ),
          ),
        );
      },
    );
  }
}
谢谢你的帮助

最佳答案

您在onSaveCallback中使用了错误的上下文。这是您的小部件的简化层次结构:

- MaterialApp
  - EtatCollecteScreen
    - MultiBlocProvider
  - FicheMvtAddScreen
因此,在onSaveCallback中,您正在访问FicheMvtAddScreen的上下文,从上面的层次结构中可以明显地看出BlocProvider找不到请求的Bloc。解决这个问题很容易:
    MaterialPageRoute(
  builder: (pageContext) {
    return FicheMvtAddScreen(onSaveCallback: (idFiche, indicateurModel, codeSite) {
      BlocProvider.of<FicheMvtBloc>(context).add(
        FicheMvtAdded(
          idFiche: idFiche,
          indicateurModel: indicateurModel,
          codeSite: codeSite,
        ),
      );
    });
  },
  settings: RouteSettings(
    arguments: ficheModel,
  ),
  ),
我在路由生成器函数中将上下文变量重命名为pageContext(这样就不会遮盖了所需的上下文)。现在,BlocProvider应该能够通过访问正确的上下文找到请求的Bloc
解决的另一种方法是将MultiBlocProvider放在小部件层次结构中较高的位置。

关于flutter - 使用不包含FicheMvtBloc类型的Bloc的上下文调用的BlocProvider.of(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63100433/

相关文章:

android - 如何从 Flutter 页面导航到原生 Android 页面

dart - Dart Polymer:当编译为JS时,从DOM中删除元素似乎已损坏?

flutter - ConnectionState在主类中更改两次

android - 带有ListTiles和Button行的Flutter下拉菜单

json - Flutter - 如何将嵌套的 json 解析为具有泛型的类?

dart - 从命令行调试 Dart 程序

flutter - Dart:有没有办法不使用 Dart 的 split 方法将字符串拆分成句子?

flutter - 更改 flutter 的本地化与 Bloc 状态管理问题

flutter - 常量创建的参数必须是常量表达式,同时使用卡住实现模型

flutter - 如何测试异步生成器引发的异常?