flutter實戰2:爲APP增長上下Tab頁

緊接上一篇的有側邊欄APP,此次咱們向APP中加入上下Tab頁,使之跟趨近主流大部分APP的信息佈局套路,等不及看源碼的同窗能夠點擊進入個人git倉庫下載代碼。html

Tab關鍵元素

  • TabController 這是Tab頁的控制器,用於定義Tab標籤和內容頁的座標,還可配置標籤頁的切換動畫效果等。

TabController通常放入有狀態控件中使用,以適應標籤頁數量和內容有動態變化的場景,若是標籤頁在APP中是靜態固定的格局,則能夠在無狀態控件中加入簡易版的DefaultTabController以提升運行效率,畢竟無狀態控件要比有狀態控件更省資源,運行效率更快。android

  • TabBar Tab頁的Title控件,切換Tab頁的入口,通常放到AppBar控件下使用,內部有**Title*屬性。其子元素按水平橫向排列布局,若是須要縱向排列,請使用ColumnListView控件包裝一下。子元素爲Tab類型的數組。git

  • TabBarView Tab頁的內容容器,其內放置Tab頁的主體內容。子元素能夠是多個各類類型的控件。github

Tab使用方法

有狀態控件搭配TabController

Tab頁的切換搭配了動畫,所以到State類上附加一個SingleTickerProviderStateMixin:數組

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

  //用於使用到了一點點的動畫效果,所以加入了SingleTickerProviderStateMixin
  class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin{
    ...
  }
複製代碼

而後到有狀態控件的子類State中初始化控制器TabControllerapp

@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)),
          ]
  //標籤頁內容區域
  new TabBarView(
        children: [
          new Icon(Icons.directions_car),
          new Icon(Icons.directions_transit),
          new Icon(Icons.directions_bike),
        ]
複製代碼

最後,咱們把定義好的TabBarTabBarView安放到須要的地方去,好比:less

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

無狀態控件搭配DefaultTabController

DefaultTabController要簡單不少,因爲應用在無狀態控件中,使用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填充成下面這個樣式:ide

效果圖

代碼結構

如上圖所示,APP以底部Tab導航欄爲主入口,底部每一個Tab中又各自有本身的頂部次級Tab頁,所以咱們把代碼結構整理一下:

代碼框架

  • _HomePageState是APP的主頁面HomePage的子類State

  • 經過Scaffold底部的bottomNavigationBar屬性擺放TabBar,搭建Tab頁的標籤欄

  • 放入Scaffoldbody屬性放入TabBarViewTabBarViewchildren是三個外部dart文件定義的控件頁面

  • 外部dart文件定義的控件頁面分別又有各自風格的Tab標籤頁

  • Tab頁的通用屬性能夠提早定義到數組List中,在TabBarTabBarView經過遍歷提取List的值建立子元素能夠提升代碼的維護效率:

    //在StatefulWidget控件中,可經過修改下面的數組,實現Tab頁的動態加載 final List myTabs = [ 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類型的數組呈現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子類中實現具體的頁面內容,所以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源碼試試效果吧,代碼中有附加的註釋,對一些控件屬性的特性也有單獨的描述,相信看完源碼以後,你們也能夠自行實現效果了。

順便分享一個雷區,因爲當初建立這個項目時,我使用命令**flutter create [APPname1]的方式建立了這個項目,但我發現這個APPname1(代稱,並不是真實名稱)很差聽,想把項目更名爲APPname2,因而參考以前寫的安卓怎麼打包?,把項目文件夾更名爲APPname2,並不是常任性的把項目目錄下的_android\app\src\main\AndroidManifest.xml_文件,把packageandroid:label都改爲了APPname2,因而不出意料的悲催了,命令flutter fun**報錯,沒法啓動APP,還原配置以後,沒法啓動APP,即使嘗試經過全文搜索APPname1,都按規定格式替換成APPname2,或者逆向改回去,都沒法啓動APP,此時已經是凌晨1點。。。妥妥的血淚史,因此鄭重的告誡你們:

不要在項目的各類配置文件中輕易改動項目名稱!不要在項目的各類配置文件中輕易改動項目名稱!不要在項目的各類配置文件中輕易改動項目名稱! 不然你就是下一個在電腦面前捶胸頓足的魚丸。什麼?問我怎麼恢復的?固然是託git的福。

感謝你們的支持,請關注個人Flutter圈子,多多投稿,也能夠加入**flutter 中文社區(官方QQ羣:338252156)**共同成長,謝謝你們~

相關文章
相關標籤/搜索