? ? ?TabBar
還有TabBarView
都是谷歌flutter官方組件庫——Material組件庫提供的組件,其中TabBar
用于導航切換,TabBarView
則是配合其切換顯示的對應的視圖,官網參考地址:TabBarView class - material library - Dart API。
? ? ? ? 實現一體聯動有兩種實現方式:使用默認控制器(DefaultTabController)和自定義控制器。使用自定義控制器靈活性更高,但是需要指定TabController的length屬性,但有些情況下欄目的實際數據是從網絡上異步加載讀取的,這個TabController的length無法動態更新或后面重新指定,非常扯淡,具體實現參考:flutter 之 TabBar、TabBarView的使用 - 簡書。
? ? ? 本文主要介紹在使用DefaultTabController下,實現獲取點擊當前欄目的索引,包括點擊TabBar和滑動TabBarView以及視圖狀態保持,示例如下:
late int _selectIndex = 0;late final ScrollController _scrollController;late final NewsPageViewModel _vm = NewsPageViewModel();late final _scaffoldKey = GlobalKey<_NewsPageViewState>();@overridevoid initState() {// TODO: implement initStatesuper.initState();//欄目滑動索引改變_scrollController = ScrollController(onDetach: (ScrollPosition position) {if (_scaffoldKey.currentContext != null) {final controller =DefaultTabController.of(_scaffoldKey.currentContext!);controller.addListener(() {if (controller.index.toDouble() == controller.animation?.value) {//loadListDataFor(controller.index);debugPrint(controller.index);}});}});}@overrideWidget build(BuildContext context) {// TODO: implement buildreturn BaseView<NewsPageViewModel>(viewModel: _vm,build: (context, viewModel, child) {if (viewModel.state == ViewState.Busy) {return BaseView.loadingWidget();} else {return DefaultTabController(initialIndex: _selectIndex,length: viewModel.arrCategory.length,child: NestedScrollView(controller: _scrollController,headerSliverBuilder:(BuildContext context, bool innerBoxIsScrolled) {return <Widget>[SliverOverlapAbsorber(handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),sliver: SliverAppBar(title: Utils.shareInstance.customPageTitle('資訊'),//標題橫向間距titleSpacing: 15,//左側的圖標或文字,多為返回箭頭leading: null,//左邊按鈕的寬度leadingWidth: 15,//居中centerTitle: false,//標題欄是否固定pinned: true,//滑動時是否懸浮floating: false,//配合floating使用snap: false,//是否顯示在狀態欄的下面,false就會占領狀態欄的高度primary: true,//設置分欄區域上面的高度// expandedHeight: 0,elevation: 0,//是否顯示陰影,直接取值innerBoxIsScrolled,展開不顯示陰影,合并后會顯示forceElevated: innerBoxIsScrolled,//自定義導航和中間內容的展示flexibleSpace: null,//導航欄背景色backgroundColor: K_APP_NAVIGATION_BACKGROUND_COLOR,//TabBar 分欄標題bottom: _setCategoryTabBar(viewModel),),)];},//分欄展示的頁面信息body: _setCategoryTabBarView(context, viewModel),));}},onModelReady: (viewModel) {//加載欄目viewModel.categoryLoad(context, isLoading: false);});}@overridevoid dispose() {_scrollController.dispose();// TODO: implement disposesuper.dispose();}//MARK: - 擴展
extension on _NewsPageViewState {//分欄菜單TabBar _setCategoryTabBar(NewsPageViewModel viewModel) {return TabBar(key: _scaffoldKey,//設置對齊方式(否則左邊有空白)tabAlignment: TabAlignment.start,//是否允許滾動isScrollable: true,//未選中的顏色unselectedLabelColor: Utils.shareInstance.hexToInt(0x505050),//未選中的樣式unselectedLabelStyle: const TextStyle(fontSize: 15),//選中的顏色labelColor: K_APP_TINT_COLOR,//選中的樣式labelStyle: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),//底部滑動條樣式indicatorSize: TabBarIndicatorSize.label,//底部滑動條顏色indicatorColor: K_APP_TINT_COLOR,//菜單項目tabs: viewModel.arrCategory.map((item) {return Tab(text: item.name);}).toList(),//點擊事件onTap: (index) {debugPrint(index);//loadListDataFor(index);setState(() {_selectIndex = index;});},);}// tabBar 分欄菜單各個頁面Widget _setCategoryTabBarView(BuildContext context, NewsPageViewModel viewModel) {return TabBarView(children: viewModel.arrCategory.map((item) {//設置UIreturn SafeArea(top: false,bottom: false,child: Builder(builder: (BuildContext context) {return CustomScrollView(slivers: [SliverOverlapInjector(handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),),SliverPadding(padding: EdgeInsets.zero,sliver: SliverFixedExtentList(delegate: SliverChildBuilderDelegate((BuildContext context, int index) {final m = viewModel.arrList[index];if (m.className == '快訊') {//7x24快訊return NewsPageViewInformationCell(index, m, context, viewModel);}return NewsPageViewCell(index, m, context, viewModel);}, childCount: viewModel.arrList.length),itemExtent: 50.0 //item高度或寬度,取決于滑動方向),)],);},));}).toList());}}
上面方法進過實際測試,可以實現點擊和滑動時獲取當前欄目的索引,以便根據索引執行加載當前頁面的欄目數據的業務邏輯。隨之會產生新的問題就是欄目來回切換沒有保持頁面的狀態,會重復加載數據,解決思路是借助?AutomaticKeepAliveClientMixin 并設置?wantKeepAlive 為true,在PageView和PageController組合中同樣適用,效果如下:
86_1720350605
參考Demo,日拱一卒,持續更新