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

Flutter项目需求:搜索模式下隐藏SliverAppBar右侧操作按钮

我来帮你解决这个问题!要实现激活搜索模式时隐藏SliverAppBar右侧的action按钮,核心是让父组件(HomePage)能感知到SearchBar的搜索状态变化,然后根据这个状态控制按钮的显示。下面是具体的实现步骤和修改后的代码:

1. 把HomePage改为StatefulWidget

因为需要维护搜索激活状态,所以先将原本的StatelessWidget改为StatefulWidget,添加本地状态变量来跟踪搜索模式是否激活。

2. 给SearchBar添加状态回调

在SearchBar的构造函数中新增一个回调参数,用于将搜索状态变化通知给父组件。

3. 在SearchBar中触发状态回调

当搜索模式激活或取消时,调用回调函数把状态传递给HomePage。

4. 根据状态控制action按钮显示

在HomePage的SliverAppBar中,根据搜索状态决定是否渲染右侧的action按钮。


修改后的完整代码片段:

首先是HomePage部分:

class HomePage extends StatefulWidget with NavigationStates {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  // 跟踪搜索模式是否激活
  bool _isSearchActive = false;

  @override
  Widget build(BuildContext context) {
    final sliverAppBar = SliverAppBar(
      pinned: true,
      expandedHeight: 140.0,
      backgroundColor: Theme.of(context).primaryColor,
      flexibleSpace: FlexibleSpaceBar(
        title: Text(
          AppStrings.homeTitle,
          style: Theme.of(context).textTheme.title.copyWith(
                color: CustomColors().novaWhite,
              ),
        ),
        background: Column(
          children: <Widget>[
            SearchBar(
              canSearch: true,
              // 添加回调接收搜索状态变化
              onSearchModeChanged: (isActive) {
                setState(() {
                  _isSearchActive = isActive;
                });
              },
            ),
          ],
        ),
      ),
      // 根据搜索状态动态显示/隐藏action按钮
      actions: _isSearchActive
          ? []
          : <Widget>[
              IconButton(
                icon: Icon(Icons.filter_list),
                color: CustomColors().novaWhite,
                onPressed: () {},
              ),
              IconButton(
                icon: Icon(Icons.more_vert),
                color: CustomColors().novaWhite,
                onPressed: () {},
              ),
            ],
    );

    final pageContent = SafeArea(
      bottom: false,
      child: CustomScrollView(
        slivers: <Widget>[
          sliverAppBar,
          SliverToBoxAdapter(
            child: Container(
              height: MediaQuery.of(context).size.height,
              padding: EdgeInsets.all(Constants.paddingXY_32),
              decoration: BoxDecoration(
                color: Theme.of(context).scaffoldBackgroundColor,
              ),
              // Page content begins here...
            ),
          ),
        ],
      ),
    );

    return Scaffold(
      body: pageContent,
      backgroundColor: Theme.of(context).primaryColor,
    );
  }
}

然后是SearchBar部分:

class SearchBar extends StatefulWidget implements PreferredSizeWidget {
  final bool canSearch;
  // 新增回调参数,用于通知父组件搜索状态变化
  final ValueChanged<bool> onSearchModeChanged;

  const SearchBar({
    Key key,
    this.canSearch = false,
    @required this.onSearchModeChanged,
  }) : super(key: key);

  @override
  Size get preferredSize => Size.fromHeight(56.0);

  @override
  _SearchBar createState() => _SearchBar();
}

class _SearchBar extends State<SearchBar> with SingleTickerProviderStateMixin {
  double rippleStartX, rippleStartY;
  AnimationController _controller;
  Animation _animation;
  bool isInSearchMode = false;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this, duration: Duration(milliseconds: 500));
    _animation = Tween(begin: 0.0, end: 1.0).animate(_controller);
    _controller.addStatusListener(animationStatusListener);
  }

  void animationStatusListener(AnimationStatus animationStatus) {
    if (animationStatus == AnimationStatus.completed) {
      setState(() {
        isInSearchMode = true;
      });
      // 通知父组件:搜索模式已激活
      widget.onSearchModeChanged(true);
    }
  }

  void onSearchTapUp(TapUpDetails details) {
    setState(() {
      rippleStartX = details.globalPosition.dx;
      rippleStartY = details.globalPosition.dy;
    });
    print("pointer location $rippleStartX, $rippleStartY");
    _controller.forward();
  }

  void cancelSearch() {
    setState(() {
      isInSearchMode = false;
    });
    onSearchQueryChange('');
    _controller.reverse();
    // 通知父组件:搜索模式已取消
    widget.onSearchModeChanged(false);
  }

  void onSearchQueryChange(String query) {
    print('search $query');
  }

  @override
  Widget build(BuildContext context) {
    double screenWidth = MediaQuery.of(context).size.width;
    return Stack(
      children: <Widget>[
        AppBar(
          elevation: 0.0,
          backgroundColor: Theme.of(context).primaryColor,
          leading: widget.canSearch
              ? GestureDetector(
                  child: IconButton(
                    icon: Icon(
                      Icons.search,
                      color: CustomColors().novaWhite,
                    ),
                    onPressed: null,
                  ),
                  onTapUp: onSearchTapUp,
                )
              : Container(),
        ),
        AnimatedBuilder(
          animation: _animation,
          builder: (context, child) {
            return CustomPaint(
              painter: Painter(
                containerHeight: widget.preferredSize.height,
                center: Offset(rippleStartX ?? 0, rippleStartY ?? 0),
                radius: _animation.value * screenWidth,
                context: context,
              ),
            );
          },
        ),
        isInSearchMode
            ? Search(
                onCancelSearch: cancelSearch,
                onSearchQueryChanged: onSearchQueryChange,
              )
            : Container()
      ],
    );
  }
}

这个方案通过组件间回调的方式实现了状态通信,逻辑简单直接,适合当前的场景。如果后续你的项目状态管理需求更复杂,也可以考虑使用Provider、Riverpod等状态管理库来优化,但这个方案已经能完美解决你当前的问题了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 09:22:47