1. 程式人生 > 其它 >Flutter常用元件總結-底部導航欄BottomNavigationBar元件

Flutter常用元件總結-底部導航欄BottomNavigationBar元件

技術標籤:Flutter

示例頁面目錄如下:

在入口檔案main.dart中使用我們定義的widget元件類BottomNavigationWidget():

import 'package:flutter/material.dart';
import './mywidget/bottom_navigation_widget.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
   @override
  Widget build(BuildContext context) {
    return Container(
      child: MaterialApp(
        title: '優品酒家',
        debugShowCheckedModeBanner: false,
        theme: ThemeData(
          primaryColor: Colors.pink
        ),
        home: BottomNavigationWidget(),
      ),
    );
  }
}


下面是與底部導航欄4個導航按鈕一一對應的頁面的home_page.dart、picture_page.dart、friend_page.dart、share_page.dart,這裡只給出前兩個頁面,後兩個頁面的內容與第二個頁面的內容一樣,只是文字顯示改了一下,不再給出:

home_page.dart:

import 'package:flutter/material.dart';
import '../pages/goods_detail.dart';

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title:Text('優品酒家')),
      body: new Center(
        child: RaisedButton(
          child: new Text("檢視商品詳情"),
          onPressed: (){
            Navigator.push(context, new MaterialPageRoute(builder: (context)=>new GoodsDetail()));
          },
        ),
      )
    );
  }
}

picture_page.dart:

import 'package:flutter/material.dart';

class PicturePage extends StatefulWidget {
  @override
  _PicturePageState createState() => _PicturePageState();
}

class _PicturePageState extends State<PicturePage> {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Center(
        child: Text("圖片頁"),
      ),
    );
  }
}

下面就是我們重點的內容BottomNavigationBar元件,同樣先通過原始碼定義類來認識:

class BottomNavigationBar extends StatefulWidget {
  /// Creates a bottom navigation bar which is typically used as a
  /// [Scaffold]'s [Scaffold.bottomNavigationBar] argument.
  ///
  /// The length of [items] must be at least two and each item's icon and title
  /// must not be null.
  ///
  /// If [type] is null then [BottomNavigationBarType.fixed] is used when there
  /// are two or three [items], [BottomNavigationBarType.shifting] otherwise.
  ///
  /// The [iconSize], [selectedFontSize], [unselectedFontSize], and [elevation]
  /// arguments must be non-null and non-negative.
  ///
  /// If [selectedLabelStyle.color] and [unselectedLabelStyle.color] values
  /// are non-null, they will be used instead of [selectedItemColor] and
  /// [unselectedItemColor].
  ///
  /// If custom [IconThemData]s are used, you must provide both
  /// [selectedIconTheme] and [unselectedIconTheme], and both
  /// [IconThemeData.color] and [IconThemeData.size] must be set.
  ///
  /// If both [selectedLabelStyle.fontSize] and [selectedFontSize] are set,
  /// [selectedLabelStyle.fontSize] will be used.
  ///
  /// Only one of [selectedItemColor] and [fixedColor] can be specified. The
  /// former is preferred, [fixedColor] only exists for the sake of
  /// backwards compatibility.
  ///
  /// The [showSelectedLabels] argument must be non-null.
  ///
  /// The [showUnselectedLabels] argument defaults to `true` if [type] is
  /// [BottomNavigationBarType.fixed] and `false` if [type] is
  /// [BottomNavigationBarType.shifting].
  BottomNavigationBar({
    Key key,
    @required this.items,
    this.onTap,
    this.currentIndex = 0,
    this.elevation,
    this.type,
    Color fixedColor,
    this.backgroundColor,
    this.iconSize = 24.0,
    Color selectedItemColor,
    this.unselectedItemColor,
    this.selectedIconTheme,
    this.unselectedIconTheme,
    this.selectedFontSize = 14.0,
    this.unselectedFontSize = 12.0,
    this.selectedLabelStyle,
    this.unselectedLabelStyle,
    this.showSelectedLabels = true,
    this.showUnselectedLabels,
    this.mouseCursor,
  }) : assert(items != null),
       assert(items.length >= 2),
       assert(
        items.every((BottomNavigationBarItem item) => item.title != null) == true,
        'Every item must have a non-null title',
       ),
       assert(0 <= currentIndex && currentIndex < items.length),
       assert(elevation == null || elevation >= 0.0),
       assert(iconSize != null && iconSize >= 0.0),
       assert(
         selectedItemColor == null || fixedColor == null,
         'Either selectedItemColor or fixedColor can be specified, but not both'
       ),
       assert(selectedFontSize != null && selectedFontSize >= 0.0),
       assert(unselectedFontSize != null && unselectedFontSize >= 0.0),
       assert(showSelectedLabels != null),
       selectedItemColor = selectedItemColor ?? fixedColor,
       super(key: key);

  /// Defines the appearance of the button items that are arrayed within the
  /// bottom navigation bar.
  final List<BottomNavigationBarItem> items;

  /// Called when one of the [items] is tapped.
  ///
  /// The stateful widget that creates the bottom navigation bar needs to keep
  /// track of the index of the selected [BottomNavigationBarItem] and call
  /// `setState` to rebuild the bottom navigation bar with the new [currentIndex].
  final ValueChanged<int> onTap;

  /// The index into [items] for the current active [BottomNavigationBarItem].
  final int currentIndex;

  /// The z-coordinate of this [BottomNavigationBar].
  ///
  /// If null, defaults to `8.0`.
  ///
  /// {@macro flutter.material.material.elevation}
  final double elevation;

  /// Defines the layout and behavior of a [BottomNavigationBar].
  ///
  /// See documentation for [BottomNavigationBarType] for information on the
  /// meaning of different types.
  final BottomNavigationBarType type;

  /// The value of [selectedItemColor].
  ///
  /// This getter only exists for backwards compatibility, the
  /// [selectedItemColor] property is preferred.
  Color get fixedColor => selectedItemColor;

  /// The color of the [BottomNavigationBar] itself.
  ///
  /// If [type] is [BottomNavigationBarType.shifting] and the
  /// [items]s, have [BottomNavigationBarItem.backgroundColor] set, the [item]'s
  /// backgroundColor will splash and overwrite this color.
  final Color backgroundColor;

  /// The size of all of the [BottomNavigationBarItem] icons.
  ///
  /// See [BottomNavigationBarItem.icon] for more information.
  final double iconSize;

  /// The color of the selected [BottomNavigationBarItem.icon] and
  /// [BottomNavigationBarItem.label].
  ///
  /// If null then the [ThemeData.primaryColor] is used.
  final Color selectedItemColor;

  /// The color of the unselected [BottomNavigationBarItem.icon] and
  /// [BottomNavigationBarItem.label]s.
  ///
  /// If null then the [TextTheme.caption]'s color is used.
  final Color unselectedItemColor;

  /// The size, opacity, and color of the icon in the currently selected
  /// [BottomNavigationBarItem.icon].
  ///
  /// If this is not provided, the size will default to [iconSize], the color
  /// will default to [selectedItemColor].
  ///
  /// It this field is provided, it must contain non-null [IconThemeData.size]
  /// and [IconThemeData.color] properties. Also, if this field is supplied,
  /// [unselectedIconTheme] must be provided.
  final IconThemeData selectedIconTheme;

  /// The size, opacity, and color of the icon in the currently unselected
  /// [BottomNavigationBarItem.icon]s
  ///
  /// If this is not provided, the size will default to [iconSize], the color
  /// will default to [unselectedItemColor].
  ///
  /// It this field is provided, it must contain non-null [IconThemeData.size]
  /// and [IconThemeData.color] properties. Also, if this field is supplied,
  /// [unselectedIconTheme] must be provided.
  final IconThemeData unselectedIconTheme;

  /// The [TextStyle] of the [BottomNavigationBarItem] labels when they are
  /// selected.
  final TextStyle selectedLabelStyle;

  /// The [TextStyle] of the [BottomNavigationBarItem] labels when they are not
  /// selected.
  final TextStyle unselectedLabelStyle;

  /// The font size of the [BottomNavigationBarItem] labels when they are selected.
  ///
  /// If [selectedLabelStyle.fontSize] is non-null, it will be used instead of this.
  ///
  /// Defaults to `14.0`.
  final double selectedFontSize;

  /// The font size of the [BottomNavigationBarItem] labels when they are not
  /// selected.
  ///
  /// If [unselectedLabelStyle.fontSize] is non-null, it will be used instead of this.
  ///
  /// Defaults to `12.0`.
  final double unselectedFontSize;

  /// Whether the labels are shown for the selected [BottomNavigationBarItem].
  final bool showUnselectedLabels;

  /// Whether the labels are shown for the unselected [BottomNavigationBarItem]s.
  final bool showSelectedLabels;

  /// The cursor for a mouse pointer when it enters or is hovering over the
  /// tiles.
  ///
  /// If this property is null, [SystemMouseCursors.click] will be used.
  final MouseCursor mouseCursor;

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

可以看出它定義了一些選中和未選中時的顏色、大小、樣式、主題,以及底部導航欄的背景顏色、點選事件,最重要的就是一個必須的導航item陣列,這個陣列的大小必須大於2,底部導航欄的顯示才可以顯示生效。

BottomNavigationBar元件裡提供了一個相應事件onTap,這個事件自帶一個索引值index,通過索引值就可以和相應的頁面實現聯動切換。

下面會給出一個示例,在給出示例時,這裡需要明確一下有狀態元件StatefulWidget的個幾個概念:

(一)、Flutter元件分為有狀態和無狀態元件,專案開發常用的是有狀態元件。自定義的有狀態元件需要實現狀態控制器類後,再重寫 createState()方法。

(二)、Flutter開發的主要邏輯程式碼都會寫在狀態控制器類中,宣告變數、重寫方法、業務邏輯等。狀態控制器類的命名規則:

①類名前加字首"_" ②類名後加字尾State ③繼承State類,泛型引數中傳入類名。

下面給出在入口檔案中呼叫的BottomNavigationWidget(),底部導航欄示例:

import 'package:flutter/material.dart';
import 'package:flutter_app/pages/home_page.dart';
import 'package:flutter_app/pages/friend_page.dart';
import 'package:flutter_app/pages/share_page.dart';
import 'package:flutter_app/pages/picture_page.dart';

/// Flutter元件分為有狀態和無狀態元件,專案開發常用的是有狀態元件
/// 自定義的有狀態元件需要實現狀態控制器類後,重寫 createState()方法
class BottomNavigationWidget extends StatefulWidget {
  @override
  _BottomNavigationWidgetState createState() => _BottomNavigationWidgetState();
}

/// 主要的邏輯程式碼會寫在下面的狀態控制器類中
/// 狀態控制器類的命名規則:①類名前加字首"_" ②類名後加字尾State ③繼承State類,泛型引數中傳入類名
class _BottomNavigationWidgetState extends State<BottomNavigationWidget> {

  final _bottomNavigationColor = Colors.pink;
  int _currentIndex=0;
  List<Widget> list=List();

  @override
  void initState() {
    //使用構建者模式的鏈式呼叫,".."是Dart的語法,返回還是list物件
    list..add(HomePage())..add(PicturePage())..add(FriendPage())..add(SharePage());
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: list[_currentIndex],
      bottomNavigationBar: BottomNavigationBar(
        items: [
          BottomNavigationBarItem(
              icon: Icon(Icons.home,color: _bottomNavigationColor,),
              title:Text('首頁',style: TextStyle(color: _bottomNavigationColor)),),
          BottomNavigationBarItem(
              icon: Icon(Icons.email,color: _bottomNavigationColor,),
              title:Text('頻道',style: TextStyle(color: _bottomNavigationColor),)),
          BottomNavigationBarItem(icon: Icon(Icons.child_friendly,color: _bottomNavigationColor,),
              title:Text('朋友圈',style: TextStyle(color: _bottomNavigationColor),)),
          BottomNavigationBarItem(icon: Icon(Icons.share,color: _bottomNavigationColor,),
              title:Text('分享',style: TextStyle(color: _bottomNavigationColor),))
        ],
        /// BottomNavigationBar元件的點選事件onTap,事件自帶索引值index
        /// 通過索引值index實現和list數組裡面的頁面例項的索引值一一對應,實現切換聯動
        onTap:(int index){
          setState(() {
            _currentIndex=index;
          });
        },
        ///讓底部導航欄根據底部導航欄的item項的寬度設定固定寬度值,設定後底部的導航文字才能全部顯示
        type:BottomNavigationBarType.fixed
      ),
    );
  }
}

注意:這裡重寫initState()方法,把剛才做好的頁面進行初始化到一個Widget陣列中。有了陣列就可以根據陣列的索引來切換不同的頁面了。這是現在幾乎所有的APP採用的方式。

程式碼如下:

 List<Widget> list = List();
 @override
 void initState(){
    list..add(HomeScreen())..add(EmailScreen())..add(PagesScreen())..add(AirplayScreen());
    super.initState();
  }

這裡的..add()是Dart語言的..語法,如果你學過程式設計模式,你一定聽說過建造者模式,簡單來說就是返回呼叫者本身。這裡list後用了..add(),還會返回list,這樣呼叫下去就可以一直向list裡增加widget元素。 最後我們呼叫了一些父類的initState()方法。

頁面效果如下,其他頁面省略:

下一篇繼續總結!