You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter Material3中Search Anchor返回时断言失败问题求助

解决Flutter Material3 SearchAnchor断言错误:_anchor != null

问题原因

这个断言错误本质是SearchAnchor内部状态不一致导致的:当组件因searchController文本变化重建、或hot reload时,SearchAnchor的内部锚点实例(_anchor)尚未完成初始化,就触发了依赖该实例的操作;另外,焦点管理不当、Controller生命周期未正确处理也会加剧这个状态异常。

解决方案

1. 规范SearchController与FocusNode的生命周期

在State类中初始化并销毁Controller和FocusNode,避免重建时复用无效实例:

class _YourPageState extends State<YourPage> {
  late final SearchController searchController;
  late final FocusNode searchFocusNode;
  final GlobalKey<SearchAnchorState> _searchAnchorKey = GlobalKey(); // 新增SearchAnchor的Key

  @override
  void initState() {
    super.initState();
    searchController = SearchController();
    searchFocusNode = FocusNode();
  }

  @override
  void dispose() {
    searchController.dispose();
    searchFocusNode.dispose();
    super.dispose();
  }
  // ... 其他代码
}

2. 修正View返回按钮的逻辑

移除创建临时FocusNode的操作,改为直接取消焦点,再关闭搜索视图,避免干扰SearchAnchor的内部状态:

viewLeading: IconButton(
  onPressed: () {
    // 先取消焦点,再关闭视图
    searchFocusNode.unfocus();
    searchController.closeView("");
  },
  icon: const Icon(Icons.arrow_back)),

3. 给SearchAnchor添加GlobalKey

确保组件重建时,SearchAnchor的状态被正确保留,避免内部_anchor实例丢失:

SearchAnchor(
  key: _searchAnchorKey, // 新增这一行
  viewLeading: ..., // 其他原有代码
  // ... 其他参数
)

4. 简化SearchBar的onTap逻辑

openView后SearchAnchor会自动处理焦点,无需手动请求,避免重复操作导致状态冲突:

onTap: () {
  searchController.openView();
  // 移除这一行:FocusScope.of(context).requestFocus(focusNode);
},

修改后的完整代码片段

class _YourPageState extends State<YourPage> {
  late final SearchController searchController;
  late final FocusNode searchFocusNode;
  final GlobalKey<SearchAnchorState> _searchAnchorKey = GlobalKey();
  final GlobalKey _searchBarKey = GlobalKey();
  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey();
  double height = 0;
  double width = 0;
  Color textFieldBackgoundColor = Colors.grey[100]!;

  @override
  void initState() {
    super.initState();
    searchController = SearchController();
    searchFocusNode = FocusNode();
  }

  @override
  void dispose() {
    searchController.dispose();
    searchFocusNode.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    height = MediaQuery.of(context).size.height;
    width = MediaQuery.of(context).size.width;
    return Scaffold(
      key: _scaffoldKey,
      body: SearchAnchor(
        key: _searchAnchorKey,
        viewLeading: IconButton(
          onPressed: () {
            searchFocusNode.unfocus();
            searchController.closeView("");
          },
          icon: const Icon(Icons.arrow_back)),
        searchController: searchController,
        isFullScreen: true,
        builder: (BuildContext context, SearchController controller) {
          return SearchBar(
            key: _searchBarKey,
            padding: const MaterialStatePropertyAll<EdgeInsets>(
              EdgeInsets.only(left: 8.0, right: 8.0),
            ),
            hintText: "Search your notes",
            hintStyle: const MaterialStatePropertyAll(TextStyle(
                fontSize: 16,
                fontWeight: FontWeight.normal,
                color: Colors.black54)),
            leading: IconButton(
              icon: const Icon(Icons.menu),
              onPressed: () {
                _scaffoldKey.currentState!.openDrawer();
              },
            ),
            focusNode: searchFocusNode,
            shape: MaterialStatePropertyAll(RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(50))),
            controller: controller,
            onTap: () {
              controller.openView();
            },
            trailing: [],
          );
        },
        viewBuilder: (suggestions) {
          return Container(
            height: height,
            width: width,
            color: textFieldBackgoundColor,
            child: const Center(child: Icon(Icons.mood, color: Colors.black)),
          );
        },
        suggestionsBuilder: (BuildContext context, SearchController controller) {
          return <Widget>[const Icon(Icons.abc)];
        },
      ),
    );
  }
}

额外提示

如果上述方案仍未解决,可尝试升级Flutter SDK到最新稳定版——这个_anchor != null的断言错误曾在部分老版本的Material3组件中存在,官方后续已修复相关状态同步问题。

内容的提问来源于stack exchange,提问作者Priyanshu Paliwal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 22:47:29