前置知识
- 了解flutter基础知识
- 在Scaffold组件中有bottomNavigationBar可以设置底部导航
- 使用BottomNavigationBar数量超过3个时会显示白屏,需要将type设置为BottomNavigationBarType.fixed
设置中间凸起的底部导航
思路
- 中间凸起效果:利用浮动按钮居中,覆盖中间的BottomNavigationBarItem
- 关于BottomNavigationBar的样式,后期会抽到全局的主题进行管理。本文没有抽离
效果
代码
/pages/Index_page.dart
dart
import 'package:flutter/material.dart';
class IndexPage extends StatelessWidget {
const IndexPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed, items: _buildBottomBarItem),
// 浮动按钮
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.transparent,
elevation: 0,
// 设置为圆形
shape: const CircleBorder(),
onPressed: () {},
child: const Icon(
Icons.change_circle,
size: 55,
),
),
// 设置浮动按钮位置底部居中
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
}
// 生成底部导航按钮
List<BottomNavigationBarItem> get _buildBottomBarItem {
return [
const BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'),
const BottomNavigationBarItem(icon: Icon(Icons.search), label: '搜索'),
// 中间这个使用空白占位
const BottomNavigationBarItem(icon: SizedBox(height: 24), label: '交易'),
const BottomNavigationBarItem(icon: Icon(Icons.person), label: '我的'),
const BottomNavigationBarItem(icon: Icon(Icons.person), label: '我的'),
];
}
}
切换时的弹跳动画效果
思路
- 设置动画需要AnimationController,实例AnimationController需要this,因此使用StatefulWidget有状态组件,在initState中实例化AnimationController
- 使用AnimationController需要混入SingleTickerProviderStateMixin类
- 利用AnimatedBuilder监听_animationController的变化
- 将_animationController转换为Animation,即可使用Animation的value设置激活的图标的大小,通过AnimatedBuilder的监听不断build让页面呈现动画效果
- 在bar切换时执行动画
效果
代码
/pages/Index_page.dart
dart
import 'package:flutter/material.dart';
class IndexPage extends StatefulWidget {
const IndexPage({Key? key}) : super(key: key);
@override
State<IndexPage> createState() => _IndexPageState();
}
// 使用AnimationController需要混入SingleTickerProviderStateMixin类
class _IndexPageState extends State<IndexPage>
with SingleTickerProviderStateMixin {
late final AnimationController _animationController;
late final Animation<double> _animation;
int _currentIndex = 0;
@override
void initState() {
super.initState();
// 动画Controller,可以控制动画的开始,停止等操作,首次先执行一次动画
_animationController = AnimationController(
vsync: this, duration: const Duration(milliseconds: 500))
..forward();
// Curves.bounceOut弹跳退出效果
_animation = Tween<double>(begin: 1, end: 24)
.chain(CurveTween(curve: Curves.bounceOut))
.animate(_animationController);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('底部导航设置')),
// AnimatedBuilder监听_animationController的变化
bottomNavigationBar: AnimatedBuilder(
animation: _animationController,
builder: (context, child) {
return BottomNavigationBar(
currentIndex: _currentIndex,
type: BottomNavigationBarType.fixed,
onTap: (value) {
_currentIndex = value;
// 需要先清除上一个动画,forward才能生效,不让动画已经解释了,此时的value不会变了
_animationController.reset();
_animationController.forward();
},
items: _buildBottomBarItem,
);
}),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.transparent,
elevation: 0,
// 设置为圆形
shape: const CircleBorder(),
onPressed: () {},
child: const Icon(
Icons.change_circle,
size: 55,
),
),
// 设置浮动按钮位置底部居中
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
}
// 生成底部导航按钮
List<BottomNavigationBarItem> get _buildBottomBarItem {
return [
BottomNavigationBarItem(
icon: Icon(
Icons.person,
),
activeIcon: Icon(
Icons.person,
size: _animation.value,
),
label: '首页'),
BottomNavigationBarItem(
icon: Icon(
Icons.person,
),
activeIcon: Icon(
Icons.person,
size: _animation.value,
),
label: '搜索'),
// 中间这个使用空白占位
const BottomNavigationBarItem(icon: SizedBox(height: 24), label: '交易'),
BottomNavigationBarItem(
icon: Icon(
Icons.person,
),
activeIcon: Icon(
Icons.person,
// 设置激活图标的大小
size: _animation.value,
),
label: '我的'),
BottomNavigationBarItem(
icon: Icon(
Icons.person,
),
activeIcon: Icon(
Icons.person,
size: _animation.value,
),
label: '我的'),
];
}
}
让中间的按钮旋转起来
效果
第一步:设置旋转效果的animation
dart
@override
void initState() {
super.initState();
//...
// 交易按钮旋转效果的animation
_animationRotate = Tween<double>(begin: 0, end: 1)
.chain(CurveTween(curve: Curves.linear))
.animate(_animationController);
}
第二步:用AnimatedBuilder监听浮动按钮
dart
floatingActionButton: AnimatedBuilder(
animation: _animationController,
builder: (context, child) {
return FloatingActionButton(
backgroundColor: Colors.transparent,
elevation: 0,
// 设置为圆形
shape: const CircleBorder(),
onPressed: () {
_currentIndex = 2;
// 执行动画
_animationController.reset();
_animationController.forward();
},
child: _currentIndex != 2
? const SvgIcon(icon: 'transaction')
// 激活的时候进行旋转 SvgIcon是自己封装的组件可替换
: Transform.rotate(
angle: math.pi * _animationRotate.value,
child: const SvgIcon(icon: 'transaction_active'),
),
);
}),