Flutter Deer顶部导航栏:实现分类切换

Flutter Deer顶部导航栏:实现分类切换

【免费下载链接】flutter_deer 🦌 Flutter 练习项目(包括集成测试、可访问性测试)。内含完整UI设计图,更贴近真实项目的练习。Flutter practice project (including integration testing and accessibility testing). Contains complete UI design drawings for a more realistic practice project. 【免费下载链接】flutter_deer 项目地址: https://gitcode.com/gh_mirrors/fl/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分类切换效果

商品分类菜单实现

除了顶部的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项目的顶部导航栏和分类切换功能实现了以下关键技术点:

  1. 自定义AppBar组件:通过封装lib/widgets/my_app_bar.dart实现统一的导航栏样式
  2. TabBar与PageView联动:实现平滑的分类切换效果
  3. 自定义Tab视图:显示动态数据并优化用户体验
  4. 弹出菜单实现:通过自定义弹窗实现复杂的分类选择功能
  5. 状态管理:使用Provider模式管理页面状态,实现响应式UI

这些实现方式不仅满足了功能需求,还兼顾了性能优化和用户体验,是Flutter应用开发中导航和分类功能实现的良好实践。项目中还有更多细节值得学习,如lib/goods/page/goods_edit_page.dart中的商品编辑页面实现,以及lib/order/page/order_page.dart中订单页面的SliverAppBar实现等。

通过学习Flutter Deer项目的导航栏和分类切换实现,开发者可以掌握复杂UI交互的实现方法,提升Flutter应用的用户体验。

【免费下载链接】flutter_deer 🦌 Flutter 练习项目(包括集成测试、可访问性测试)。内含完整UI设计图,更贴近真实项目的练习。Flutter practice project (including integration testing and accessibility testing). Contains complete UI design drawings for a more realistic practice project. 【免费下载链接】flutter_deer 项目地址: https://gitcode.com/gh_mirrors/fl/flutter_deer

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值