我有一个Flutter页面/小部件,带有一个搜索框和一个从flutter_bloc BLOC填充的ListView。
输入文本后,我调用BLOC方法搜索结果,结果显示在ListView中。
flutter - 为什么我的PopupMenuItem上的 “Looking up a deactivated widget'的祖先不安全?-LMLPHP
当我单击PopupMenuItem项内的ListTile时,我得到:

flutter - 为什么我的PopupMenuItem上的 “Looking up a deactivated widget'的祖先不安全?-LMLPHP



如果我用stations变量的简单静态列表初始化替换BLOC“dance”,它就可以正常工作。

我完全不知道该怎么办?
有人可以给我一些指示吗?

编辑:
这是一个repro示例(仅适用于Android):
https://github.com/MagnusJohansson/bloc_test
单击应用程序中的搜索图标,输入“test”作为搜索词。单击三点菜单以触发错误(可能必须单击两次)。
如果不执行sqlite查询,而是返回一个静态列表(如文件/bloc/searchpresets/search_presets_bloc.dart第55行中的注释所示),则可以正常工作。

class SearchPresetsPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _SearchPresetsPageState();
}

final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey2 =
    new GlobalKey<RefreshIndicatorState>();

final GlobalKey<ScaffoldState> scaffoldKey2 = new GlobalKey<ScaffoldState>();

final snackBar = SnackBar(
  content: Text('Added as a favorite'),
  action: SnackBarAction(
    label: 'Undo',
    onPressed: () {},
  ),
);

class _SearchPresetsPageState extends State<SearchPresetsPage> {
  final TextEditingController _filter = new TextEditingController();
  List<Station> stations = [];

  _textInputSearch() {
    if (_filter.text.length >= 2) {
      BlocProvider.of<SearchPresetsBloc>(context)
          .add(SearchPresets(_filter.text));
    }
  }

  @override
  void initState() {
    _filter.addListener(_textInputSearch);
    super.initState();
  }

  Widget buildInitialStationsInput(BuildContext context, String message) {
    return Column(children: <Widget>[
      Center(child: Text(message)),
    ]);
  }

  Widget buildLoading() {
    return Center(
      child: LinearProgressIndicator(
        value: null,
      ),
    );
  }

  Widget _buildListView() {
    return RefreshIndicator(
      key: _refreshIndicatorKey2,
      child: Column(
        children: <Widget>[
          Container(
            color: Colors.grey[300],
            alignment: Alignment.topLeft,
            child: Padding(
              padding: const EdgeInsets.all(4.0),
              child: Text(
                "${stations.length} Stations:",
              ),
            ),
          ),
          Expanded(
            child: ListView.builder(
              itemCount: stations != null ? stations.length : 0,
              itemBuilder: (context, index) {
                return Card(
                  child: ListTile(
                    dense: true,
                    title: Text(stations[index].name),
                    leading: IconButton(
                      alignment: Alignment.center,
                      icon: Icon(Icons.play_arrow),
                      onPressed: () async {
                      },
                    ),
                    trailing: PopupMenuButton(
                      onSelected: (value) async {
                      },
                      itemBuilder: (context) {
                        return [
                          PopupMenuItem(
                            value: 1,
                            child: Text("Add to favorites"),
                          ),
                        ];
                      },
                    ),
                  ),
                );
              },
            ),
          ),
        ],
      ),
      onRefresh: () async {
        BlocProvider.of<SearchPresetsBloc>(context)
            .add(SearchPresets(_filter.text));
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return BlocListener<SearchPresetsBloc, SearchPresetsState>(
      listener: (BuildContext context, SearchPresetsState state) {},
      child: Scaffold(
        key: scaffoldKey2,
        appBar: AppBar(
          actions: <Widget>[],
          title: Container(
            color: Colors.white,
            child: TextField(
              controller: _filter,
              decoration: new InputDecoration(
                  filled: true,
                  suffixIcon: IconButton(
                    icon: Icon(Icons.clear),
                    onPressed: () {
                      _filter.clear();
                      setState(() {
                        stations = [];
                        BlocProvider.of<SearchPresetsBloc>(context)
                            .add(ClearSearch());
                      });
                    },
                  ),
                  prefixIcon: new Icon(Icons.search),
                  hintText: 'Search...'),
            ),
          ),
        ),
        body: BlocBuilder<SearchPresetsBloc, SearchPresetsState>(
            builder: (BuildContext context, SearchPresetsState state) {
          if (state is InitialSearchPresetsState) {
            return buildInitialStationsInput(context,
                "Search for stations in the search box above (at least 2 characters)");
          }
          if (state is SearchPresetsLoading) {
            return buildLoading();
          }
          if (state is SearchPresetsLoaded) {
            stations = state.stations;
            return _buildListView();
          }
          if (state is SearchPresetsError) {
            return buildInitialStationsInput(
                context, state.exception.toString());
          }
          return Container();
        }),
      ),
    );
  }
}

最佳答案

这是contexts的问题。它通常在Dialogs中发生,并且PopMenu的行为有点类似于对话框,但是在对话框中,您可以传递所需的context并使用GlobalKey和访问contextScaffold来解决此问题。不幸的是,这是不可能的。

但是,PopMenuButton上有一个标志似乎可以解决您的问题。 captureInheritedThemesdocumentation指出:“如果为true(默认值),则菜单将包裹InheritedThemes的副本,如Theme和PopupMenuTheme,,这些副本在BuildContext上方定义,菜单显示为”。那似乎是我们的问题,上下文的继承。

在您的search_presets_widget.dart文件中,在PopupMenuButton方法内的_buildListView中,添加以下行:

PopupMenuButton(
  captureInheritedThemes: false, // Add this line
  ...

关于flutter - 为什么我的PopupMenuItem上的 “Looking up a deactivated widget'的祖先不安全?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59636507/

10-14 00:45