【第四天 - 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),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

<<:  补充…小知识?

>>:  Day4-React Hook 篇-认识 useRef & useImperativeHandle

今晚来聊聊铼德2349

晚上在看线图的时候,发现铼德2349有几个观点可以提出来跟大家分享。 在今年1月初时,跌破季线,这时...

day30_arm 还是 x86? 我知道该怎麽选了

ARM 与 x86 谁更对你胃口呢? 我们在之前的文章内讨论了很多 ARM 与 x86 的差异,互相...

你好 想请问css轮播问题

#photos{position:absolute;width:calc(600px*6);z-i...

[第二十五只羊] 迷雾森林舞会XIV 房间介面调整

天亮了 昨晚是平安夜 关於迷雾森林故事 旋木 随着蓝调漫节奏 大家跟着音浪一起流动 阿虎也拿着麦克风...

Day 6. 资料库篇

後端工程师最必须要会的其中一个技能,就是对资料库的操作,我相信很少有後端工程师可以完全不用学到对资料...