繁体   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