Flutter從入門到奔潰(五):擼一些UI互動以及動態頁面

Mr_凌宇發表於2019-03-03

[toc]

Flutter從入門到奔潰(五):擼一些UI互動以及動態頁面

前記

我們之前粗略介紹了基礎以及佈局:

Flutter從入門到奔潰(一):擼一個登入介面

Flutter從入門到奔潰(二):擼一個個人介面

Flutter從入門到奔潰(三):擼一個App基礎框架

# Flutter從入門到奔潰(四):擼一個包含列表重新整理以及網路請求的首頁

總算是脫離了無聊的靜態頁面,涉及到了一部分網路互動,今天我們接著進行UI互動的學習(因為開源中國的api感覺不太好用,我們改用wananzhuo的api,這個也是我們安卓狗練手的必備專案了,這裡感謝鴻洋大神)。

頁面互動

登入介面

image.png

登入介面使用我們之前繪製的靜態頁面,在這裡我們進行UI互動:

  1. 拿到使用者輸入的資料
  2. 對資料進行必要的驗證
  3. 提交使用者資料到後臺
  4. 根據介面成功與否進行頁面互動以及資料更新

接下來我們分步進行上述操作:

拿到使用者輸入的資料

我們頁面是用了基礎的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": ""
}
複製程式碼
  1. 判斷成功與否用errorCode
  2. 顯示資訊用errorMsg
  3. 拿資料用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 **,我們用來進行登入的憑證,這裡的思路是:

  1. 登入的時候拿到cookie,儲存到OsApplication類中,並且持久化
  2. 啟動app的時候從持久化中獲取,儲存到OsApplication類中,
  3. 呼叫介面的時候根據是否要儲存,重新進行儲存,並攜帶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,它是我們下一個步驟的主角。

根據介面成功與否進行頁面互動以及資料更新

我們登入後,要怎麼通知其他頁面,我已經登入了呢?

  1. 把棧裡所有activity都出棧,重新new出新的帶登入資訊的activity再壓棧進去
  2. 通過其他手段通知需要更新狀態的頁面:爺爺我登入了,你趕緊地更新頁面!!

我覺得第二種比較划算!而在安卓中,我們可以通過原生的廣播,第三方的EventBus來實現,而在flutter,我們可以考慮用外掛event_bus: ^1.0. 1來實現。

它的實現方式和安卓版的類似:

  1. 寫event類
  2. 事件源發出event
  3. 接受源接受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 “鑑賞”

接下來做好準備!!! 你將會受到視覺的衝擊!!! 一大波鋼鐵直男的粗糙審美將會衝擊你! 我很自豪地認為要是有審美選醜比賽,我肯定可以奪得第一!

image.png
image.png
image.png

頁面拆解

一級頁面

一級頁面沒有什麼難度,我們仍然有多種方案來實現它:

  1. listView
  2. CustomScrollView
  3. 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

總結

陳詞

草草地說,好像也沒什麼好寫了,接下去做的都是重複性的勞動:

  1. 接介面
  2. 寫資料
  3. 畫UI

但是

但是flutter不止這麼些可以玩的,動畫,2端互動,其其他他零零總總的,還有很多好玩的控制元件,嗯...接下來繼續慢慢玩flutter。

互勉

一起玩吧

相關文章