Flutter Deer顶部导航栏:实现分类切换
在移动应用开发中,顶部导航栏(AppBar)是用户交互的重要组成部分,而分类切换功能则是提升用户体验的关键。Flutter Deer项目通过自定义AppBar组件和TabBar实现了高效的分类切换功能,本文将详细解析其实现原理和核心代码。
导航栏组件设计
Flutter Deer项目采用了自定义的导航栏组件,主要通过lib/widgets/my_app_bar.dart实现基础样式,而商品页面则使用原生AppBar结合自定义元素构建更复杂的导航功能。
基础导航栏实现
项目中大多数页面使用了自定义的MyAppBar组件,其定义如下:
class MyAppBar extends StatelessWidget implements PreferredSizeWidget {
const MyAppBar({
super.key,
this.title = '',
this.centerTitle = true,
this.backgroundColor,
this.leading,
this.actions,
this.bottom,
this.elevation = 0.5,
this.titleWidget,
});
// ...实现代码
}
这个自定义导航栏提供了灵活的配置选项,包括标题、背景色、左侧按钮、右侧操作按钮等,满足不同页面的导航需求。
商品页面导航栏设计
商品页面采用了更复杂的导航栏设计,结合了搜索、添加功能和分类切换。核心实现代码位于lib/goods/page/goods_page.dart:
appBar: AppBar(
actions: <Widget>[
IconButton(
tooltip: '搜索商品',
onPressed: () => NavigatorUtils.push(context, GoodsRouter.goodsSearchPage),
icon: LoadAssetImage(
'goods/search',
key: const Key('search'),
width: 24.0,
height: 24.0,
color: iconColor,
),
),
IconButton(
tooltip: '添加商品',
key: _addKey,
onPressed: _showAddMenu,
icon: LoadAssetImage(
'goods/add',
key: const Key('add'),
width: 24.0,
height: 24.0,
color: iconColor,
),
)
],
),
这个导航栏包含了搜索按钮和添加按钮,点击添加按钮会触发一个弹出菜单,实现代码在_showAddMenu方法中。
TabBar分类切换实现
Flutter Deer的商品页面使用TabBar实现了"在售"、"待售"、"下架"三个状态的快速切换,这是通过结合TabBar和PageView实现的。
核心实现代码
在lib/goods/page/goods_page.dart中,TabBar的实现代码如下:
Padding(
padding: const EdgeInsets.only(left: 16.0),
child: TabBar(
onTap: (index) {
if (!mounted) {
return;
}
_pageController.jumpToPage(index);
},
isScrollable: true,
controller: _tabController,
labelStyle: TextStyles.textBold18,
indicatorSize: TabBarIndicatorSize.label,
labelPadding: EdgeInsets.zero,
unselectedLabelColor: context.isDark ? Colours.text_gray : Colours.text,
labelColor: Theme.of(context).primaryColor,
indicatorPadding: const EdgeInsets.only(right: 98.0 - 36.0),
// 隐藏点击效果
overlayColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
return Colors.transparent;
},
),
tabs: const <Widget>[
_TabView('在售', 0),
_TabView('待售', 1),
_TabView('下架', 2),
],
),
),
Tab与PageView联动
TabBar与PageView的联动通过以下代码实现:
Expanded(
child: PageView.builder(
key: const Key('pageView'),
itemCount: 3,
onPageChanged: _onPageChange,
controller: _pageController,
itemBuilder: (_, int index) => GoodsListPage(index: index)
),
)
void _onPageChange(int index) {
_tabController?.animateTo(index);
provider.setIndex(index);
}
当用户点击Tab时,通过onTap回调跳转到对应Page;当用户滑动PageView时,通过onPageChanged回调更新Tab选中状态。
自定义Tab视图
项目中还实现了自定义的Tab视图_TabView,用于显示分类名称和商品数量:
class _TabView extends StatelessWidget {
const _TabView(this.tabName, this.index);
final String tabName;
final int index;
@override
Widget build(BuildContext context) {
return Tab(
child: SizedBox(
width: 98.0,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(tabName),
Consumer<GoodsPageProvider>(
builder: (_, provider, child) {
return Visibility(
visible: !(provider.goodsCountList[index] == 0 || provider.index != index),
child: Padding(
padding: const EdgeInsets.only(top: 1.0),
child: Text(' (${provider.goodsCountList[index]}件)',
style: const TextStyle(fontSize: Dimens.font_sp12),
),
),
);
},
),
],
),
),
);
}
}
这个自定义Tab视图通过Consumer监听商品数量变化,动态显示每个分类下的商品数量,只有当数量大于0且当前Tab被选中时才显示。
商品分类菜单实现
除了顶部的Tab分类,Flutter Deer还实现了商品类型分类菜单,通过点击"全部商品"文本区域触发。
分类菜单触发
分类菜单的触发代码位于lib/goods/page/goods_page.dart:
Semantics(
container: true,
label: '选择商品类型',
child: GestureDetector(
key: _buttonKey,
child: Selector<GoodsPageProvider, int>(
selector: (_, provider) => provider.sortIndex,
builder: (_, sortIndex, __) {
return Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Gaps.hGap16,
Text(
_sortList[sortIndex],
style: TextStyles.textBold24,
),
Gaps.hGap8,
LoadAssetImage('goods/expand', width: 16.0, height: 16.0, color: iconColor,)
],
);
},
),
onTap: () => _showSortMenu(),
),
),
分类菜单弹窗
点击后触发_showSortMenu方法显示分类菜单弹窗:
void _showSortMenu() {
// 获取点击控件的坐标
final RenderBox button = _buttonKey.currentContext!.findRenderObject()! as RenderBox;
final RenderBox body = _bodyKey.currentContext!.findRenderObject()! as RenderBox;
showPopupWindow<void>(
context: context,
offset: const Offset(0.0, 12.0),
anchor: button,
child: GoodsSortMenu(
data: _sortList,
height: body.size.height - button.size.height,
sortIndex: provider.sortIndex,
onSelected: (index, name) {
provider.setSortIndex(index);
Toast.show('选择分类: $name');
},
),
);
}
分类菜单的UI实现位于lib/goods/widgets/goods_sort_bottom_sheet.dart,使用了TabBar实现二级分类:
Widget _buildView() {
return Column(
children: <Widget>[
Container(
height: 44.0,
color: Colours.bg_color,
child: TabBar(
controller: _tabController,
isScrollable: true,
labelStyle: TextStyles.textBold14,
unselectedLabelStyle: TextStyles.text14,
labelColor: Theme.of(context).primaryColor,
unselectedLabelColor: Colours.text,
indicatorSize: TabBarIndicatorSize.label,
indicator: UnderlineTabIndicator(
borderSide: BorderSide(width: 2.0, color: Theme.of(context).primaryColor),
insets: const EdgeInsets.only(top: 41.0, bottom: 1.0),
),
tabs: const <Widget>[
Tab(text: '商品分类'),
Tab(text: '品牌'),
Tab(text: '价格区间'),
],
),
),
Gaps.line,
Expanded(
child: TabBarView(
controller: _tabController,
children: <Widget>[
_buildCategoryView(),
_buildBrandView(),
_buildPriceRangeView(),
],
),
),
],
);
}
状态管理与数据交互
导航栏和分类切换功能的实现离不开状态管理,Flutter Deer项目使用Provider模式管理商品页面的状态。
商品页面状态管理
商品页面的状态管理类GoodsPageProvider定义在lib/goods/provider/goods_page_provider.dart,主要管理以下状态:
- 当前选中的Tab索引
- 商品分类索引
- 各分类下的商品数量
核心代码如下:
class GoodsPageProvider extends ChangeNotifier {
int _index = 0;
int _sortIndex = 0;
final List<int> _goodsCountList = <int>[10, 5, 3];
int get index => _index;
int get sortIndex => _sortIndex;
List<int> get goodsCountList => _goodsCountList;
void setIndex(int index) {
_index = index;
notifyListeners();
}
void setSortIndex(int index) {
_sortIndex = index;
notifyListeners();
}
}
通过调用notifyListeners()方法通知UI更新,实现状态与UI的响应式交互。
数据交互与更新
在实际应用中,商品数量等数据通常来自网络请求。项目中的网络请求封装在lib/net/dio_utils.dart中,通过Dio库实现网络请求,具体API定义在lib/net/http_api.dart。
总结与最佳实践
Flutter Deer项目的顶部导航栏和分类切换功能实现了以下关键技术点:
- 自定义AppBar组件:通过封装lib/widgets/my_app_bar.dart实现统一的导航栏样式
- TabBar与PageView联动:实现平滑的分类切换效果
- 自定义Tab视图:显示动态数据并优化用户体验
- 弹出菜单实现:通过自定义弹窗实现复杂的分类选择功能
- 状态管理:使用Provider模式管理页面状态,实现响应式UI
这些实现方式不仅满足了功能需求,还兼顾了性能优化和用户体验,是Flutter应用开发中导航和分类功能实现的良好实践。项目中还有更多细节值得学习,如lib/goods/page/goods_edit_page.dart中的商品编辑页面实现,以及lib/order/page/order_page.dart中订单页面的SliverAppBar实现等。
通过学习Flutter Deer项目的导航栏和分类切换实现,开发者可以掌握复杂UI交互的实现方法,提升Flutter应用的用户体验。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考






