[toc]
Flutter从入门到奔溃(五):撸一些UI交互以及动态页面
前记
我们之前粗略介绍了基础以及布局:
Flutter从入门到奔溃(一):撸一个登录界面
Flutter从入门到奔溃(二):撸一个个人界面
Flutter从入门到奔溃(三):撸一个App基础框架
# Flutter从入门到奔溃(四):撸一个包含列表刷新以及网络请求的首页
总算是脱离了无聊的静态页面,涉及到了一部分网络交互,今天我们接着进行UI交互的学习(因为开源中国的api感觉不太好用,我们改用wananzhuo的api,这个也是我们安卓狗练手的必备项目了,这里感谢鸿洋大神)。
页面交互
登录界面
登录界面使用我们之前绘制的静态页面,在这里我们进行UI交互:
- 拿到用户输入的数据
- 对数据进行必要的验证
- 提交用户数据到后台
- 根据接口成功与否进行页面交互以及数据更新
接下来我们分步进行上述操作:
拿到用户输入的数据
我们页面是用了基础的TextField,所以我们通过controller来进行数据获取,(如果是用form表单的格式的话,还有另外一种方式,在onsave方法里面保存,这里按下不表)。
onPressed: () {
_postLogin(
_userNameController.text, _userPassController.text);
},
我们很容易可以知道上述代码的作用是通过2个对应的controller拿到了username,userpass2个参数,并把他们作为参数提供给了私有方法_postLogin。
对数据进行必要的验证
这里对数据并没有太大的要求,只要是非空,我们就默认是有效数据,所以我们做了简单的判断:
_postLogin(String userName, String userPassword) {
if (userName.isNotEmpty && userPassword.isNotEmpty) {
// do some
} else {
TsUtils.showShort('请输入用户名和密码');
}
}
这里我们稍微讲下使用的一个插件:** fluttertoast: ^2.0.7**,很明显就是一个android的Toast方法,(这里还有另外一种做法是用原生提供桥接,让flutter调用原生方法进行交互,这里也按下不表,后续我们更新这部分的内容),
import 'package:fluttertoast/fluttertoast.dart';
class TsUtils{
static showShort(String msg){
Fluttertoast.showToast(
msg: msg,
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
timeInSecForIos: 1,
bgcolor: "#63CA6C",
textcolor: '#ffffff'
);
}
}
提交用户数据到后台
玩安卓的api接口是典型的三段式接口:
{
"data": ...,
"errorCode": 0,
"errorMsg": ""
}
- 判断成功与否用errorCode
- 显示信息用errorMsg
- 拿数据用data
所以我们对应进行了比较拙略的封装:
// post请求
static Future<Map> post(String url,
{Map<String, String> params, bool saveCookie = false}) async {
if (params == null) {
params = new Map();
}
String _url = Api.BASE_URL + url;
if (OsApplication.cookie != null) {
params['Cookie'] = OsApplication.cookie;
}
http.Response res = await http.post(_url, body: params);
return _dealWithRes(res, saveCookie: saveCookie);
}
static Map<String, dynamic> _dealWithRes(var res, {bool saveCookie}) {
if (res.statusCode == 200) {
var cookie = res.headers['set-cookie'];
if (saveCookie) {
SpUtils.saveCookie(cookie);
OsApplication.cookie = cookie;
}
String body = res.body;
var jsonStr = json.decode(body);
print('the jsonStr is $jsonStr');
int errCode = jsonStr['errorCode'];
if (errCode == 0) {
var data = jsonStr['data'];
return data;
} else {
TsUtils.showShort(jsonStr['errorMsg']);
return null;
}
} else {
TsUtils.showShort('您的网络好像不太好哟~~~///(^v^)\\\~~~');
return null;
}
}
这里需要注意的是一个**cookie **,我们用来进行登录的凭证,这里的思路是:
- 登录的时候拿到cookie,保存到OsApplication类中,并且持久化
- 启动app的时候从持久化中获取,保存到OsApplication类中,
- 调用接口的时候根据是否要保存,重新进行保存,并携带cookie传到后台
(存在一个问题是内存被杀死后,要重新从持久化中获取)
我们完整的登录方法应该是:
_postLogin(String userName, String userPassword) {
if (userName.isNotEmpty && userPassword.isNotEmpty) {
Map<String, String> params = new Map();
params['username'] = userName;
params['password'] = userPassword;
Http.post(Api.USER_LOGIN, params: params,saveCookie: true).then((result) {
SpUtils.map2UserInfo(result).then((userInfoBean){
if(userInfoBean!=null){
OsApplication.eventBus.fire(new LoginEvent(userInfoBean.username));
SpUtils.saveUserInfo(userInfoBean);
Navigator.pop(context);
}
});
});
} else {
TsUtils.showShort('请输入用户名和密码');
}
}
这里我们是不是看到了一个熟悉的名词呢=> eventBus,hahahahahahahahhahahahahhaha,它是我们下一个步骤的主角。
根据接口成功与否进行页面交互以及数据更新
我们登录后,要怎么通知其他页面,我已经登录了呢?
- 把栈里所有activity都出栈,重新new出新的带登录信息的activity再压栈进去
- 通过其他手段通知需要更新状态的页面:爷爷我登录了,你赶紧地更新页面!!
我觉得第二种比较划算!而在安卓中,我们可以通过原生的广播,第三方的EventBus来实现,而在flutter,我们可以考虑用插件event_bus: ^1.0. 1来实现。
它的实现方式和安卓版的类似:
- 写event类
- 事件源发出event
- 接受源接受event,并作出对应处理
登录事件源发出消息
OsApplication.eventBus.fire(new LoginEvent(userInfoBean.username));
个人中心接受源接收消息
OsApplication.eventBus.on<LoginEvent>().listen((event) {
setState(() {
if (event != null && event.userName != null) {
userName = event.userName;
userAvatar = 'http://www.wanandroid.com/resources/image/pc/logo.png';
} else {
userName = null;
userAvatar = null;
}
});
});
设置页面-退出登录发送logout事件源
这里不能说用户一点退出就立马噌噌噌地退出了,要有一个交互的过程--AlertDialog,
_showDialog() {
showDialog(
builder: (context) => new AlertDialog(
title: new Text('提示'),
content: new Text('是否要退出登录'),
actions: <Widget>[
new FlatButton(
onPressed: () {
Navigator.pop(context);
},
child: new Text('取消')),
new FlatButton(
onPressed: () {
SpUtils.cleanUserInfo();
OsApplication.eventBus.fire(new LoginEvent(null));
Navigator.pop(context);
},
child: new Text('是的'))
],
),
context: context);
}
在确定按钮中,我们清除了用户信息(包括保存保存于内存和持久化的userName,token,id,cookie),
并且发出了一个null的event,由接受源代码可以知道,会显示未登录状态。
丑丑的体系页面
UI “鉴赏”
接下来做好准备!!! 你将会受到视觉的冲击!!! 一大波钢铁直男的粗糙审美将会冲击你!
我很自豪地认为要是有审美选丑比赛,我肯定可以夺得第一!
页面拆解
一级页面
一级页面没有什么难度,我们仍然有多种方案来实现它:
- listView
- CustomScrollView
- ScrollView
这里我们选用CustomScrollView,具体代码可以:
体系页面
如果有人有兴趣的话,可以试试自己动手写一个呢。
二级页面
二级页面用安卓来实现肯定就是:tabLayout+ViewPager,有趣的是flutter只用一个控件就可以实现了DefaultTabController
@override
Widget build(BuildContext context) {
widgetsUtils = new WidgetsUtils(context);
return new Scaffold(
appBar: new AppBar(
title: widgetsUtils.getAppBar(_title),
iconTheme: new IconThemeData(color: Colors.white),
),
body: new DefaultTabController(
child: new Scaffold(
appBar: new TabBar(
isScrollable: true,
tabs: _initTabs(),
),
body: new TabBarView(children: _initBody())),
length: classList.length,
),
);
}
其中的TabBar类似于tabLayout;
其中的TabBarView类似于ViewPager;
这里有一个需要注意的点,TabBarView的children每次划到的时候都会重新走一次initState(),而如果我们在那里请求接口的话,就会每次都请求一次,这样无论是UI还是性能还是体验都不是我们要的
而解决方案是在children(也就是类似fragment)的State加上 with AutomaticKeepAliveClientMixin
class _SystemChildPageState extends State<SystemChildPage>
with AutomaticKeepAliveClientMixin{
@override
bool get wantKeepAlive => true;
}
见名知意,这个是用于标志是否保持状态的tag。
三级
三级没了... 就一个webview
总结
陈词
草草地说,好像也没什么好写了,接下去做的都是重复性的劳动:
- 接接口
- 写数据
- 画UI
但是
但是flutter不止这么些可以玩的,动画,2端交互,其其他他零零总总的,还有很多好玩的控件,嗯...接下来继续慢慢玩flutter。
互勉
一起玩吧