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

时间:2020-07-26 12:55:05

标签: flutter dart flutter-bloc

我正在使用BLoC模式开发一个新的Flutter Mobile应用程序。但是我有一个问题,我还没有找到解决方案。

第一个是我的主页(使用MultiBlocProvider) 当我按下FloatingActionButton时。 它推送一个新屏幕以添加新的“ FicheMvt” 当我点击添加按钮。 它使用onSave回调函数来通知其父项新创建的“ FicheMvt” 它给了我一个错误。

在不包含Bloc的上下文中调用

BlocProvider.of() 类型为FicheMvtBloc。

从所传递的上下文开始找不到祖先 到BlocProvider.of()。

如果您使用的上下文来自上方的小部件,则可能会发生这种情况 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)),
                  ),
                )
              ],
            ),
          ),
        );
      },
    );
  }
}

感谢您的帮助

1 个答案:

答案 0 :(得分:1)

您在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放在小部件层次结构中较高的位置。