Flutter 中自定义动画底部导航栏

2021/07/10 08:00
阅读数 293


在这个博客中,我们将探索Flutter中的自定义动画底部导航栏。我们将看到如何实现自定义动画底部导航栏的演示程序以及如何在您的 Flutter 应用程序中使用它。

介绍:

显示在应用程序底部的Material小部件,用于在几个视角中进行选择,通常在 3 到 5 范围内的某个位置。底部导航栏包含各种选项,如文本标签、图标或两者。它提供了应用程序的高级视角之间的快速导航。对于更大的屏幕,侧面导航可能更合适。

这个演示视频展示了如何在 flutter 中使用自定义的底部导航栏。它展示了自定义底部导航栏将如何在您的 Flutter 应用程序中工作。它显示当用户点击底部导航栏图标时,它们将被动画化并显示标签文本。当用户点击任何图标时,颜色也会发生变化和动画。它将显示在您的设备上。

特性

自定义动画底部导航栏的一些属性是:

  • selectedIndex:这个属性用于被选中的项是一个索引。更改此属性将更改所选项目并为其设置动画。默认为零。
  • backgroundColor:该属性用于导航栏的背景颜色。如果未提供,则默认为 Theme.bottomAppBarColor。
  • showElevation:此属性用于此导航栏是否应显示高程。默认为真。
  • List items :该属性用于定义底部导航栏中显示的按钮的外观。这应该至少有两个项目,最多五个。
  • onItemSelected:该属性用于在按下项目时调用的回调。
  • **curve:**该属性用于配置动画曲线。
  • itemCornerRadius:该属性用于物品的角半径,如果不设置,默认为50。

如何在 dart 文件中实现代码

创建一个新的 dart 文件*my_home_page.dart*

在构建方法中,我们将返回一个 scaffold()。在里面我们将添加一个 appBar。在 appBar 中,我们将添加 title 和 backgroundColor。我们将添加 body 并添加到**getBody()小部件中。下面我们将深入定义代码。现在,我们将添加 bottomNavigationBar 并将其添加到_buildBottomBar()**小部件中。我们还将深入定义下面的代码。

return Scaffold(
    appBar: AppBar(
      automaticallyImplyLeading: false,
      title: Text("Custom Animated Bottom Navigation Bar"),
      backgroundColor: Colors.green[200],
    ),
    body: getBody(),
    bottomNavigationBar: _buildBottomBar()
);

我们将深入定义**getBody()**小部件

首先,我们将创建一个整数变量 _currentIndex 等于零。

int_currentIndex = 0;

我们将创建 getBody() 小部件。在这个小部件中,我们将添加 List 页面。我们将添加四个具有不同文本的容器并返回**IndexedStack()**小部件。在小部件内部,我们将添加索引是我的变量 _currentIndex 和 children 是列表小部件页面。

Widget getBody() {
  List<Widget> pages = [
    Container(
      alignment: Alignment.center,
      child: Text("Home",style: TextStyle(fontSize: 25,fontWeight: FontWeight.bold),),
    ),
    Container(
      alignment: Alignment.center,
      child: Text("Users",style: TextStyle(fontSize: 25,fontWeight: FontWeight.bold),),
    ),
    Container(
      alignment: Alignment.center,
      child: Text("Messages",style: TextStyle(fontSize: 25,fontWeight: FontWeight.bold),),
    ),
    Container(
      alignment: Alignment.center,
      child: Text("Settings",style: TextStyle(fontSize: 25,fontWeight: FontWeight.bold),),
    ),
  ];
  return IndexedStack(
    index: _currentIndex,
    children: pages,
  );
}

我们将创建一个 _buildBottomBar() 小部件。在这个小部件中,我们将返回一个CustomAnimatedBottomBar()。在里面,我们将添加一个容器高度、backgroundColor、selectedIndex、变量_currentIndex、showElevation、动画曲线、onItemSelected和items。在 items 中, e 将添加四个BottomNavyBarItem()。在里面,我们将添加四个不同的图标、标题、activeColors,并且所有的 text-align 都应该居中。

Widget _buildBottomBar(){
  return CustomAnimatedBottomBar(
    containerHeight: 70,
    backgroundColor: Colors.black,
    selectedIndex: _currentIndex,
    showElevation: true,
    itemCornerRadius: 24,
    curve: Curves.easeIn,
    onItemSelected: (index) => setState(() => _currentIndex = index),
    items: <BottomNavyBarItem>[
      BottomNavyBarItem(
        icon: Icon(Icons.apps),
        title: Text('Home'),
        activeColor: Colors.green,
        inactiveColor: _inactiveColor,
        textAlign: TextAlign.center,
      ),
      BottomNavyBarItem(
        icon: Icon(Icons.people),
        title: Text('Users'),
        activeColor: Colors.purpleAccent,
        inactiveColor: _inactiveColor,
        textAlign: TextAlign.center,
      ),
      BottomNavyBarItem(
        icon: Icon(Icons.message),
        title: Text(
          'Messages ',
        ),
        activeColor: Colors.pink,
        inactiveColor: _inactiveColor,
        textAlign: TextAlign.center,
      ),
      BottomNavyBarItem(
        icon: Icon(Icons.settings),
        title: Text('Settings'),
        activeColor: Colors.blue,
        inactiveColor: _inactiveColor,
        textAlign: TextAlign.center,
      ),
    ],
  );
}

创建custom_animated_bottom_bar.dart文件

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

class CustomAnimatedBottomBar extends StatelessWidget {

  CustomAnimatedBottomBar({
    Key? key,
    this.selectedIndex = 0,
    this.showElevation = true,
    this.iconSize = 24,
    this.backgroundColor,
    this.itemCornerRadius = 50,
    this.containerHeight = 56,
    this.animationDuration = const Duration(milliseconds: 270),
    this.mainAxisAlignment = MainAxisAlignment.spaceBetween,
    required this.items,
    required this.onItemSelected,
    this.curve = Curves.linear,
  }) : assert(items.length >= 2 && items.length <= 5),
        super(key: key);
  
  final int selectedIndex;
  final double iconSize;
  final Color? backgroundColor;
  final bool showElevation;
  final Duration animationDuration;
  final List<BottomNavyBarItem> items;
  final ValueChanged<int> onItemSelected;
  final MainAxisAlignment mainAxisAlignment;
  final double itemCornerRadius;
  final double containerHeight;
  final Curve curve;

  @override
  Widget build(BuildContext context) {
    final bgColor = backgroundColor ?? Theme.of(context).bottomAppBarColor;

    return Container(
      decoration: BoxDecoration(
        color: bgColor,
        boxShadow: [
          if (showElevation)
            const BoxShadow(
              color: Colors.black12,
              blurRadius: 2,
            ),
        ],
      ),
      child: SafeArea(
        child: Container(
          width: double.infinity,
          height: containerHeight,
          padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 8),
          child: Row(
            mainAxisAlignment: mainAxisAlignment,
            children: items.map((item) {
              var index = items.indexOf(item);
              return GestureDetector(
                onTap: () => onItemSelected(index),
                child: _ItemWidget(
                  item: item,
                  iconSize: iconSize,
                  isSelected: index == selectedIndex,
                  backgroundColor: bgColor,
                  itemCornerRadius: itemCornerRadius,
                  animationDuration: animationDuration,
                  curve: curve,
                ),
              );
            }).toList(),
          ),
        ),
      ),
    );
  }
}

class _ItemWidget extends StatelessWidget {
  final double iconSize;
  final bool isSelected;
  final BottomNavyBarItem item;
  final Color backgroundColor;
  final double itemCornerRadius;
  final Duration animationDuration;
  final Curve curve;

  const _ItemWidget({
    Key? key,
    required this.item,
    required this.isSelected,
    required this.backgroundColor,
    required this.animationDuration,
    required this.itemCornerRadius,
    required this.iconSize,
    this.curve = Curves.linear,
  })  : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Semantics(
      container: true,
      selected: isSelected,
      child: AnimatedContainer(
        width: isSelected ? 130 : 50,
        height: double.maxFinite,
        duration: animationDuration,
        curve: curve,
        decoration: BoxDecoration(
          color:
          isSelected ? item.activeColor.withOpacity(0.2) : backgroundColor,
          borderRadius: BorderRadius.circular(itemCornerRadius),
        ),
        child: SingleChildScrollView(
          scrollDirection: Axis.horizontal,
          physics: NeverScrollableScrollPhysics(),
          child: Container(
            width: isSelected ? 130 : 50,
            padding: EdgeInsets.symmetric(horizontal: 8),
            child: Row(
              mainAxisSize: MainAxisSize.max,
              mainAxisAlignment: MainAxisAlignment.start,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: <Widget>[
                IconTheme(
                  data: IconThemeData(
                    size: iconSize,
                    color: isSelected
                        ? item.activeColor.withOpacity(1)
                        : item.inactiveColor == null
                        ? item.activeColor
                        : item.inactiveColor,
                  ),
                  child: item.icon,
                ),
                if (isSelected)
                  Expanded(
                    child: Container(
                      padding: EdgeInsets.symmetric(horizontal: 4),
                      child: DefaultTextStyle.merge(
                        style: TextStyle(
                          color: item.activeColor,
                          fontWeight: FontWeight.bold,
                        ),
                        maxLines: 1,
                        textAlign: item.textAlign,
                        child: item.title,
                      ),
                    ),
                  ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}
class BottomNavyBarItem {

  BottomNavyBarItem({
    required this.icon,
    required this.title,
    this.activeColor = Colors.blue,
    this.textAlign,
    this.inactiveColor,
  });

  final Widget icon;
  final Widget title;
  final Color activeColor;
  final Color? inactiveColor;
  final TextAlign? textAlign;

}

代码文件

import 'package:flutter/material.dart';
import 'package:flutter_animated_bottom/custom_animated_bottom_bar.dart';

class MyHomePage extends StatefulWidget {

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

class _MyHomePageState extends State<MyHomePage{
  int _currentIndex = 0;

  final _inactiveColor = Colors.grey;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          automaticallyImplyLeading: false,
          title: Text("Custom Animated Bottom Navigation Bar"),
          backgroundColor: Colors.green[200],
        ),
        body: getBody(),
        bottomNavigationBar: _buildBottomBar()
    );
  }

  Widget _buildBottomBar(){
    return CustomAnimatedBottomBar(
      containerHeight: 70,
      backgroundColor: Colors.black,
      selectedIndex: _currentIndex,
      showElevation: true,
      itemCornerRadius: 24,
      curve: Curves.easeIn,
      onItemSelected: (index) => setState(() => _currentIndex = index),
      items: <BottomNavyBarItem>[
        BottomNavyBarItem(
          icon: Icon(Icons.apps),
          title: Text('Home'),
          activeColor: Colors.green,
          inactiveColor: _inactiveColor,
          textAlign: TextAlign.center,
        ),
        BottomNavyBarItem(
          icon: Icon(Icons.people),
          title: Text('Users'),
          activeColor: Colors.purpleAccent,
          inactiveColor: _inactiveColor,
          textAlign: TextAlign.center,
        ),
        BottomNavyBarItem(
          icon: Icon(Icons.message),
          title: Text(
            'Messages ',
          ),
          activeColor: Colors.pink,
          inactiveColor: _inactiveColor,
          textAlign: TextAlign.center,
        ),
        BottomNavyBarItem(
          icon: Icon(Icons.settings),
          title: Text('Settings'),
          activeColor: Colors.blue,
          inactiveColor: _inactiveColor,
          textAlign: TextAlign.center,
        ),
      ],
    );
  }


  Widget getBody() {
    List<Widget> pages = [
      Container(
        alignment: Alignment.center,
        child: Text("Home",style: TextStyle(fontSize: 25,fontWeight: FontWeight.bold),),
      ),
      Container(
        alignment: Alignment.center,
        child: Text("Users",style: TextStyle(fontSize: 25,fontWeight: FontWeight.bold),),
      ),
      Container(
        alignment: Alignment.center,
        child: Text("Messages",style: TextStyle(fontSize: 25,fontWeight: FontWeight.bold),),
      ),
      Container(
        alignment: Alignment.center,
        child: Text("Settings",style: TextStyle(fontSize: 25,fontWeight: FontWeight.bold),),
      ),
    ];
    return IndexedStack(
      index: _currentIndex,
      children: pages,
    );
  }


}

结论:

在文章中,我已经 在flutter中讲解了自定义动画BottomNavigation Bar的基本结构;您可以根据自己的选择修改此代码。这是我对用户交互自定义动画底部导航栏的一个小介绍。

原文链接:https://medium.com/flutterdevs/custom-animated-bottomnavigation-bar-in-flutter-65293e231e4a


本文分享自微信公众号 - 老孟Flutter(lao_meng_qd)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
加载中

作者的其它热门文章

打赏
0
0 收藏
分享
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部