iT邦幫忙

2021 iThome 鐵人賽

DAY 4
0
Mobile Development

Flutter - 複製貼上到開發套件之旅系列 第 4

【第四天 - Flutter BottomNavigationBar(上)Animation】

前言

一般來說,寫 BottomNavigationBar 會使用這個方法,官方文件,這個是官方的範例。

今天我想要介紹,如何從簡單的變成有動畫的感覺。
今日的程式碼 => GITHUB

講講大致上的邏輯

製作一個 Widget,然後裡面放一個 RowRow 裡面放一個可以點擊的 AnimatedContainer,然後設定 icon 的圖示和動畫。這個 Widget 會有一個初始的 pageIndexBottomNavigator 也會有一個 pageIndex,這兩個的 index 要設為一樣。在這個自定義的 Widget 裡面,會有一個 callBack 可以讓 BottomNavigator 拿到這個自定義 Widget 裡面被點擊的 index

今天會用到的物件

這邊我是把它建立成 2 個物件,有興趣的話(可以自行嘗試變成一個物件)

  • BarItem 的 Style
  • BarItem 的 資料
class BarStyle {
  final double fontSize, iconSize;
  final FontWeight fontWeight;
  
  BarStyle(
      {this.fontSize = 18.0,
      this.iconSize = 32.0,
      this.fontWeight = FontWeight.w600});
}

class BarItemData {
  final String text;
  final IconData iconData;
  final Color color;

  BarItemData(this.text, this.iconData, this.color);
}

MyHomePage

控制 bottomNavigationBar 的頁面

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  /// the index of pageItem
  /// 畫面的 index
  int pageIndex = 0;
  /// 宣告一個 List,等等要放對應的 Page 進去
  List<Widget> pageItem = [];

  @override
  void initState() {
    super.initState();
    /// 每一頁對應的 Page 是什麼
    pageItem = [HomePage(), NotifyScreen(), UserProfile(), SettingApp()];
  }

  /// Object<BarItem> of List
  /// List 裡面包一個 自定義的物件 BarItem
  final List<BarItemData> barItems = [
    BarItemData("Home", Icons.home, Color(0xFF498AEF)),
    BarItemData("Notify", Icons.notifications_active, Colors.red),
    BarItemData("Profile", Icons.person_outline, Colors.teal),
    BarItemData("Setting", Icons.menu, Colors.purple)
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('BottomNavigationBar Sample'),
      ),
      body: IndexedStack(
        index: pageIndex,
        children: pageItem,
      ),
      bottomNavigationBar: AnimationBottomBar(
        barItemsData: barItems,
        // animationDuration: const Duration(milliseconds: 500), 非必填
        // curves: Curves.easeInOut, 非必填
        barStyle: BarStyle(
            fointSize: 20.0, fontWeight: FontWeight.w400, iconSize: 30.0),
        changePageIndex: (int index) {
          /// when bottomBar on Tap will return the bottomBar index
          /// 會回傳被點擊到的 bottomBar index
          setState(() {
            pageIndex = index;
          });
        },
      ),
    );
  }
}

AnimationBattomBar

自定義的客製化 BottomBar 的 Item

class AnimationBottomBar extends StatefulWidget {
  /// get data of List<BarItem>
  /// 取得 BarItem 的資料
  final List<BarItemData> _barItemsData;

  /// get animation duration time
  /// 取得 animation 的延遲時間
  final Duration _animationDuration;

  /// get data of List<BarItem>
  /// 取得 BarItem 的資料
  final BarStyle _barStyle;

  /// callBack will return the index of the bottombar that was clicked
  /// callBack 回傳被點擊到的 index
  final void Function(int index) _changePageIndex;

  /// animation
  /// 動畫的效果
  final Curve _curves;

  const AnimationBottomBar(
      {Key? key,
      required List<BarItemData> barItemsData,
      Duration animationDuration = const Duration(milliseconds: 500),
      Curve curves = Curves.easeInOut,
      required BarStyle barStyle,
      required Function(int index) changePageIndex})
      : _barItemsData = barItemsData,
        _animationDuration = animationDuration,
        _barStyle = barStyle,
        _curves = curves,
        _changePageIndex = changePageIndex,
        super(key: key);

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

class _AnimationBottomBarState extends State<AnimationBottomBar>
    with TickerProviderStateMixin {
  /// init selectedBarIndex = 0;
  /// 初始化 selectedBarIndex = 0;
  int selectedBarIndex = 0;

  @override
  Widget build(BuildContext context) {
    return Material(
      elevation: 10.0,
      child: Padding(
        padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16),
        child: Row(
            mainAxisSize: MainAxisSize.max,
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: _buildBarItems()),
      ),
    );
  }

  /// save the single data of barItemsData into the List
  /// 將 barItemsData 的單筆資料存入 List 裡面
  List<Widget> _buildBarItems() {
    /// init _barItem List
    List<Widget> _barItems = [];
    for (int i = 0; i < widget._barItemsData.length; i++) {
      BarItemData item = widget._barItemsData[i];
      bool isSelected = selectedBarIndex == i;
      _barItems.add(_customBarItem(i, isSelected, item));
    }
    return _barItems;
  }

  /// build CustomBarItem in BottomNavigatorBar
  /// 建立客製化的 BarItem 樣式
  InkWell _customBarItem(int i, bool isSelected, BarItemData item) {
    return InkWell(
      splashColor: Colors.transparent,
      onTap: () {
        setState(() {
          selectedBarIndex = i;
          widget._changePageIndex(selectedBarIndex);
        });
      },
      child: AnimatedContainer(
        padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
        duration: widget._animationDuration,
        decoration: BoxDecoration(
            color:
                isSelected ? item.color.withOpacity(0.15) : Colors.transparent,
            borderRadius: BorderRadius.all(Radius.circular(30.0))),
        child: Row(
          children: <Widget>[
            Icon(
              item.iconData,
              color: isSelected ? item.color : Colors.black,
              size: widget._barStyle.iconSize,
            ),
            SizedBox(width: 10.0),
            AnimatedSize(
              duration: widget._animationDuration,
              curve: widget._curves,
              vsync: this,
              child: Text(
                isSelected ? item.text : "",
                style: TextStyle(
                    color: item.color,
                    fontWeight: widget._barStyle.fontWeight,
                    fontSize: widget._barStyle.fontSize),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

上一篇
【第三天 - Flutter Route 規劃分享】
下一篇
【第五天 - Fluter BottomNavigationBar(下)行為分析】
系列文
Flutter - 複製貼上到開發套件之旅30

1 則留言

我要留言

立即登入留言