flutter實戰(zhàn)2:為APP增加上下Tab頁

緊接上一篇的有側邊欄APP泞歉,這次我們向APP中加入上下Tab頁,使之跟趨近主流大部分APP的信息布局套路宇弛,等不及看源碼的同學可以點擊進入我的git倉庫下載代碼鸡典。

Tab關鍵元素

  • TabController
    這是Tab頁的控制器,用于定義Tab標簽和內容頁的坐標枪芒,還可配置標簽頁的切換動畫效果等彻况。

TabController一般放入有狀態(tài)控件中使用,以適應標簽頁數量和內容有動態(tài)變化的場景舅踪,如果標簽頁在APP中是靜態(tài)固定的格局纽甘,則可以在無狀態(tài)控件中加入簡易版的DefaultTabController以提高運行效率,畢竟無狀態(tài)控件要比有狀態(tài)控件更省資源抽碌,運行效率更快悍赢。

  • TabBar
    Tab頁的Title控件,切換Tab頁的入口货徙,一般放到AppBar控件下使用左权,內部有*Title屬性。其子元素按水平橫向排列布局痴颊,如果需要縱向排列赏迟,請使用ColumnListView控件包裝一下。子元素為Tab類型的數組蠢棱。

  • TabBarView
    Tab頁的內容容器锌杀,其內放置Tab頁的主體內容甩栈。子元素可以是多個各種類型的控件。

Tab使用方法

有狀態(tài)控件搭配TabController

Tab頁的切換搭配了動畫糕再,因此到State類上附加一個SingleTickerProviderStateMixin:

  //定義有狀態(tài)控件
  class HomePage extends StatefulWidget {
    @override
    _HomePageState createState() => new _HomePageState();
  }

  //用于使用到了一點點的動畫效果谤职,因此加入了SingleTickerProviderStateMixin
  class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin{
    ...
  }

然后到有狀態(tài)控件的子類State中初始化控制器TabController

  @override
  void initState() {
    super.initState();
    _tabController = new TabController(
      vsync: this,     //動畫效果的異步處理,默認格式亿鲜,背下來即可
      length: 3      //需要控制的Tab頁數量
    );    
  }
  //當整個頁面dispose時允蜈,記得把控制器也dispose掉,釋放內存
  @override
  void dispose() {
    _tabController .dispose();
    super.dispose();
  }

然后到TabBarTabBarView中的controller屬性中調用控制器_tabController

  //標簽頁標題
  new TabBar(
          tabs: [    //注意TabBar的子元素為Tab類型的數組
            new Tab(icon: new Icon(Icons.directions_car)),
            new Tab(icon: new Icon(Icons.directions_transit)),
            new Tab(icon: new Icon(Icons.directions_bike)),
          ]
  //標簽頁內容區(qū)域
  new TabBarView(
        children: [
          new Icon(Icons.directions_car),
          new Icon(Icons.directions_transit),
          new Icon(Icons.directions_bike),
        ]

最后蒿柳,我們把定義好的TabBarTabBarView安放到需要的地方去饶套,比如:

new Scaffold(
      appBar: new AppBar(
        backgroundColor: Colors.deepOrange,
        title: new Text('title'),
      ),
      ....
      body: new TabBarView(        //TabBarView呈現(xiàn)內容,因此放到Scaffold的body中
          controller: _bottomNavigation,      //配置控制器
          children:  [      //注意順序與TabBar保持一直
            new News(data: '參數值'),    //上一篇定義好的子頁面
            new TabPage2(),
            new TabPage3(),
          ]
        ),
      bottomNavigationBar: new Material(    //為了適配主題風格垒探,包一層Material實現(xiàn)風格套用
        color: Colors.deepOrange,   //底部導航欄主題顏色
        child: new TabBar(        //TabBar導航標簽妓蛮,底部導航放到Scaffold的bottomNavigationBar中
          controller: _bottomNavigation,      //配置控制器
          tabs: _bottomTabs,
          indicatorColor: Colors.white, //tab標簽的下劃線顏色
        ),
      ) 
    );

無狀態(tài)控件搭配DefaultTabController

DefaultTabController要簡單很多,由于應用在無狀態(tài)控件中圾叼,使用DefaultTabController包裹需要用到Tab的頁面即可:

class TabPage3 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return  new DefaultTabController(
        length: 3,
        child: new Scaffold(
          appBar: new AppBar(
            backgroundColor: Colors.orangeAccent,
            title: new TabBar(
              tabs: [
                new Tab(icon: new Icon(Icons.directions_car)),
                new Tab(icon: new Icon(Icons.directions_transit)),
                new Tab(icon: new Icon(Icons.directions_bike)),
              ],
              indicatorColor: Colors.white,
            ),
          ),
          body: new TabBarView(
            children: [
              new Icon(Icons.directions_car),
              new Icon(Icons.directions_transit),
              new Icon(Icons.directions_bike),
            ],
          ),
        ),
      );
  }
}

DefaultTabControllerTabController的用法差異主要在控制器的定義上蛤克,TabBarTabBarView的使用方法完全相同,通常上下Tab頁的標簽分別安放在Scaffold控件的appBarbottomNavigationBar屬性上夷蚊,然后我們把APP填充成下面這個樣式:

效果圖

代碼結構

如上圖所示构挤,APP以底部Tab導航欄為主入口,底部每個Tab中又各自有自己的頂部次級Tab頁惕鼓,因此我們把代碼結構整理一下:

代碼框架
  • _HomePageState是APP的主頁面HomePage的子類State
  • 通過Scaffold底部的bottomNavigationBar屬性擺放TabBar筋现,搭建Tab頁的標簽欄
  • 放入Scaffoldbody屬性放入TabBarViewTabBarViewchildren是三個外部dart文件定義的控件頁面
  • 外部dart文件定義的控件頁面分別又有各自風格的Tab標簽頁
  • Tab頁的通用屬性可以提前定義到數組List中箱歧,在TabBarTabBarView通過遍歷提取List的值創(chuàng)建子元素可以提高代碼的維護效率:
//在`StatefulWidget`控件中矾飞,可通過修改下面的數組,實現(xiàn)Tab頁的動態(tài)加載
final List<Tab> myTabs = <Tab>[
    new Tab(text: 'Tab1'),
    new Tab(text: 'Tab2'),
    new Tab(text: 'Tab3'),
    new Tab(text: 'Tab4'),
    new Tab(text: 'Tab5'),
    new Tab(text: 'Tab6'),
    new Tab(text: 'Tab7'),
    new Tab(text: 'Tab8'),
    new Tab(text: 'Tab9'),
    new Tab(text: 'Tab10'),
    new Tab(text: 'Tab11'),
  ];

  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        backgroundColor: Colors.orangeAccent,
        title: new TabBar(
          controller: _tabController,
          tabs: myTabs,    //使用Tab類型的數組呈現(xiàn)Tab標簽
          indicatorColor: Colors.white,
          isScrollable: true,   
        ),
      ),
      body: new TabBarView(
        controller: _tabController,
        children: myTabs.map((Tab tab) {    //遍歷List<Tab>類型的對象myTabs并提取其屬性值作為子控件的內容
          return new Center(child: new Text(tab.text+'   '+widget.data)); //使用參數值
        }).toList(),
      ),
    );
  }

使用獲取到的參數

由于StatelessWidgetStatefulWidget的頁面構建不同呀邢,使用從外部獲取到的參數的方式也略有差異洒沦,在這里簡單總結下。

  • StatelessWidget的獲參和用參方式
    定義StatelessWidget控件時价淌,添加一個final類型的變量如pageText申眼,用于為參數值預留空間,并在構造函數中加入參數值:
class SidebarPage extends StatelessWidget {
  final String pageText;    //定義一個常量输钩,用于保存跳轉進來獲取到的參數
  SidebarPage(this.pageText);   //構造函數豺型,獲取參數
  ...
}

使用參數時直接引用即可:

Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(title: new Text(pageText),),   //將參數當作頁面標題
      body: new Center(
        child: new Text('pageText'),
      ),
    );
  }

從外部傳入參數時,直接向構造函數中填入參數值即可:

Navigator.of(context).push(new MaterialPageRoute(builder: 
    (BuildContext context) => new SidebarPage('First Page')));    //在new方法時調用控件的構造函數傳入參數值
  • StatefulWidget的獲參和用參方式
    相比StatelessWidget略復雜买乃。定義構造函數時需要默認聲明key
class TabPage1 extends StatefulWidget {
  const TabPage1({ Key key , this.data}) : super(key: key); //構造函數中增加參數
  final String data;    //為參數分配空間
  @override
  _MyTabbedPageState createState() => new _MyTabbedPageState();
}

使用時,由于在State子類中實現(xiàn)具體的頁面內容钓辆,因此State子類使用父類TabPage1的參數時需要在參數名前增加一個widget關鍵字:

class _MyTabbedPageState extends State<TabPage1> {
  ...
  new Center(child: new Text(tab.text+'   '+widget.data));   //使用參數值剪验,需在參數名前增加widget前綴
  ...
}

從外部傳入參數時肴焊,需要聲明參數名:

new TabBarView
    controller: _bottomNavigation,
    children:  [      
      new TabPage1(data: '參數值'),    //new方法調用構造函數時,還需要聲明參數名稱
      new TabPage2(),
      new TabPage3(),
    ]
  )

好勒功戚,今天就講到這里娶眷,大家去下載我的git源碼試試效果吧,代碼中有附加的注釋啸臀,對一些控件屬性的特性也有單獨的描述届宠,相信看完源碼之后,大家也可以自行實現(xiàn)效果了乘粒。

順便分享一個雷區(qū),由于當初創(chuàng)建這個項目時灯萍,我使用命令flutter create [APPname1]的方式創(chuàng)建了這個項目,但我發(fā)現(xiàn)這個APPname1(代稱齿风,并非真實名稱)不好聽,想把項目改名為APPname2绑洛,于是參考之前寫的安卓怎么打包救斑?,把項目文件夾改名為APPname2真屯,并非常任性的把項目目錄下的android\app\src\main\AndroidManifest.xml文件,把packageandroid:label都改成了APPname2讨跟,于是不出意料的悲催了纪他,命令flutter fun報錯,無法啟動APP晾匠,還原配置之后茶袒,無法啟動APP凉馆,即便嘗試通過全文搜索APPname1,都按規(guī)定格式替換成APPname2澜共,或者逆向改回去,都無法啟動APP母谎,此時已是凌晨1點京革。幸斥。咬扇。妥妥的血淚史,所以鄭重的告誡大家:

不要在項目的各種配置文件中輕易改動項目名稱懈贺!不要在項目的各種配置文件中輕易改動項目名稱!不要在項目的各種配置文件中輕易改動項目名稱画侣! 否則你就是下一個在電腦面前捶胸頓足的魚丸胎源。什么?問我怎么恢復的宪卿?當然是托git的福万栅。

感謝大家的支持,請關注我的Flutter圈子烦粒,多多投稿,也可以加入flutter 中文社區(qū)(官方QQ群:338252156)共同成長兽掰,謝謝大家~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末徒役,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子杉女,更是在濱河造成了極大的恐慌鸳吸,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件坎拐,死亡現(xiàn)場離奇詭異,居然都是意外死亡廉白,警方通過查閱死者的電腦和手機猴蹂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門楣嘁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人聋溜,你說我怎么就攤上這事叭爱。” “怎么了买雾?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵漓穿,是天一觀的道長。 經常有香客問我晃危,道長,這世上最難降的妖魔是什么震叮? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任鳍鸵,我火速辦了婚禮,結果婚禮上钓简,老公的妹妹穿的比我還像新娘汹想。我一直安慰自己,他們只是感情好损话,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著光涂,像睡著了一般拧烦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上恋博,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天债沮,我揣著相機與錄音,去河邊找鬼疫衩。 笑死,一個胖子當著我的面吹牛童芹,可吹牛的內容都是我干的曹傀。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼嗜价,長吁一口氣:“原來是場噩夢啊……” “哼幕庐!你這毒婦竟也來了?” 一聲冷哼從身側響起异剥,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤冤寿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后督怜,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡蚪腋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年屉凯,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片悠砚。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡哩簿,死狀恐怖酝静,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情别智,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布讳窟,位于F島的核電站敞恋,受9級特大地震影響,放射性物質發(fā)生泄漏补箍。R本人自食惡果不足惜啸蜜,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望衬横。 院中可真熱鬧,春花似錦遥诉、人聲如沸噪叙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至猫缭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間芝加,已是汗流浹背射窒。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蝌麸,地道東北人艾疟。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像弟疆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子怠苔,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,071評論 25 707
  • 發(fā)現(xiàn) 關注 消息 iOS 第三方庫柑司、插件乓诽、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,093評論 4 62
  • 前言: 不知不覺一年過去了鸠天,這一年過得很累,不確定的產品目標稠集,不確定的技術難度,當然還有不確定的人事變更剥纷,都讓我的...
    Abson在簡書閱讀 1,794評論 14 28
  • 世間安得雙全法,不負如來不負卿纱皆。 “我一直喜歡一個人芭商,所以抱歉了☆蹰梗” 每次遇見男生遞給我情書,我總是這樣回答钳踊,不敢...
    李舒煙閱讀 423評論 0 1
  • 眾生皆苦勿侯,哪有那么多天生就無憂無慮的缴罗。你不煩惱柴米油鹽,肯定要憂慮詩和遠方面氓;你無暇審視精神世界,因為開門幾件事掘譬,件...
    帝都三少510閱讀 1,115評論 0 1