Flutter质感设计之底部导航

BottomNavigationBar即底部导航栏控件。显示在应用底部的质感设计控件,用于在少量视图中切换。底部导航栏包含多个以标签、图标或两者搭配的形式显示在项目底部的项目,提供了应用程序的顶级视图之间的快速导航。对于较大的屏幕,侧面导航可能更好。

创建navigation_icon_view.dart文件,定义一个NavigationIconView类,用于管理BottomNavigationBarItem(底部导航栏项目)控件的样式、行为与动画。

import 'package:flutter/material.dart';

// 创建类,导航图标视图

class NavigationIconView {

// 导航图标视图的构造函数

NavigationIconView({

// 控件参数,传递图标

Widget icon,

// 控件参数,传递标题

Widget title,

// 控件参数,传递颜色

Color color,

/*

* Ticker提供者

* 由类实现的接口,可以提供Ticker对象

* Ticker对象:每个动画帧调用它的回调一次

*/

TickerProvider vsync,

}):_icon = icon, //接收传递的图标

// 接收传递的颜色

_color = color,

// 创建底部导航栏项目

item = new BottomNavigationBarItem(

// 项目的图标

icon: icon,

// 项目的标题

title: title

),

// 创建动画控制器

controller = new AnimationController(

// 动画持续的时间长度:默认情况下主题更改动画的持续时间

duration: kThemeAnimationDuration,

// 垂直同步

vsync: vsync,

) {

// 创建曲线动画

_animation = new CurvedAnimation(

// 应用曲线动画的动画

parent: controller,

/*

* 正向使用的曲线:

* 从0.5

* 到1.0结束

* 应用的曲线:快速启动并缓和到最终位置的曲线

*/

curve: new Interval(0.5, 1.0, curve: Curves.fastOutSlowIn),

);

}

// 类成员,存储图标

final Widget _icon;

// 类成员,存储颜色

final Color _color;

// 类成员,底部导航栏项目

final BottomNavigationBarItem item;

// 类成员,动画控制器

final AnimationController controller;

// 类成员,曲线动画

CurvedAnimation _animation;

/*

* 类函数,过渡转换

* BottomNavigationBarType:定义底部导航栏的布局和行为

* BuildContext:处理控件树中的控件

*/

FadeTransition transition(BottomNavigationBarType type, BuildContext context) {

// 局部变量,存储图标颜色

Color iconColor;

// 如果底部导航栏的位置和大小在点击时会变大

if (type == BottomNavigationBarType.shifting) {

// 存储颜色作为图标颜色

iconColor = _color;

} else {

/*

* 保存质感设计主题的颜色和排版值:

* 使用ThemeData来配置主题控件

* 使用Theme.of获取当前主题

*/

final ThemeData themeData = Theme.of(context);

/*

* 如果程序整体主题的亮度很高(需要深色文本颜色才能实现可读的对比度)

* 就返回程序主要部分的背景颜色作为图标颜色

* 否则返回控件的前景颜色作为图标颜色

*/

iconColor = themeData.brightness == Brightness.light

? themeData.primaryColor

: themeData.accentColor;

}

// 返回值,创建不透明度转换

return new FadeTransition(

// 控制子控件不透明度的动画

opacity: _animation,

// 子控件:创建滑动转换过渡

child: new SlideTransition(

/*

* 控制子控件位置的动画

* 开始值和结束值之间的线性插值<以尺寸的分数表示的偏移量>

* (1.0,0.0)表示Size的右上角

* (0.0,1.0)表示Size的左下角

*/

position: new Tween<FractionalOffset>(

// 此变量在动画开头的值

begin: const FractionalOffset(0.0, 0.02),

// 此变量在动画结尾处的值:左上角

end: FractionalOffset.topLeft,

).animate(_animation), // 返回给定动画,该动画接受由此对象确定的值

// 子控件:创建控制子控件的颜色,不透明度和大小的图标主题

child: new IconTheme(

// 用于子控件中图标的颜色,不透明度和大小

data: new IconThemeData(

// 图标的默认颜色

color: iconColor,

// 图标的默认大小

size: 120.0,

),

// 子控件

child: _icon,

)

)

);

}

}

再创建main.dart文件。类CustomIcon创建一个容器控件,作为一个自定义的图标使用。同时使用质感设计的弹出菜单控件切换底部导航栏的行为和样式。

import 'package:flutter/material.dart';

import 'navigation_icon_view.dart';

// 创建类,自定义图标,继承StatelessWidget(无状态的控件)

class CustomIcon extends StatelessWidget {

// 覆盖此函数以构建依赖于动画的当前状态的控件

@override

Widget build(BuildContext context) {

// 获取当前图标主题,创建与此图标主题相同的图标主题

final IconThemeData iconTheme = IconTheme.of(context).fallback();

// 返回值,创建一个容器控件

return new Container(

// 围绕子控件的填充:每个边都偏移4.0

margin: const EdgeInsets.all(4.0),

// 容器宽度:图标主题的宽度减8.0

width: iconTheme.size - 8.0,

// 容器高度:图标主题的高度减8.0

height: iconTheme.size - 8.0,

// 子控件的装饰:创建一个装饰

decoration: new BoxDecoration(

// 背景颜色:图标主题的颜色

backgroundColor: iconTheme.color

)

);

}

}

// 创建类,菜单演示,继承StatefulWidget(有状态的控件)

class MenusDemo extends StatefulWidget {

/*

* 覆盖具有相同名称的超类成员

* createState方法在树中的给定位置为此控件创建可变状态

* 子类应重写此方法以返回其关联的State子类新创建的实例

*/

@override

_MenusDemoState createState() => new _MenusDemoState();

}

/*

* 关联State子类的实例

* 继承State:StatefulWidget(有状态的控件)逻辑和内部状态

* 继承TickerProviderStateMixin,提供Ticker对象

*/

class _MenusDemoState extends State<MenusDemo> with TickerProviderStateMixin {

// 类成员,存储底部导航栏的当前选择

int _currentIndex = 2;

// 类成员,存储底部导航栏的布局和行为:在点击时会变大

BottomNavigationBarType _type = BottomNavigationBarType.shifting;

// 类成员,存储NavigationIconView类的列表

List<NavigationIconView> _navigationViews;

/*

* 在对象插入到树中时调用

* 框架将为它创建的每个State(状态)对象调用此方法一次

* 覆盖此方法可以实现此对象被插入到树中的位置的初始化

* 或用于配置此对象上的控件的位置的初始化

*/

@override

void initState() {

// 调用父类的内容

super.initState();

// 在存储NavigationIconView类的列表里添加内容

_navigationViews = <NavigationIconView>[

/*

* 创建NavigationIconView类的实例

* 传递图标参数

* 传递标题参数

* 传递颜色参数

* 传递Ticker对象

*/

new NavigationIconView(

icon: new Icon(Icons.access_alarm),

title: new Text('成就'),

color: Colors.deepPurple[500],

vsync: this,

),

new NavigationIconView(

icon: new CustomIcon(),

title: new Text('行动'),

color: Colors.deepOrange[500],

vsync: this,

),

new NavigationIconView(

icon: new Icon(Icons.cloud),

title: new Text('人物'),

color: Colors.teal[500],

vsync: this,

),

new NavigationIconView(

icon: new Icon(Icons.favorite),

title: new Text('财产'),

color: Colors.indigo[500],

vsync: this,

),

new NavigationIconView(

icon: new Icon(Icons.event_available),

title: new Text('设置'),

color: Colors.pink[500],

vsync: this,

),

];

// 循环调用存储NavigationIconView类的列表的值

for (NavigationIconView view in _navigationViews)

// 每次动画控制器的值更改时调用侦听器

view.controller.addListener(_rebuild);

// 底部导航栏当前选择的动画控制器的值为1.0

_navigationViews[_currentIndex].controller.value = 1.0;

}

// 释放此对象使用的资源

@override

void dispose() {

// 调用父类的内容

super.dispose();

// 循环调用存储NavigationIconView类的列表中的项

for (NavigationIconView view in _navigationViews)

// 调用此方法后,对象不再可用

view.controller.dispose();

}

// 动画控制器的值更改时的操作

void _rebuild() {

// 通知框架此对象的内部状态已更改

setState((){

// 重建,以便为视图创建动画

});

}

// 建立过渡堆栈

Widget _buildTransitionsStack() {

// 局部变量,存储不透明度转换的列表

final List<FadeTransition> transitions = <FadeTransition>[];

// 循环调用存储NavigationIconView类的列表的值

for (NavigationIconView view in _navigationViews)

// 在存储不透明度转换的列表中添加transition函数的返回值

transitions.add(view.transition(_type, context));

// 对存储不透明度转换的列表进行排序

transitions.sort((FadeTransition a, FadeTransition b) {

final Animation<double> aAnimation = a.listenable;

final Animation<double> bAnimation = b.listenable;

// aValue:a的动画值

double aValue = aAnimation.value;

// bValue:b的动画值

double bValue = bAnimation.value;

/*

* 将aValue与bValue进行比较

* 返回一个负整数,aValue排序在bValue之前

* 返回一个正整数,aValue排序在bValue之后

*/

return aValue.compareTo(bValue);

});

// 返回值,创建层叠布局控件

return new Stack(children: transitions);

}

// 覆盖此函数以构建依赖于动画的当前状态的控件

@override

Widget build(BuildContext context) {

// 局部变量,创建底部导航栏

final BottomNavigationBar botNavBar = new BottomNavigationBar(

/*

* 在底部导航栏中布置的交互项:迭代存储NavigationIconView类的列表

* 返回此迭代的每个元素的底部导航栏项目

* 创建包含此迭代的元素的列表

*/

items: _navigationViews

.map((NavigationIconView navigationView) => navigationView.item)

.toList(),

// 当前活动项的索引:存储底部导航栏的当前选择

currentIndex: _currentIndex,

// 底部导航栏的布局和行为:存储底部导航栏的布局和行为

type: _type,

// 当点击项目时调用的回调

onTap: (int index) {

// 通知框架此对象的内部状态已更改

setState((){

// 当前选择的底部导航栏项目,开始反向运行此动画

_navigationViews[_currentIndex].controller.reverse();

// 更新存储底部导航栏的当前选择

_currentIndex = index;

// 当前选择的底部导航栏项目,开始向前运行此动画

_navigationViews[_currentIndex].controller.forward();

});

}

);

// 实现基本的质感设计视觉布局结构

return new Scaffold(

// 质感设计应用栏

appBar: new AppBar(

// 应用栏中显示的主要控件,包含程序当前内容描述的文本

title: new Text('底部导航演示'),

// 在标题控件后显示的控件

actions: <Widget> [

// 创建一个显示弹出式菜单的按钮

new PopupMenuButton<BottomNavigationBarType>(

// 当用户从此按钮创建的弹出菜单中选择一个值时调用

onSelected: (BottomNavigationBarType value) {

// 通知框架此对象的内部状态已更改

setState((){

// 存储底部导航栏的布局和行为:选择值

_type = value;

});

},

// 点击弹出菜单中显示的项目时调用

itemBuilder: (BuildContext context) => <PopupMenuItem<BottomNavigationBarType>> [

/*

* 弹出菜单中的显示项目

* 返回值:底部导航栏的布局和行为

* 子控件:文本控件

*/

new PopupMenuItem<BottomNavigationBarType>(

value: BottomNavigationBarType.fixed,

child: new Text('Fixed')

),

new PopupMenuItem<BottomNavigationBarType>(

value: BottomNavigationBarType.shifting,

child: new Text('Shifting')

)

]

)

]

),

// 主要内容

body: new Center(

// 主要内容:_buildTransitionsStack函数的返回值

child: _buildTransitionsStack()

),

// 水平的按钮数组,沿着程序的底部显示

bottomNavigationBar: botNavBar,

);

}

}

// 程序入口

void main() {

// 创建质感设计程序,并放置到主屏幕

runApp(new MaterialApp(

// 在窗口管理器中使用此应用程序的单行描述

title: 'Flutter教程',

// 程序的默认路由的控件

home: new MenusDemo(),

));

}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

以上是 Flutter质感设计之底部导航 的全部内容, 来源链接: utcz.com/p/241115.html

回到顶部