📜  Flutter – 凸底条(1)

📅  最后修改于: 2023-12-03 15:15:08.274000             🧑  作者: Mango

Flutter - 凸底条

Flutter 凸底条是一个常见的 UI 组件,可以给应用增添美观度和用户交互性。凸底条通常用于底部菜单导航栏,提供应用的核心功能和浏览多个页面的方式。Flutter 提供了内置的凸底条组件,可以轻松实现此功能。下面详细介绍凸底条的使用方法。

实现凸底条

在 Flutter 中,可以使用 BottomNavigationBar 组件来实现凸底条。BottomNavigationBar 中有 items 属性,它的值应该为一个包含 BottomNavigationBarItem 的数组。BottomNavigationBarItem 包含了需要显示在凸底条上的每个条目。每个条目都包含一个图标和标题文字。下面是一个实现简单的凸底条的例子:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  static const String _title = 'Flutter Bottom Navigation Bar Example';

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: _title,
      home: BottomNavigation(),
    );
  }
}

class BottomNavigation extends StatefulWidget {
  BottomNavigation({Key? key}) : super(key: key);

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

class _BottomNavigationState extends State<BottomNavigation> {
  int _currentIndex = 0;

  final List<Widget> _children = [
    PlaceholderWidget(Colors.white),
    PlaceholderWidget(Colors.deepOrange),
    PlaceholderWidget(Colors.green)
  ];

  void onTabTapped(int index) {
    setState(() {
      _currentIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Bottom Navigation Bar Example'),
      ),
      body: _children[_currentIndex],
      bottomNavigationBar: BottomNavigationBar(
        onTap: onTabTapped,
        currentIndex: _currentIndex,
        items: [
          BottomNavigationBarItem(
            icon: new Icon(Icons.home),
            label: 'Home',
          ),
          BottomNavigationBarItem(
            icon: new Icon(Icons.mail),
            label: 'Messages',
          ),
          BottomNavigationBarItem(
              icon: Icon(Icons.person), label: 'Profile')
        ],
      ),
    );
  }
}

class PlaceholderWidget extends StatelessWidget {
  final Color color;

  PlaceholderWidget(this.color);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: color,
    );
  }
}

在这个例子中,我们定义了一个 BottomNavigation 组件作为主组件,并定义了几个常规的页面片段作为凸底条条目的子页面。最后我们将它们放在 BottomNavigationBarItem 组件中,并用 BottomNavigationBar 进行渲染形成凸底条。下面是效果图。

flutter_bottom_nav_bar_example

自定义凸底条动画

Flutter 的 BottomNavigationBar 组件默认提供了简单的动画。但是,我们可以使用 Flutter 的动画库来自定义凸底条的动画过渡效果,从而增加应用的交互性。例如,在 tab 之间切换时缩放图标,可以给用户更加明显的反馈。下面是一个自定义的凸底条动画的示例:

class CustomBottomNavigationBar extends StatefulWidget {
  final List<BottomBarItem> items;
  final ValueChanged<int> onTap;
  final int currentIndex;

  CustomBottomNavigationBar({
    required this.items,
    required this.onTap,
    required this.currentIndex,
  }) : assert(items.length > 1, 'Items cannot be less than 2');

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

class _CustomBottomNavigationBarState
    extends State<CustomBottomNavigationBar>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 300),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return BottomNavigationBar(
      currentIndex: widget.currentIndex,
      onTap: (index) {
        if (widget.currentIndex != index) {
          setState(() {
            _controller.reset();
            _controller.forward();
          });
          widget.onTap(index);
        }
      },
      items: List.generate(
        widget.items.length,
        (index) => buildBottomNavigationBarItem(
          index: index,
          item: widget.items[index],
          animation: _controller,
        ),
      ),
      type: BottomNavigationBarType.fixed,
    );
  }

  BottomNavigationBarItem buildBottomNavigationBarItem({
    required int index,
    required BottomBarItem item,
    required AnimationController animation,
  }) {
    ColorTween colorTween;
    if (widget.currentIndex == index) {
      colorTween = ColorTween(
        begin: item.inactiveColor,
        end: item.activeColor,
      );
    } else {
      colorTween = ColorTween(
        begin: item.activeColor,
        end: item.inactiveColor,
      );
    }
    return BottomNavigationBarItem(
      icon: ScaleTransition(
        scale: Tween<double>(begin: 1.0, end: 1.2).animate(
          CurvedAnimation(
            parent: animation,
            curve: Interval(
              0.0,
              0.5,
              curve: index == 0
                  ? Curves.easeOutSine
                  : Curves.easeInSine,
            ),
          ),
        ),
        child: Icon(
          item.icon,
          color: colorTween.evaluate(animation),
        ),
      ),
      label: item.label,
    );
  }
}

class BottomBarItem {
  final IconData icon;
  final String label;
  final Color activeColor;
  final Color inactiveColor;

  BottomBarItem({
    required this.icon,
    required this.label,
    required this.activeColor,
    required this.inactiveColor,
  });
}

在此例中,我们定义了 CustomBottomNavigationBar 组件并注入一组 BottomBarItem 实例。同时,还传递了 onTap 回调函数、currentIndex 和对应的 BottomNavigationBarItem 以及动画控制器。buildBottomNavigationBarItem 函数只是一个简单的封装,用于构建图标动画,当用户从一个标签切换到另一个标签时,每个图标按规定的曲线轻轻地缩小和放大。

最后,使用此自定义函数来渲染我们的凸底条:

class _CustomBarAnimationState extends State<CustomBarAnimation>
    with TickerProviderStateMixin {
  static const barHeight = 72.0;

  late Animation<Offset> _animation;
  late AnimationController _controller;
  int _currentIndex = 0;

  final List<Widget> _children = [
    PlaceholderWidget(Colors.white),
    PlaceholderWidget(Colors.deepOrange),
    PlaceholderWidget(Colors.green),
  ];

  final List<BottomBarItem> _items = [
    BottomBarItem(
      icon: Icons.home,
      label: 'Home',
      activeColor: Colors.blue,
      inactiveColor: Colors.grey,
    ),
    BottomBarItem(
      icon: Icons.mail,
      label: 'Messages',
      activeColor: Colors.blue,
      inactiveColor: Colors.grey,
    ),
    BottomBarItem(
      icon: Icons.person,
      label: 'Profile',
      activeColor: Colors.blue,
      inactiveColor: Colors.grey,
    ),
  ];

  void _onTap(int index) {
    setState(() {
      _currentIndex = index;
    });
  }

  @override
  void initState() {
    _controller = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 800),
    );
    _animation = Tween<Offset>(
      begin: Offset.zero,
      end: Offset(0, 1),
    ).animate(
      CurvedAnimation(
        parent: _controller,
        curve: Curves.easeInOutExpo,
      ),
    );
    super.initState();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Custom Bottom Navigation Bar Animation'),
      ),
      body: Stack(
        alignment: Alignment.bottomCenter,
        children: [
          SizedBox(
            height: MediaQuery.of(context).size.height - barHeight,
            child: _children[_currentIndex],
          ),
          SlideTransition(
            position: _animation,
            child: Container(
              height: barHeight,
              decoration: BoxDecoration(
                color: Colors.white,
                boxShadow: [
                  BoxShadow(
                    color: Colors.grey,
                    blurRadius: 5,
                  ),
                ],
              ),
              child: CustomBottomNavigationBar(
                items: _items,
                currentIndex: _currentIndex,
                onTap: _onTap,
              ),
            ),
          ),
        ],
      ),
    );
  }
}

如图所示,自定义组件提供了一种更细微的交互效果:

flutter_custom_bottom_nav_bar_animation

结论

Flutter 对凸底条的支持,使得应用程序增量增加了和用户交互式的用户标准,也提供了自定义动画的空间,从而为应用程序提供了更多的个性化。如果您想使用凸底条组件来改进您的应用程序,请查看官方文档,了解更多信息。