[toc]
Flutter從入門到奔潰(五):擼一些UI互動以及動態頁面
前記
我們之前粗略介紹了基礎以及佈局:
# 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。
互勉
一起玩吧