簡體   English   中英

OverlayEntry flutter 上方的 Bloc 提供商

[英]Bloc provider above OverlayEntry flutter

我的 flutter 應用程序出現問題。 我正在嘗試在下面的照片中添加這樣的疊加層:

在此處輸入圖像描述

而且它工作得很好,我可以長按打開它,然后在屏幕上的其他地方點擊關閉它。 問題是這兩個按鈕 - 刪除和編輯 - 應該調用一個 bloc 方法,然后執行所有邏輯,但我在 OverlayEntry 之上沒有 bloc 提供程序。 這是錯誤:

Error: Could not find the correct Provider<BrowseBloc> above this _OverlayEntryWidget Widget

This happens because you used a `BuildContext` that does not include the provider
of your choice. There are a few common scenarios:

- You added a new provider in your `main.dart` and performed a hot-reload.
  To fix, perform a hot-restart.

- The provider you are trying to read is in a different route.

  Providers are "scoped". So if you insert of provider inside a route, then
  other routes will not be able to access that provider.

- You used a `BuildContext` that is an ancestor of the provider you are trying to read.

  Make sure that _OverlayEntryWidget is under your MultiProvider/Provider<BrowseBloc>.
  This usually happens when you are creating a provider and trying to read it immediately.

  For example, instead of:

  ```
  Widget build(BuildContext context) {
    return Provider<Example>(
      create: (_) => Example(),
      // Will throw a ProviderNotFoundError, because `context` is associated
      // to the widget that is the parent of `Provider<Example>`
      child: Text(context.watch<Example>().toString()),
    );
  }
  ```

  consider using `builder` like so:

  ```
  Widget build(BuildContext context) {
    return Provider<Example>(
      create: (_) => Example(),
      // we use `builder` to obtain a new `BuildContext` that has access to the provider
      builder: (context, child) {
        // No longer throws
        return Text(context.watch<Example>().toString());
      }
    );
  }
  ```

If none of these solutions work, consider asking for help on StackOverflow:
https://stackoverflow.com/questions/tagged/flutter

我已經遇到過這個錯誤,但這次我遇到了一些麻煩,因為我正在使用疊加層而不是小部件。

這是我的代碼:

late OverlayEntry _popupDialog;

class ExpenseCard extends StatelessWidget {
  const ExpenseCard({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BlocConsumer<AppBloc, AppState>(
      listener: (context, state) {},
      buildWhen: (previous, current) => previous.theme != current.theme,
      builder: (context, state) {
        return Column(
          children: [
            GestureDetector(
              onLongPress: () {
                _popupDialog = _createOverlay(expense);
                Overlay.of(context)?.insert(_popupDialog);
              },
              child: Card(
                ...some widgets
              ),
            ),
            const Divider(height: 0),
          ],
        );
      },
    );
  }
}

OverlayEntry _createOverlay(Expenses e) {
  return OverlayEntry(
    builder: (context) => GestureDetector(
      onTap: () => _popupDialog.remove(),
      child: AnimatedDialog(
        child: _createPopupContent(context, e),
      ),
    ),
  );
}

Widget _createPopupContent(BuildContext context, Expenses e) {
  return GestureDetector(
    onTap: () {},
    child: Column(
      mainAxisSize: MainAxisSize.min,
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Container(
          width: MediaQuery.of(context).size.width * 0.9,
          decoration: BoxDecoration(
            color: LocalCache.getActiveTheme() == ThemeMode.dark ? darkColorScheme.surface : lightColorScheme.surface,
            borderRadius: const BorderRadius.all(Radius.circular(16)),
          ),
          padding: const EdgeInsets.all(16.0),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              ...some other widgets
            ],
          ),
        ),
        SizedBox(
          width: 256,
          child: Card(
            child: Column(
              children: [
                InkWell(
                  onTap: () {
                    _popupDialog.remove();
                    // This is where the error is been thrown
                    context.read<BrowseBloc>().add(SetTransactionToEdit(e));
                    showBottomModalSheet(
                      context,
                      dateExpense: e.dateExpense,
                      total: e.total,
                      transactionToEdit: e,
                    );
                  },
                  child: Padding(
                    padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
                    child: Row(
                      children: [Text(AppLocalizations.of(context).edit), const Spacer(), const Icon(Icons.edit)],
                    ),
                  ),
                ),
                const Divider(height: 0),
                InkWell(
                  onTap: () {
                    _popupDialog.remove();
                    // This is where the error is been thrown
                    context.read<BrowseBloc>().add(DeleteExpense(e.id!, e.isExpense));
                  },
                  child: Padding(
                    padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
                    child: Row(
                      children: [Text(AppLocalizations.of(context).delete), const Spacer(), const Icon(Unicons.delete)],
                    ),
                  ),
                ),
              ],
            ),
          ),
        ),
      ],
    ),
  );
}

如何在我的 OverlayEntry 上方添加 bloc 提供程序? 這是最好的做法嗎?

感謝所有可以提供幫助的人!

將您在OverlayEntry中使用的小部件包裝在BlocProvider.value構造函數中,並將所需的塊作為參數傳遞給它,就像這樣

OverlayEntry _createOverlay(Expenses e, ExampleBloc exampleBloc) {
  return OverlayEntry(
    builder: (context) => GestureDetector(
      onTap: () => _popupDialog.remove(),
      child: BlocProvider<ExampleBloc>.value(
        value: exampleBloc,
        child: AnimatedDialog(
          child: _createPopupContent(context, e),
        ),
      ),
    ),
  );
}

我找到了一個從 Olga P 的答案開始的解決方案,但改變了一件事。 我使用 BlocProvider.value 但我將上下文而不是 bloc 本身作為參數傳遞給方法。 這是代碼:

OverlayEntry _createOverlay(Expenses e, BuildContext context) {
  return OverlayEntry(
    builder: (_) => GestureDetector(
      onTap: () => _popupDialog.remove(),
      child: BlocProvider<BrowseBloc>.value(
        value: BlocProvider.of(context),
        child: AnimatedDialog(
          child: _createPopupContent(context, e),
        ),
      ),
    ),
  );
}

通過此更改,兩種方法 - 編輯和刪除 - 完美地工作。 感謝大家的回復,我今天也學到了東西!

問題是您使用的是 function 而不是小部件。 因此,您可以將 _createOverlay 修改為無狀態或有狀態小部件,或者您可以將 bloc 作為參數傳遞給 function。

在后一種情況下,這將是_createOverlay(expense, context.read<AppBloc>())

暫無
暫無

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

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