Flutter点击搜索时隐藏Tab名称实现方案咨询
Flutter问题:点击搜索框时隐藏Tab名称
我是Flutter新手,想实现点击搜索框时隐藏Tab名称的效果——未点击搜索框时正常显示Tab栏;点击搜索框后Tab栏隐藏,搜索框占据原本Tab栏的空间。但当前自定义TabbedCard组件的实现效果不符合预期,代码如下:
class TabbedCard extends StatefulWidget { final List<Tab> tabs; final List<Widget> children; final bool showDeleteBtn; final bool showPrimaryBtn; const TabbedCard( {Key? key, required this.tabs, required this.children, this.showDeleteBtn = false, this.showPrimaryBtn = true}) : super(key: key); @override State<TabbedCard> createState() => _TabbedCardState(); } class _TabbedCardState extends State<TabbedCard> with TickerProviderStateMixin { @override Widget build(BuildContext context) { double _scrHeight = MediaQuery.of(context).size.height - 150; // String searchText = ""; TextEditingController textController = new TextEditingController(); TabController _controller = TabController(length: widget.tabs.length, vsync: this); TabBar _tabBar = new TabBar( isScrollable: true, labelColor: kPrimaryColor, unselectedLabelColor: kTextGray, controller: _controller, tabs: widget.tabs, ); return Container( constraints: BoxConstraints(minHeight: _scrHeight), child: SingleChildScrollView( child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: const EdgeInsets.symmetric( vertical: 1, horizontal: 1, ), decoration: BoxDecoration( border: Border.all( color: Colors.white, ), color: Colors.white, boxShadow: [ BoxShadow( color: Colors.white.withOpacity(0.5), spreadRadius: -4, blurRadius: 35, offset: const Offset(0, 9), // changes position of shadow ), ], ), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Align( alignment: Alignment.topLeft, child: Row( children: [ AnimSearchBar( width: 400, textController: textController, onSuffixTap: (value) { setState(() {}); }, ), const SizedBox(width: 10), _tabBar, const SizedBox(width: 10), ], )), const SizedBox(height: 10), Container( height: 440, child: TabBarView( controller: _controller, children: widget.children, ), ), ], ), ), const SizedBox(height: 10), ], ), ), ); } }
当前实现的核心问题:
- 控制器(TextEditingController、TabController)在
build方法内声明,每次组件重建都会重新初始化,导致状态丢失 - 未监听搜索框的焦点状态,无法触发Tab栏的显示/隐藏逻辑
修复方案
通过添加焦点监听、提取状态变量,实现Tab栏的动态显示/隐藏:
class TabbedCard extends StatefulWidget { final List<Tab> tabs; final List<Widget> children; final bool showDeleteBtn; final bool showPrimaryBtn; const TabbedCard( {Key? key, required this.tabs, required this.children, this.showDeleteBtn = false, this.showPrimaryBtn = true}) : super(key: key); @override State<TabbedCard> createState() => _TabbedCardState(); } class _TabbedCardState extends State<TabbedCard> with TickerProviderStateMixin { // 提取为成员变量,避免build重建导致状态丢失 late final TextEditingController _textController; late final TabController _tabController; late final FocusNode _searchFocusNode; // 跟踪搜索框是否获得焦点 bool _isSearchFocused = false; @override void initState() { super.initState(); _textController = TextEditingController(); _tabController = TabController(length: widget.tabs.length, vsync: this); _searchFocusNode = FocusNode(); // 监听搜索框焦点变化,更新状态 _searchFocusNode.addListener(() { setState(() { _isSearchFocused = _searchFocusNode.hasFocus; }); }); } @override void dispose() { // 释放资源,避免内存泄漏 _textController.dispose(); _tabController.dispose(); _searchFocusNode.dispose(); super.dispose(); } @override Widget build(BuildContext context) { double _scrHeight = MediaQuery.of(context).size.height - 150; TabBar _tabBar = TabBar( isScrollable: true, labelColor: kPrimaryColor, unselectedLabelColor: kTextGray, controller: _tabController, tabs: widget.tabs, ); return Container( constraints: BoxConstraints(minHeight: _scrHeight), child: SingleChildScrollView( child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: const EdgeInsets.symmetric(vertical: 1, horizontal: 1), decoration: BoxDecoration( border: Border.all(color: Colors.white), color: Colors.white, boxShadow: [ BoxShadow( color: Colors.white.withOpacity(0.5), spreadRadius: -4, blurRadius: 35, offset: const Offset(0, 9), ), ], ), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Align( alignment: Alignment.topLeft, child: Row( children: [ // 根据焦点状态动态调整搜索框宽度 AnimSearchBar( width: _isSearchFocused ? MediaQuery.of(context).size.width - 40 : 400, textController: _textController, focusNode: _searchFocusNode, onSuffixTap: (value) { setState(() {}); }, ), const SizedBox(width: 10), // 搜索框聚焦时隐藏Tab栏 if (!_isSearchFocused) _tabBar, const SizedBox(width: 10), ], ), ), const SizedBox(height: 10), Container( height: 440, child: TabBarView( controller: _tabController, children: widget.children, ), ), ], ), ), const SizedBox(height: 10), ], ), ), ); } }
关键修改说明:
- 将控制器和
FocusNode移到initState中初始化,在dispose中释放资源,避免内存泄漏和状态丢失 - 通过
FocusNode监听搜索框焦点变化,更新_isSearchFocused状态 - 根据焦点状态动态控制TabBar的显示/隐藏,同时调整搜索框宽度,让搜索框在聚焦时占满可用空间
- 给
AnimSearchBar传入focusNode,确保焦点监听生效
内容的提问来源于stack exchange,提问作者ApizMan
相关产品推荐
相关产品推荐

