Flutter自定义AppBar及搜索栏实现咨询(附Figma设计图)
实现自定义AppBar与搜索栏的Flutter方案
一、核心思路
这类自定义布局通常是将搜索栏与AppBar的导航元素(返回键、标题等)整合到同一顶部区域,或是通过PreferredSizeWidget封装一个包含导航区+搜索栏的自定义组件,直接替代默认AppBar。
二、基础实现代码示例
1. 自定义顶部组件(替代默认AppBar)
如果你的设计要求搜索栏和导航元素在同一视觉区域,可以用以下方式封装:
class CustomTopBar extends StatelessWidget implements PreferredSizeWidget { @override Size get preferredSize => const Size.fromHeight(120); // 自定义高度,适配导航区+搜索栏总高度 @override Widget build(BuildContext context) { return Container( color: Colors.white, // 匹配设计稿背景色 padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Column( children: [ // 顶部导航区 Row( children: [ IconButton( icon: const Icon(Icons.arrow_back), onPressed: () => Navigator.pop(context), ), const SizedBox(width: 12), const Text( "页面标题", style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), ], ), const SizedBox(height: 12), // 搜索栏 Container( decoration: BoxDecoration( color: Colors.grey[200], borderRadius: BorderRadius.circular(8), ), child: TextField( decoration: const InputDecoration( hintText: "搜索...", prefixIcon: Icon(Icons.search), border: InputBorder.none, contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 14), ), ), ), ], ), ); } }
2. 在页面中使用自定义组件
class TargetPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: CustomTopBar(), // 替换默认AppBar body: const Center( child: Text("页面内容区域"), ), ); } }
三、细节适配调整
- 高度校准:根据Figma设计的实际尺寸,修改
preferredSize中的fromHeight数值 - 样式匹配:调整搜索栏的背景色、圆角、输入框字体,以及导航区的图标颜色、标题字体,完全对齐设计稿
- 交互扩展:若需要实时搜索功能,给
TextField添加onChanged回调处理输入内容
四、进阶滚动收起效果实现
如果设计要求滚动页面时搜索栏或AppBar有收起/展开效果,可结合SliverAppBar和NestedScrollView实现:
class ScrollableTopBarPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: NestedScrollView( headerSliverBuilder: (context, innerBoxIsScrolled) { return [ SliverAppBar( pinned: true, title: const Text("滚动页面标题"), bottom: PreferredSize( preferredSize: const Size.fromHeight(60), child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), color: Colors.white, child: Container( decoration: BoxDecoration( color: Colors.grey[200], borderRadius: BorderRadius.circular(8), ), child: const TextField( decoration: InputDecoration( hintText: "搜索...", prefixIcon: Icon(Icons.search), border: InputBorder.none, contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 14), ), ), ), ), ), ), ]; }, body: ListView.builder( itemCount: 50, itemBuilder: (context, index) { return ListTile(title: Text("列表项 $index")); }, ), ), ); } }
内容的提问来源于stack exchange,提问作者Hagi Matahari Julio
相关产品推荐
相关产品推荐

