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

Flutter中SliverAppBar搜索栏滚动切换效果实现请求

实现搜索栏滚动迁移至SliverAppBar的方案

核心思路

通过ScrollController监听滚动偏移量,配合状态控制/动画组件,实现页面初始搜索栏与SliverAppBar内搜索栏的平滑切换:滚动到指定阈值时,隐藏页面顶部的搜索栏,同时在SliverAppBar中显示搜索栏;反向滚动则恢复初始状态。

完整代码示例

import 'package:flutter/material.dart';

class SearchBarScrollDemo extends StatefulWidget {
  const SearchBarScrollDemo({super.key});

  @override
  State<SearchBarScrollDemo> createState() => _SearchBarScrollDemoState();
}

class _SearchBarScrollDemoState extends State<SearchBarScrollDemo> with SingleTickerProviderStateMixin {
  final ScrollController _scrollController = ScrollController();
  // 触发切换的滚动阈值,与SliverAppBar展开高度一致
  final double _offsetThreshold = 100.0;
  late AnimationController _animationController;
  late Animation<double> _pageSearchOpacity;
  late Animation<double> _appBarSearchOpacity;

  @override
  void initState() {
    super.initState();
    _scrollController.addListener(_onScroll);
    // 初始化动画控制器,实现平滑过渡
    _animationController = AnimationController(
      duration: const Duration(milliseconds: 300),
      vsync: this,
    );
    _pageSearchOpacity = Tween<double>(begin: 1, end: 0).animate(_animationController);
    _appBarSearchOpacity = Tween<double>(begin: 0, end: 1).animate(_animationController);
  }

  void _onScroll() {
    if (_scrollController.offset >= _offsetThreshold) {
      _animationController.forward();
    } else {
      _animationController.reverse();
    }
  }

  @override
  void dispose() {
    _scrollController.dispose();
    _animationController.dispose();
    super.dispose();
  }

  // 复用搜索栏组件,减少冗余代码
  Widget _buildSearchBar() {
    return Container(
      margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(8),
        boxShadow: const [
          BoxShadow(color: Colors.black12, blurRadius: 4, offset: Offset(0, 2))
        ],
      ),
      child: const TextField(
        decoration: InputDecoration(
          hintText: "搜索...",
          prefixIcon: Icon(Icons.search),
          border: InputBorder.none,
          contentPadding: EdgeInsets.symmetric(vertical: 12),
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        controller: _scrollController,
        slivers: [
          SliverAppBar(
            pinned: true,
            backgroundColor: Colors.blue,
            // AppBar内的搜索栏,随动画渐显
            title: AnimatedOpacity(
              opacity: _appBarSearchOpacity.value,
              duration: const Duration(milliseconds: 300),
              child: _appBarSearchOpacity.value > 0 ? _buildSearchBar() : const Text("首页"),
            ),
            expandedHeight: _offsetThreshold,
            flexibleSpace: FlexibleSpaceBar(
              background: Container(color: Colors.blue),
            ),
          ),
          // 页面初始显示的搜索栏,随动画渐隐
          SliverToBoxAdapter(
            child: AnimatedOpacity(
              opacity: _pageSearchOpacity.value,
              duration: const Duration(milliseconds: 300),
              child: _buildSearchBar(),
            ),
          ),
          // 页面内容列表
          SliverList(
            delegate: SliverChildBuilderDelegate(
              (context, index) => ListTile(
                title: Text("列表项 $index"),
                subtitle: const Text("测试内容"),
              ),
              childCount: 50,
            ),
          ),
        ],
      ),
    );
  }
}

关键细节说明

  1. 滚动阈值设置:_offsetThreshold需与SliverAppBar的expandedHeight保持一致,确保AppBar完全收起时完成搜索栏的切换。
  2. 动画优化:使用AnimationController配合AnimatedOpacity实现搜索栏的平滑显隐,避免生硬切换。
  3. 组件复用:封装_buildSearchBar确保两个搜索栏样式统一,减少代码冗余。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 14:01:05