Flutter BottomNavigationBar結合TabBar使用

平常開發一個 app 時,首頁通常都會有個底部導航欄,那若是底部導航欄對應的頁面有頂部導航欄,那麼應該怎麼作呢?如下是本文 demo 的效果圖:android

tabbar

1、 BottomNavigationBar

先直接看下 BottomNavigationBar 的用法:bash

@override
Widget build(BuildContext context) {
  return Scaffold(
    bottomNavigationBar: BottomNavigationBar(
      items: [
        BottomNavigationBarItem(
          icon: Icon(Icons.android),
          title: Text('android'),
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.favorite),
          title: Text('favorite'),
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.phone),
          title: Text('phone'),
        ),
      ],
      onTap: (index){
        setState(() {
          this.selectedIndex = index;
        });
      },
      currentIndex: selectedIndex,
    ),
  );
}
複製代碼

Scaffold 中有個 bottomNavigationBar 屬性就是專門爲底部導航欄提供的,BottomNavigationBar 有幾個必須實現的屬性,首先 items ,你想要有幾個導航欄條目就放幾個 BottomNavigationBarItem 部件便可。你還須要實現 onTap 屬性,在點擊條目的時候改變選中的索引 selectedIndex,這個 selectedIndex 用於 currentIndex 屬性,同時也會定位每一個導航欄條目對應的頁面。從點擊事件中也能夠看出,底部導航欄所屬的頁面必須是 StatefulWidget,由於 selectedIndex 是可變的。app

完成底部導航欄的部件後,接下來須要建立每一個導航欄 item 對應的頁面了,這個其實直接在 Scaffold 中的 body 定義便可:less

final List<Widget> bottomBarViews = [
    FirstBarView(),
    SecondBarView(),
    ThirdBarView(),
  ];
  
@override
Widget build(BuildContext context) {
  return Scaffold(
    body: bottomBarViews[selectedIndex],
  );
}
複製代碼

bottomBarViews 裏是一系列你任意定義的頁面,數量和導航欄條目數量一致。ide

2、TabBar 和 TabBarView

頂部導航欄用 TabBar 實現,官方文檔和不少文章都會用下面這種方式來實現:佈局

@override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: DefaultTabController(
        length: choices.length,
        child: Scaffold(
          appBar: AppBar(
            title: const Text('Tabbed AppBar'),
            bottom: TabBar(
              isScrollable: true,
              tabs: choices.map((Choice choice) {
                return Tab(
                  text: choice.title,
                  icon: Icon(choice.icon),
                );
              }).toList(),
            ),
          ),
          body: TabBarView(
            children: choices.map((Choice choice) {
              return Padding(
                padding: const EdgeInsets.all(16.0),
                child: ChoiceCard(choice: choice),
              );
            }).toList(),
          ),
        ),
      ),
    );
  }
複製代碼

DefaultTabController 是 Flutter 提供的默認同步 TabBarTabBarView 狀態的部件,通常都會把 TabBar 寫到 AppBarbottom 屬性中,而後在 Scaffoldbody 屬性中放入 TabBarView。這兩個部件也是比較好理解,每一個頂部導航欄 item 對應一張頁面。可是這樣和底部導航欄就衝突了,兩個頁面列表都是放在 Scaffoldbody 屬性,那麼如何調整呢?ui

我在底部導航欄的第三個頁面 ThirdBarView 中加入頂部導航欄:this

class ThirdBarViewState extends State<ThirdBarView> with SingleTickerProviderStateMixin{

  List tabs = ["新聞", "歷史", "圖片"];
  TabController tabController;

  @override
  void initState() {
    super.initState();
    tabController = TabController(length: tabs.length, vsync: this);
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        TabBar(
          tabs: tabs.map((text) => Tab(text: text)).toList(),
          controller: tabController,
          labelColor: Colors.blue,
          indicatorColor: Colors.pink,
        ),
        Expanded(
          child: TabBarView(
            controller: tabController,
            children: tabs.map((text){
              return Center(
                child: Text(text),
              );
            }).toList(),
          ),
        ),
      ],
    );
  }
}
複製代碼

首先由於沒有使用 DefaultTabController ,咱們須要聲明一個 TabController 對象,能夠經過這個對象來控制 TabBarTabBarView 同步。而後就按照佈局的方式把這兩個部件放到 Column 中,TabBarView 外面須要包一層 Expanded 來佔據剩餘的空間。spa

3、 總結

最後貼出全部的代碼:code

import 'package:flutter/material.dart';

void main() => runApp(MaterialApp(
  title: 'tabbar',
  home: TableBarDemo(),
));

class TableBarDemo extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return TableBarState();
  }
}

class TableBarState extends State<TableBarDemo> {

  int selectedIndex = 0;

  final List<Widget> bottomBarViews = [
    FirstBarView(),
    SecondBarView(),
    ThirdBarView(),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('TabBar'),
      ),
      body: bottomBarViews[selectedIndex],
      bottomNavigationBar: BottomNavigationBar(
        items: [
          BottomNavigationBarItem(
            icon: Icon(Icons.android),
            title: Text('android'),
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.favorite),
            title: Text('favorite'),
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.phone),
            title: Text('phone'),
          ),
        ],
        onTap: (index){
          setState(() {
            this.selectedIndex = index;
          });
        },
        currentIndex: selectedIndex,
      ),
    );
  }

}

class FirstBarView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text('android'),
    );
  }
}

class SecondBarView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text('favorite'),
    );
  }
}

class ThirdBarView extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return ThirdBarViewState();
  }

}

class ThirdBarViewState extends State<ThirdBarView> with SingleTickerProviderStateMixin{

  List tabs = ["新聞", "歷史", "圖片"];
  TabController tabController;

  @override
  void initState() {
    super.initState();
    tabController = TabController(length: tabs.length, vsync: this);
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        TabBar(
          tabs: tabs.map((text) => Tab(text: text)).toList(),
          controller: tabController,
          labelColor: Colors.blue,
          indicatorColor: Colors.pink,
        ),
        Expanded(
          child: TabBarView(
            controller: tabController,
            children: tabs.map((text){
              return Center(
                child: Text(text),
              );
            }).toList(),
          ),
        ),
      ],
    );
  }
}
複製代碼
相關文章
相關標籤/搜索