Flutter學習之制作底部菜單導航


簡介

現在我們的 APP 上面都會在屏幕下方有一排的按鈕,點擊不同的按鈕可以進入不同的界面。就是說在界面的底部會有一排的按鈕導航。可看下面的圖示。

完成圖示 

程序工程目錄 

梳理下實現步驟
我們需要實現這個底部菜單導航,就需要有底部菜單的那一排圖標按鈕。圖標按鈕是固定在一個工具欄 “bar” 上面。然后呢,需要分別需要有按鈕對應的界面,就是說按鈕有多少個,那么界面需要對應的有多少個。我們來一個清單列表:

按鈕圖標區域。由於展示的方式都是一樣的,我們需要有一個單獨的控件,循環出來就好。
工具欄區域。用於展示按鈕圖標,並且能固定在底部。
首頁。用於將工具欄放入界面中,並且將按鈕對應的界面作為它的子元素存放於其中。
不同的按鈕對應的界面。在我們點擊的圖標按鈕的時候,展示不同的界面。
我們底部的按鈕是不會刷新的,界面會刷新,如何實現?

我們界面展示區域分為兩塊,一塊展示底部的工具欄,一塊展示頁面。下面代碼實現:

return new MaterialApp(
        home: new Scaffold(
          body: new Center(
              child: _currentPage   // 動態的展示我們當前的頁面
          ),
          bottomNavigationBar: bottomNavigationBar,   // 底部工具欄
        ),

      theme: new ThemeData(
        primarySwatch: Colors.blue,   // 設置主題顏色
      ),

    );

具體實現
第一步:創建一個 flutter 工程
可以按照工程目錄圖中的結構,將對應的文件建好。

第二步:修改 main.dart。
main.dart 是我們程序的入口。就類似於 Java、C 中的 main() ,作為一個程序的入口。我們將 main.dart 作為程序的啟動入口,就不做過多的操作,只是指定去加載我們的首頁(index.dart)。

main.dart

import 'package:flutter/material.dart';
import 'package:flutter_demo/index/index.dart';         // 導入index.dart

// 這里為入口函數
void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      home: new Index(),     // 指定去加載 Index頁面。
    );
  }
}

第三步:創建 NavigationIconView。 
正如前面說的,我們底部的按鈕區域展示的圖標加上文字是固定格式,所以將這一部分抽取出來,作為一個公共的 class,方便界面程序維護。

navigation_icon_view.dart

import 'package:flutter/material.dart';

// 創建一個 Icon 展示控件
class NavigationIconView {

  // 創建兩個屬性,一個是 用來展示 icon, 一個是動畫處理
  final BottomNavigationBarItem item;
  final AnimationController controller;

  // 類似於 java 中的構造方法
  // 創建 NavigationIconView 需要傳入三個參數, icon 圖標,title 標題, TickerProvider
  NavigationIconView({Widget icon, Widget title, TickerProvider vsync}):
    item = new BottomNavigationBarItem(
      icon: icon,
      title: title,
    ),
    controller = new AnimationController(
      duration: kThemeAnimationDuration,    // 設置動畫持續的時間
      vsync: vsync                          // 默認屬性和參數
    );
}

第四步:創建 Index 
這一步就比較重要了,因為我們需要在這個界面上面去布局,以及實現點擊按鈕圖標之后,有事件觸發。正因為我們需要有事件觸發,所以創建一個帶有狀態的 Widget(StatefulWidget)。下面的代碼注釋給的很詳細了,可以仔細看。

index.dart

import 'package:flutter/material.dart';
import 'package:flutter_demo/NoticePage/notice_page.dart';
import 'package:flutter_demo/home/home_page.dart';
import 'package:flutter_demo/idea/idea_page.dart';
import 'package:flutter_demo/market/market_page.dart';
import 'package:flutter_demo/my/my_page.dart';

import 'navigation_icon_view.dart'; // 如果是在同一個包的路徑下,可以直接使用對應的文件名

// 創建一個 帶有狀態的 Widget Index
class Index extends StatefulWidget {

  //  固定的寫法
  @override
  State<StatefulWidget> createState()  => new _IndexState();
}

// 要讓主頁面 Index 支持動效,要在它的定義中附加mixin類型的對象TickerProviderStateMixin
class _IndexState extends State<Index> with TickerProviderStateMixin{

  int _currentIndex = 0;    // 當前界面的索引值
  List<NavigationIconView> _navigationViews;  // 底部圖標按鈕區域
  List<StatefulWidget> _pageList;   // 用來存放我們的圖標對應的頁面
  StatefulWidget _currentPage;  // 當前的顯示頁面

  // 定義一個空的設置狀態值的方法
  void _rebuild() {
    setState((){});
  }

  @override
  void initState() {
    super.initState();

    // 初始化導航圖標
    _navigationViews = <NavigationIconView>[
      new NavigationIconView(icon: new Icon(Icons.assessment), title: new Text("首頁"), vsync: this), // vsync 默認屬性和參數
      new NavigationIconView(icon: new Icon(Icons.all_inclusive), title: new Text("想法"), vsync: this),
      new NavigationIconView(icon: new Icon(Icons.add_shopping_cart), title: new Text("市場"), vsync: this),
      new NavigationIconView(icon: new Icon(Icons.add_alert), title: new Text("通知"), vsync: this),
      new NavigationIconView(icon: new Icon(Icons.perm_identity), title: new Text("我的"), vsync: this),
    ];

    // 給每一個按鈕區域加上監聽
    for (NavigationIconView view in _navigationViews) {
      view.controller.addListener(_rebuild);
    }

    // 將我們 bottomBar 上面的按鈕圖標對應的頁面存放起來,方便我們在點擊的時候
    _pageList = <StatefulWidget>[
      new HomePage(),
      new IdeaPage(),
      new MarketPage(),
      new NoticePage(),
      new MyPage()
    ];
    _currentPage = _pageList[_currentIndex];
  }

  @override
  Widget build(BuildContext context) {

    // 聲明定義一個 底部導航的工具欄
    final BottomNavigationBar bottomNavigationBar = new BottomNavigationBar(
      items: _navigationViews
          .map((NavigationIconView navigationIconView) => navigationIconView.item)
          .toList(),  // 添加 icon 按鈕
      currentIndex: _currentIndex,  // 當前點擊的索引值
      type: BottomNavigationBarType.fixed,    // 設置底部導航工具欄的類型:fixed 固定
      onTap: (int index){   // 添加點擊事件
        setState((){    // 點擊之后,需要觸發的邏輯事件
          _navigationViews[_currentIndex].controller.reverse();
          _currentIndex = index;
          _navigationViews[_currentIndex].controller.forward();
          _currentPage = _pageList[_currentIndex];
        });
      },
    );

    return new MaterialApp(
        home: new Scaffold(
          body: new Center(
              child: _currentPage   // 動態的展示我們當前的頁面
          ),
          bottomNavigationBar: bottomNavigationBar,   // 底部工具欄
        ),

      theme: new ThemeData(
        primarySwatch: Colors.blue,   // 設置主題顏色
      ),

    );
  }

}

第四步:創建頁面 
前面的步驟都是在搭建我們的基礎,現在是做展示界面。由於不同的界面,對應的源碼都是和下面的是一樣的,只是 class 的名字不一樣,就都可以使用同樣的模版復制過去就有可以了。

home_page.dart

import 'package:flutter/material.dart';

class HomePage extends StatefulWidget{
  @override
  State<StatefulWidget> createState() => new _HomePageState();
}

class _HomePageState extends State<HomePage> {

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new Scaffold(
        appBar: new AppBar(
          title: new Text('首頁'),
          actions: <Widget>[
            new Container()
          ],
        ),
        body: new Center(
          child: null,
        ),
      ),
    );
  }
}




idea_page.dart
import 'package:flutter/material.dart';

class IdeaPage extends StatefulWidget{
  @override
  State<StatefulWidget> createState() => new _IdeaPageState();
}
class _IdeaPageState extends State<IdeaPage> {

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new Scaffold(
        appBar: new AppBar(
          title: new Text('想法'),
          actions: <Widget>[
            new Container()
          ],
        ),
        body: new Center(
          child: null,
        ),
      ),
    );
  }
}

market_page.dart

import 'package:flutter/material.dart';
class MarketPage extends StatefulWidget{
  @override
  State<StatefulWidget> createState() => new _MarketPageState();
}
class _MarketPageState extends State<MarketPage> {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new Scaffold(
        appBar: new AppBar(
          title: new Text('市場'),
          // 后面的省略
          // ......
        )
      ),
    );
  }

}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM