簡體   English   中英

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

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

我正在使用 BLoC 模式開發一個新的 Flutter 移動應用程序。 但我有一個問題,我還沒有找到解決方案。

第一個是我的主頁(帶有 MultiBlocProvider),當我按下 FloatingActionButton 時。 當我點擊添加按鈕時,它會推送一個新屏幕以添加一個新的“FicheMvt”。 它使用 onSave 回調 function 通知其父級新創建的“FicheMvt”它給了我一個錯誤。

BlocProvider.of() 使用不包含 FicheMvtBloc 類型的 Bloc 的上下文調用。

從傳遞給 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)),
                  ),
                )
              ],
            ),
          ),
        );
      },
    );
  }
}

謝謝你的幫助

您在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,
  ),
  ),

我已在路由構建器 function 中將上下文變量重命名為pageContext (因此它不會影響所需的上下文)。 現在BlocProvider應該能夠通過訪問正確的上下文找到請求的Bloc

另一種修復方法是將MultiBlocProvider放在小部件層次結構中更高的位置。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM