【问题标题】:Bloc provider above OverlayEntry flutterOverlayEntry 之上的 Bloc 提供者颤动
【发布时间】:2022-12-31 08:47:55
【问题描述】:

我的 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 提供程序?这是最好的做法吗?

感谢所有可以提供帮助的人!

【问题讨论】:

    标签: flutter


    【解决方案1】:

    问题是您使用的是函数而不是小部件。因此,您可以将 _createOverlay 修改为无状态或有状态小部件,或者您可以将 bloc 作为参数传递给函数。

    在后一种情况下,这将是_createOverlay(expense, context.read&lt;AppBloc&gt;())

    【讨论】:

    • 刚刚看到您标记了引发错误的行,我认为问题出在其他地方。但无论如何,答案依然存在。问题是您已经声明了一个返回小部件而不是小部件的函数。实际上建议使用小部件而不是函数,因此您可能希望将其转换为有状态/无小部件
    • 不,如果它是一个函数而不是一个小部件类并不重要,如果它通常被放置在 blocprovider 内的小部件树中。作者是完全正确的,覆盖行为不同只是因为它在 blocprovider 上下文之外。如果您尝试从对话框访问 blocprovider,也会发生同样的情况。
    • 谢谢,我不知道覆盖层。无论如何,我猜,解决方案 2 - 将 bloc 作为参数传递 - 应该可行。
    【解决方案2】:

    将您在 OverlayEntry 中使用的小部件包装在 BlocProvider.value 构造函数中,并将所需的 bloc 作为参数传递给它,就像这样

    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),
            ),
          ),
        ),
      );
    }
    

    【讨论】:

      猜你喜欢
      • 2019-06-16
      • 2020-04-29
      • 2021-11-01
      • 2020-12-06
      • 2022-01-15
      • 2020-06-15
      • 1970-01-01
      • 2020-05-10
      • 2019-10-25
      相关资源
      最近更新 更多