博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
flutter实战2:为APP增加上下Tab页
阅读量:6520 次
发布时间:2019-06-24

本文共 6785 字,大约阅读时间需要 22 分钟。

紧接上一篇的,这次我们向APP中加入上下Tab页,使之跟趋近主流大部分APP的信息布局套路,等不及看源码的同学可以点击进入我的仓库下载代码。

Tab关键元素

  • 这是Tab页的控制器,用于定义Tab标签和内容页的坐标,还可配置标签页的切换动画效果等。

TabController一般放入有状态控件中使用,以适应标签页数量和内容有动态变化的场景,如果标签页在APP中是静态固定的格局,则可以在无状态控件中加入简易版的以提高运行效率,毕竟无状态控件要比有状态控件更省资源,运行效率更快。

  • Tab页的Title控件,切换Tab页的入口,一般放到AppBar控件下使用,内部有**Title*属性。其子元素按水平横向排列布局,如果需要纵向排列,请使用ColumnListView控件包装一下。子元素为Tab类型的数组。

  • Tab页的内容容器,其内放置Tab页的主体内容。子元素可以是多个各种类型的控件。

Tab使用方法

有状态控件搭配TabController

Tab页的切换搭配了动画,因此到State类上附加一个SingleTickerProviderStateMixin:

//定义有状态控件  class HomePage extends StatefulWidget {    @override    _HomePageState createState() => new _HomePageState();  }  //用于使用到了一点点的动画效果,因此加入了SingleTickerProviderStateMixin  class _HomePageState extends State
with SingleTickerProviderStateMixin{ ... }复制代码

然后到有状态控件的子类State中初始化控制器TabController

@override  void initState() {    super.initState();    _tabController = new TabController(      vsync: this,     //动画效果的异步处理,默认格式,背下来即可      length: 3      //需要控制的Tab页数量    );      }  //当整个页面dispose时,记得把控制器也dispose掉,释放内存  @override  void dispose() {    _tabController .dispose();    super.dispose();  }复制代码

然后到TabBarTabBarView中的controller属性中调用控制器_tabController

//标签页标题  new TabBar(          tabs: [    //注意TabBar的子元素为Tab类型的数组            new Tab(icon: new Icon(Icons.directions_car)),            new Tab(icon: new Icon(Icons.directions_transit)),            new Tab(icon: new Icon(Icons.directions_bike)),          ]  //标签页内容区域  new TabBarView(        children: [          new Icon(Icons.directions_car),          new Icon(Icons.directions_transit),          new Icon(Icons.directions_bike),        ]复制代码

最后,我们把定义好的TabBarTabBarView安放到需要的地方去,比如:

new Scaffold(      appBar: new AppBar(        backgroundColor: Colors.deepOrange,        title: new Text('title'),      ),      ....      body: new TabBarView(        //TabBarView呈现内容,因此放到Scaffold的body中          controller: _bottomNavigation,      //配置控制器          children:  [      //注意顺序与TabBar保持一直            new News(data: '参数值'),    //上一篇定义好的子页面            new TabPage2(),            new TabPage3(),          ]        ),      bottomNavigationBar: new Material(    //为了适配主题风格,包一层Material实现风格套用        color: Colors.deepOrange,   //底部导航栏主题颜色        child: new TabBar(        //TabBar导航标签,底部导航放到Scaffold的bottomNavigationBar中          controller: _bottomNavigation,      //配置控制器          tabs: _bottomTabs,          indicatorColor: Colors.white, //tab标签的下划线颜色        ),      )     );复制代码

无状态控件搭配DefaultTabController

DefaultTabController要简单很多,由于应用在无状态控件中,使用DefaultTabController包裹需要用到Tab的页面即可:

class TabPage3 extends StatelessWidget {  @override  Widget build(BuildContext context) {    return  new DefaultTabController(        length: 3,        child: new Scaffold(          appBar: new AppBar(            backgroundColor: Colors.orangeAccent,            title: new TabBar(              tabs: [                new Tab(icon: new Icon(Icons.directions_car)),                new Tab(icon: new Icon(Icons.directions_transit)),                new Tab(icon: new Icon(Icons.directions_bike)),              ],              indicatorColor: Colors.white,            ),          ),          body: new TabBarView(            children: [              new Icon(Icons.directions_car),              new Icon(Icons.directions_transit),              new Icon(Icons.directions_bike),            ],          ),        ),      );  }}复制代码

DefaultTabControllerTabController的用法差异主要在控制器的定义上,TabBarTabBarView的使用方法完全相同,通常上下Tab页的标签分别安放在Scaffold控件的appBarbottomNavigationBar属性上,然后我们把APP填充成下面这个样式:

代码结构

如上图所示,APP以底部Tab导航栏为主入口,底部每个Tab中又各自有自己的顶部次级Tab页,因此我们把代码结构整理一下:

  • _HomePageState是APP的主页面HomePage的子类State

  • 通过Scaffold底部的bottomNavigationBar属性摆放TabBar,搭建Tab页的标签栏

  • 放入Scaffoldbody属性放入TabBarViewTabBarViewchildren是三个外部dart文件定义的控件页面

  • 外部dart文件定义的控件页面分别又有各自风格的Tab标签页

  • Tab页的通用属性可以提前定义到数组List中,在TabBarTabBarView通过遍历提取List的值创建子元素可以提高代码的维护效率:

    //在StatefulWidget控件中,可通过修改下面的数组,实现Tab页的动态加载 final List myTabs = [ new Tab(text: 'Tab1'), new Tab(text: 'Tab2'), new Tab(text: 'Tab3'), new Tab(text: 'Tab4'), new Tab(text: 'Tab5'), new Tab(text: 'Tab6'), new Tab(text: 'Tab7'), new Tab(text: 'Tab8'), new Tab(text: 'Tab9'), new Tab(text: 'Tab10'), new Tab(text: 'Tab11'), ];

    Widget build(BuildContext context) {  return new Scaffold(    appBar: new AppBar(      backgroundColor: Colors.orangeAccent,      title: new TabBar(        controller: _tabController,        tabs: myTabs,    //使用Tab类型的数组呈现Tab标签        indicatorColor: Colors.white,        isScrollable: true,         ),    ),    body: new TabBarView(      controller: _tabController,      children: myTabs.map((Tab tab) {    //遍历List
    类型的对象myTabs并提取其属性值作为子控件的内容 return new Center(child: new Text(tab.text+' '+widget.data)); //使用参数值 }).toList(), ), );}复制代码

使用获取到的参数

由于StatelessWidgetStatefulWidget的页面构建不同,使用从外部获取到的参数的方式也略有差异,在这里简单总结下。

  • StatelessWidget的获参和用参方式 定义StatelessWidget控件时,添加一个final类型的变量如pageText,用于为参数值预留空间,并在构造函数中加入参数值:

    class SidebarPage extends StatelessWidget { final String pageText; //定义一个常量,用于保存跳转进来获取到的参数 SidebarPage(this.pageText); //构造函数,获取参数 ... }

使用参数时直接引用即可:

Widget build(BuildContext context) {    return new Scaffold(      appBar: new AppBar(title: new Text(pageText),),   //将参数当作页面标题      body: new Center(        child: new Text('pageText'),      ),    );  }复制代码

从外部传入参数时,直接向构造函数中填入参数值即可:

Navigator.of(context).push(new MaterialPageRoute(builder:     (BuildContext context) => new SidebarPage('First Page')));    //在new方法时调用控件的构造函数传入参数值复制代码
  • StatefulWidget的获参和用参方式 相比StatelessWidget略复杂。定义构造函数时需要默认声明key

    class TabPage1 extends StatefulWidget { const TabPage1({ Key key , this.data}) : super(key: key); //构造函数中增加参数 final String data; //为参数分配空间 @override _MyTabbedPageState createState() => new _MyTabbedPageState(); }

使用时,由于在State子类中实现具体的页面内容,因此State子类使用父类TabPage1的参数时需要在参数名前增加一个***widget***关键字:

class _MyTabbedPageState extends State
{ ... new Center(child: new Text(tab.text+' '+widget.data)); //使用参数值,需在参数名前增加widget前缀 ...}复制代码

从外部传入参数时,需要声明参数名:

new TabBarView    controller: _bottomNavigation,    children:  [            new TabPage1(data: '参数值'),    //new方法调用构造函数时,还需要声明参数名称      new TabPage2(),      new TabPage3(),    ]  )复制代码

好勒,今天就讲到这里,大家去下载我的源码试试效果吧,代码中有附加的注释,对一些控件属性的特性也有单独的描述,相信看完源码之后,大家也可以自行实现效果了。

顺便分享一个雷区,由于当初创建这个项目时,我使用命令**flutter create [APPname1]的方式创建了这个项目,但我发现这个APPname1(代称,并非真实名称)不好听,想把项目改名为APPname2,于是参考之前写的,把项目文件夹改名为APPname2,并非常任性的把项目目录下的_android\app\src\main\AndroidManifest.xml_文件,把packageandroid:label都改成了APPname2,于是不出意料的悲催了,命令flutter fun**报错,无法启动APP,还原配置之后,无法启动APP,即便尝试通过全文搜索APPname1,都按规定格式替换成APPname2,或者逆向改回去,都无法启动APP,此时已是凌晨1点。。。妥妥的血泪史,所以郑重的告诫大家:

不要在项目的各种配置文件中轻易改动项目名称!不要在项目的各种配置文件中轻易改动项目名称!不要在项目的各种配置文件中轻易改动项目名称! 否则你就是下一个在电脑面前捶胸顿足的鱼丸。什么?问我怎么恢复的?当然是托git的福。

感谢大家的支持,请关注我的,多多投稿,也可以加入**flutter 中文社区(官方QQ群:338252156)**共同成长,谢谢大家~

转载地址:http://fvrfo.baihongyu.com/

你可能感兴趣的文章
php课程---Json格式规范需要注意的小细节
查看>>
hadoop hdfs notes
查看>>
Java反射机制详解(3) -java的反射和代理实现IOC模式 模拟spring
查看>>
(2编写网络)自己动手,编写神经网络程序,解决Mnist问题,并网络化部署
查看>>
手把手教你实现boost::bind
查看>>
【转】如何使用分区助手完美迁移系统到SSD固态硬盘?
查看>>
java~lambda表达式让查询更优雅
查看>>
a标签中的javascript:;是什么
查看>>
关于connect: network is unreachable 问题的解决【转】
查看>>
RxSwift源码与模式分析一:基本类
查看>>
.NET MVC5+ Dapper+扩展+微软Unity依赖注入实例
查看>>
INSTALL_FAILED_USER_RESTRICTED
查看>>
使用 https, 并将 WWW 跳转到 NON-WWW
查看>>
iToolkit,推荐我们自己做的一套前端组件
查看>>
Junit源码阅读(一)
查看>>
JavaScript设计模式与开发实践 | 01 - 面向对象的JavaScript
查看>>
捷信达酒店管理系统使用说明
查看>>
使用java api 创建excel内容并发送邮件
查看>>
Unity3d删除无用的美术资源轻量级插件
查看>>
Linux基础命令---umask
查看>>