2

前言

在上一篇文章Flutter系列:2.实现一个简单的登录界面通过一个简单的登录页面带入了Flutter中页面构建的方式以及一些简单控件的使用;在开发一个app前首要的任务往往是搭建app需要的基础结构,比如底部菜单,路由导航,网络请求以及一些常用的颜色、图标、按钮、toast组件等。

本次的demo将实现一个简单的app所需的基础结构,实现一个简单的app,基于底部TabBar的方式模块切分,实现网络层调用豆瓣api展示电影列表,任意界面登录验证,app如下图。

[GitHub源码传送]

TabBar菜单

目前app设计中大部分app都是由底部TabBar菜单+顶部导航信息的方式构建的,在iOS开发中UITabBarController 和 UINavigationController 几乎是APP的标配, 同样在Flutter中基于Scaffold的构建方式也直接提供了appBar+body+bottomNavigationBar的方式来切分导航栏、内容和底部菜单,所以我们只需要在首页的Scaffold构造中传入bottomNavigationBar即可。

在Flutter中为我们提供了material design风格的BottomNavigationBar和iOS风格的CupertinoTabBar,我们只需要选择其一稍作封装即可,本demo选择CupertinoTabBar,并封装到BottomNavWidget中,相关细节请看源码。

body的切换

虽然Scaffold提供了appBar+body+bottomNavigationBar的组合,但是并没有实现bottomNavigationBar点击切换body页面显示功能,所以需要开发者自己去处理bottomNavigationBar的点击回调来动态切换body中的内容。

不同的bottomNavigationBarItem对应着不同的显示页面,电影tabBar对应显示电影列表页面,发现tabBar对应显示发现页面... 他们都在body中,他们之间有着频繁的切换但同时只能显示一个页面;基于此使用Stack布局的方式来实现,每个页面组成一个数组成为Stack Widget的children并缓存避免重复创建,使用Offstage组件来包装每个tab页面,并将bottomNavigationBar当前选择的index对应的页面的offstage设置为false, 这样只有当前选择的tab对应的页面显示在body中,而其他的界面并不会显示也不会接收事件占用空间。

路由导航

路由导航也是app常见的基础功能,服务器通过下发路由信息可以实现动态的控制app的页面跳转,常用于动态页面,push和web跳转。

Flutter中的导航有点类似iOS的方式,都是通过栈的方式来管理路由页面。Navigator就是Flutter中管理导航路线的Widget,注意Navigator管理的是页面导航的路线,称为Route的东西而不是像iOS中直接管理的controller,而每个Route(CupertinoPageRoute)则可以通过builder来指定显示的Widget,同时Navigator也提供了对Route 栈操作的方法,push和pop。

Navigator管理的对象是Route,Flutter提供了MaterialPageRoute和iOS风格的CupertinoPageRoute,MaterialPageRoute是根据手机平台自动调整页面的出现动画,本Demo选用CupertinoPageRoute以从右到左的页面出现动画,然后指定其builder即可实现页面的跳转。

MaterialApp内置了一个顶层的导航器Navigator,routes属性支持配置静态的路由表,如果在routes中找不到对应的路由配置时则调用onGenerateRoute来支持动态的路由跳转,它的定义如下:


所以我们需要通过一个函数来实现MaterialApp的onGenerateRoute就可以根据RouteSettings中的路由信息动态的生成页面的Route,同时以Uri的方式来指定Route的名称就可以实现动态传参了,具体详见Demo源码中RouteManager类。

登录注册

登录注册页面可能在app的任何页面推出,同时可能不支持返回需要强制登录的情况,在iOS中常常以present的方式出现,所以在Flutter中需要指定CupertinoPageRoute的fullscreenDialog属性为true即可

页面的跳转

在iOS的开发中基于UITabBarController 和 UINavigationController的构建方式中页面跳转是在UINavigationController内跳转的,同时通过设置Controller的hidesBottomBarWhenPushed属性支持动态的显示和隐藏底部的TarBar, 每个TabBar对应的是一个独立控制的UINavigationController,他们各自有自己路由的导航栈,在Flutter中提供的CupertinoTabScaffold通过为每个TabBar指定显示为CupertinoTabView来实现了同样的机制。

往往在开发中进入二级界面后底部的导航栏都是隐藏的,所以我们完全可以只使用MaterialApp内置的顶层Navigator来实现我们的导航控制,本Demo也是如此。

网络请求

移动端的网络环境是千变万化的,所以app的网络请求应该是一个异步的过程,不能阻塞主线程,本Demo是基于Dart的第三方Http网络请求库dio。

dio是一个强大的Dart Http请求库,支持Restful API、FormData、拦截器、请求取消、Cookie管理、文件上传/下载、超时等...

网络层实现了通过dio请求到网络数据然后反序列为Model对象,dart中的json反序列化要比其他语言麻烦,借助的是json_annotation这个库。

从请求api到回调再到反序列为Model对象这个过程都应该是一个异步的过程,所以他们返回的都是一个Futrure对象,使用Completer就可以很方便的生成一个Future, 然后在恰当的时候传入数据或者错误来结束这个Future。

列表

列表的展示是基于FutureBuilder的方式,因为其依赖api请求返回的future,当future的状态变更时FutureBuilder会接收到最新的快照信息AsyncSnapshot,通过其当前快照来控制ListView或者CircularProgressIndicator的显示。

其他

App开发中还有许多其他的基础模块,比如和原生通信组件(channel)、图片组件、日志组件、其他公共的弹窗、上下拉刷新组件等,本Demo还来不及一一实现,随着学习的深入以后再慢慢总结吧,有不妥的地方还望指正。

Demo源码地址:[GitHub源码传送]


DandJ
195 声望26 粉丝

iOS developer.