1. 程式人生 > 程式設計 >100 行程式碼實現Flutter自定義TabBar的示例程式碼

100 行程式碼實現Flutter自定義TabBar的示例程式碼

Flutter 的確很強大,但美中不足的是生態還有待完善,沒有出現像前端的 Antd 或 Element 那樣全能的基礎 UI 庫。
由此帶來的直接影響是開發效率提不上去,需要耗費大量的時間精力在基礎元件的封裝上。

官方的 TabBar 不滿足需求,又沒有合適的輪子,只好自己造輪子啦。接下來帶你一步步實現自定義 TabBar……

一、目標和效果

需求目標是:

  • 這個頁面不要 material 左側統一的返回鍵和 Title
  • 在右側有取消按鈕,點取消即返回
  • 點選 Tab 可以實現 content 切換並帶有動畫效果
  • 滑動內容區域也可以切換 Tab

效果如下圖:

100 行程式碼實現Flutter自定義TabBar的示例程式碼

100 行程式碼實現Flutter自定義TabBar的示例程式碼

二、實現思路


將整個頁面分為兩部分,上面的 Tab 按鈕和下面的內容區域。

為了保持通用性,上面的 Tab 和下面的內容區域都需要讓呼叫者傳入,它們都是 Widget 陣列

class STab extends StatefulWidget {
 // tab 集合
 final List<Widget> tabs;
 // 頁面集合
 final List<Widget> pages;

 STab({this.tabs,this.pages});

 @override
 _STabState createState() => _STabState();
}

頁面整體的佈局是一個 Column ,上面是 Tab 區域,下面的 Content 區域用 Expand 包裹,達到撐滿整個螢幕的效果。

上面的 Tab 佈局,最外層是 Stack 佈局,因為需要取消按鈕一直在最右邊且不能影響 tab 按鈕的排版。多個 tab 按鈕用橫向佈局 Row 來排列,並設定居中對齊。

 @override
 Widget build(BuildContext context) {
  return Container(
    child: Column(
   children: [
    TabLayout(widget.tabs,selectedIndex,onTabChange,onCancelClick),ContentLayout(widget.pages,swipeControl,onPageChange)
   ],));
 }

下面的內容區域,要實現左右滑動切換的效果,用了一個第三方庫 flutter_swiper 。當點選 Tab 的時候,設定 swiper 的下標切換顯示的內容;當左右滑動 swiper,設定 tab 的選中狀態,達到 tab 選中狀態跟 swiper 滑動的聯動。

三、元件封裝

/// tab 切換元件
import 'package:flutter/material.dart';
import 'package:flutter_swiper/flutter_swiper.dart';

class STab extends StatefulWidget {
 // tab 集合
 final List<Widget> tabs;

 // 頁面集合
 final List<Widget> pages;

 STab({this.tabs,this.pages});

 @override
 _STabState createState() => _STabState();
}

class _STabState extends State<STab> {
 int selectedIndex = 0;
 SwiperController swipeControl = new SwiperController();

 // tab 索引變化回撥
 void onTabChange(index) {
  setState(() {
   selectedIndex = index;
  });
  swipeControl.move(index);
 }

 void onCancelClick() {
  print('cancel');
 }

 void onPageChange(index) {
  setState(() {
   selectedIndex = index;
  });
 }

 @override
 Widget build(BuildContext context) {
  return Container(
    child: Column(
   children: [
    TabLayout(widget.tabs,));
 }
}

/// 上面 Tab 的佈局
Widget TabLayout(tabs,onRightButtonClick) {
 List<Widget> getItem() {
  List<Widget> children = [];
  for (var i = 0; i < tabs.length; i++) {
   children.add(
    GestureDetector(
      onTap: () {
       onTabChange(i);
      },child: Container(
       padding: EdgeInsets.only(left: 20,right: 20,bottom: 10),decoration: BoxDecoration(
         border: Border(
           bottom: BorderSide(
             color: selectedIndex == i
               ? Color(0xff595959)
               : Colors.transparent,width: 3))),child: tabs[i],)),);
  }
  return children;
 }

 return Stack(
  children: [
   Row(
    mainAxisAlignment: MainAxisAlignment.center,children: getItem(),),Positioned(
     top: 0,right: 0,child: GestureDetector(
      child: Container(
       height: 40,padding: EdgeInsets.only(left: 10,right: 10,child: Text(
        '取消',textAlign: TextAlign.center,style: TextStyle(fontSize: 16),onTap: () {
       onRightButtonClick();
      },))
  ],);
}

/// 下面頁面內容佈局
Widget ContentLayout(pages,onIndexChanged) {
 return Expanded(
  child: Container(
    decoration: BoxDecoration(color: Colors.white),child: Swiper(
     itemCount: pages.length,itemBuilder: (BuildContext context,int index) {
      return pages[index];
     },loop: false,onIndexChanged: (index) {
      onIndexChanged(index);
     },controller: swipeControl,);
}

四、如何使用

傳入 tabs 和 頁面 pages 即可

class Demo extends StatelessWidget {

 final List<Widget> tabBodies = [
  ExpensePage(),IncomePage(),];

 @override
 Widget build(BuildContext context) {
  return Scaffold(
   body: Container(
     padding: EdgeInsets.only(top: 30),decoration: BoxDecoration(
       color: Color(0xffF9DC62)
     ),child: STab(
      tabs: [
       Text('支出',style: TextStyle(fontSize: 18,color: Colors.black),Text('收入',color: Colors.black)),],pages: tabBodies,);
 }
}

五、結語

元件的封裝只是根據業務簡單的封裝了一下,沒有考慮到更多的情況,比如右側的取消按鈕也應該由外部傳入,顏色也應該由外部傳入,還有沒校驗傳入的資料是否合法……大家可以根據自己的實際業務需求調整原始碼。

至於封裝到什麼程度才算好,適合、夠用就好。

如果只是給自己的業務用,那封裝到這樣就夠了,只需考慮業務內的場景。如果要開源出去給別人共用,那最好給予更高的定製化能力。

到此這篇關於100 行程式碼實現Flutter自定義TabBar的示例程式碼的文章就介紹到這了,更多相關Flutter自定義TabBar內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!