Flutter开发WhatsApp克隆:滚动到相机页时隐藏AppBar与TabBar
实现方法
核心逻辑就是监听Tab选中状态,切到第一个相机Tab时动态隐藏SliverAppBar和TabBar,切到其他Tab时恢复原有固定布局,和原版WhatsApp交互一致。
修改步骤
- 弃用直接嵌套的
DefaultTabController,在State中手动初始化TabController,添加监听器实时记录当前选中的Tab索引,页面销毁时记得释放控制器资源。 - 给
NestedScrollView绑定滚动控制器,切Tab时自动触发滚动动画,平滑收起/展开头部,避免布局跳变。 - 在
headerSliverBuilder中根据当前Tab索引判断是否渲染SliverAppBar:选中相机Tab时返回空的占位Sliver,其他Tab正常渲染你原来写的AppBar和TabBar。 - 给TabBar手动绑定你创建的TabController,保证Tab切换和状态同步。
核心代码示例
首先是State中的初始化和生命周期逻辑:
late TabController _tabController; int _currentTabIndex = 1; // 默认选中聊天Tab,对齐原版WhatsApp交互 final ScrollController _scrollController = ScrollController(); @override void initState() { super.initState(); _tabController = TabController(length: 5, vsync: this, initialIndex: 1); _tabController.addListener(() { if (!_tabController.indexIsChanging) return; setState(() { _currentTabIndex = _tabController.index; }); // 切到相机Tab时平滑收起头部 if (_currentTabIndex == 0) { WidgetsBinding.instance.addPostFrameCallback((_) { _scrollController.animateTo( _scrollController.position.maxScrollExtent, duration: const Duration(milliseconds: 240), curve: Curves.easeOutCubic, ); }); } else { // 切回其他Tab时平滑展开头部 WidgetsBinding.instance.addPostFrameCallback((_) { _scrollController.animateTo( 0, duration: const Duration(milliseconds: 240), curve: Curves.easeOutCubic, ); }); } }); } @override void dispose() { _tabController.dispose(); _scrollController.dispose(); super.dispose(); }
然后是布局部分的修改:
return Scaffold( body: NestedScrollView( controller: _scrollController, headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { // 相机Tab不渲染头部,全屏显示相机内容 if (_currentTabIndex == 0) { return const [SliverToBoxAdapter(child: SizedBox.shrink())]; } return <Widget>[ SliverAppBar( title: const Text("WhatsApp",style: TextStyle( color: Colors.white, fontWeight: FontWeight.w400, fontSize: 22.0, ),), backgroundColor: const Color.fromRGBO(0, 117,94,1), actions: [ IconButton(onPressed: (){}, icon: const Icon(Icons.wifi,color: Colors.white,size: 30,)), IconButton(onPressed: (){}, icon: const Icon(FontAwesomeIcons.solidMoon,color: Colors.white,)), IconButton(onPressed: (){}, icon: const Icon(Icons.search,color: Colors.white,)), IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert,color: Colors.white,)), ], pinned: true, floating: true, bottom: TabBar( controller: _tabController, indicatorColor:Colors.white, indicatorWeight: 3, labelColor: Colors.white, labelStyle: const TextStyle(fontWeight: FontWeight.bold,), unselectedLabelColor: Colors.white70, tabs: const [ Tab(icon: Icon(Icons.camera_alt, color: Colors.white,),), Tab(text: "CHATS",), Tab(text: "GROUPS",), Tab(text: "STATUS"), Tab(text: "CALLS"), ], ), ), ]; }, // 后续TabBarView记得绑定_tabController,每个Tab对应你的页面内容即可 ) );
踩坑提示:不要直接修改SliverAppBar的pinned/floating参数做显隐,那样会导致TabBar布局重绘时闪烁,直接根据索引返回空Sliver的方案更稳定,240ms的动画时长和原生WhatsApp的交互手感几乎一致。
内容的提问来源于stack exchange,提问作者Aladeloye Doyinsola
相关产品推荐
相关产品推荐

