Flutter構(gòu)建帶緩存機制的全局用戶登錄信息架構(gòu)

繼前篇flutter初探之后沮焕,此次我來聊聊從傳統(tǒng)web項目角度剖析我是怎么一步步搭建flutter基礎(chǔ)項目的弓坞。本文主要講述如何實現(xiàn)flutter用戶數(shù)據(jù)多頁面共享隧甚,用戶數(shù)據(jù)本地緩存。

image.png

首先渡冻,我們引入數(shù)據(jù)共享組件Provider https://flutter.cn/docs/development/data-and-backend/state-mgmt/intro
我們用到的核心模塊有:
1.ChangeNotifier ——共享數(shù)據(jù)源頭部(數(shù)據(jù)存儲戚扳、數(shù)據(jù)傳播)
2.ChangeNotifierProvider ——共享數(shù)據(jù)視圖層注入組件
3.Consumer ——共享數(shù)據(jù)作用節(jié)點
我們用到的核心方法有:
1.Provider.of<Model>(context, listen: true).Fuc

下面開始實現(xiàn)userInfo數(shù)據(jù)共享
第一部分:構(gòu)建數(shù)據(jù)源,也就是基于ChangeNotifier的UserInfo類

import 'package:flutter/material.dart'; //ChangeNotifier需要material素材庫
import 'package:flutter_app2/models/UserModel.dart'; //這個是根據(jù)接口數(shù)據(jù)構(gòu)建的數(shù)據(jù)解析類
class UserInfo extends ChangeNotifier{
  UserModel _info;
  UserModel get info => _info ?? null;
  void setInfo(info){
    _info = UserModel.fromJson(info);
    notifyListeners();
  }
}

利用https://javiercbk.github.io/json_to_dart/在線工具可快速將后端返回的JSON數(shù)據(jù)轉(zhuǎn)化成Dart JSON類

image.png

你可能會問為啥要轉(zhuǎn)類型族吻,我一開始覺得沒必要帽借,可是當我使用返回的數(shù)據(jù)珠增,直接拿access_token參數(shù)的時候,就報錯了~轉(zhuǎn)換一下宜雀,可以規(guī)避這種特殊字符報錯問題切平。

class UserModel {
  String name;
  String accessToken;
  String mobile;

  UserModel({this.name, this.accessToken, this.mobile});

  UserModel.fromJson(Map<String, dynamic> json) {
    name = json['name'];
    accessToken = json['access_token'];
    mobile = json['mobile'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['name'] = this.name;
    data['access_token'] = this.accessToken;
    data['mobile'] = this.mobile;
    return data;
  }
}

第二部分:用數(shù)據(jù)注入組件包裹這個項目入口

void main() => runApp(
    ChangeNotifierProvider(
      builder: (context) => UserInfo(),
      child: MyApp(),
    )
);

第三部分:實現(xiàn)視圖結(jié)構(gòu)顯示

Consumer<UserInfo>(
                    builder: (context,user,child){
                        return Text(
                            user.info != null ? user.info.name : "去登錄"
                        );
                    }
                  )

第四部分:設(shè)置數(shù)據(jù)

Provider.of<UserInfo>(context, listen: true).setInfo(mapUserInfo);

完整主頁代碼_main.dart:

import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; //布局適配庫
import 'package:provider/provider.dart'; //Provider依賴
import 'providers/UserInfo.dart'; //UserInfo全局數(shù)據(jù)源
import 'package:flutter_app2/pages/login.dart'; //登錄頁面
import 'package:shared_preferences/shared_preferences.dart'; //緩存依賴
import 'dart:convert' as Convert; //數(shù)據(jù)轉(zhuǎn)化庫
void main() => runApp(
    ChangeNotifierProvider( //將數(shù)據(jù)注入器加載最外部節(jié)點,代表UserInfo將貫穿整個項目
      builder: (context) => UserInfo(), //注入UserInfo數(shù)據(jù)
      child: MyApp(),
    )
);

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  _HomePage createState()=>_HomePage();
}

class _HomePage extends State<HomePage> {
  @override
  initState() {
    super.initState();
    getUserInfo(); //初始化項目辐董,獲取本地緩存的用戶數(shù)據(jù)
  }
  getUserInfo() async {
    var prefs = await SharedPreferences.getInstance();
    var userInfo = prefs.getString("userInfo");
    var mapUserInfo = Convert.jsonDecode(userInfo); //轉(zhuǎn)化成Map<String Dynamic>類型
    Provider.of<UserInfo>(context, listen: true).setInfo(mapUserInfo);  //設(shè)置全局用戶信息
    //寫到這里我發(fā)現(xiàn)我好想沒有處理找不到本地用戶數(shù)據(jù)的情況悴品,后續(xù)改正
  }
  Widget build(BuildContext context) {
    ScreenUtil.init(context, width: 750, height: 1334); //初始化布局適配參數(shù)
    return Scaffold(
      appBar: AppBar( title: Text("用戶體系"),),
      body: Container(
        child: ListView(
          children: <Widget>[
            Container(
                child:Text("首頁")
            ),
            Center(
              child:Container(
                width: ScreenUtil().setWidth(200),
                height: ScreenUtil().setHeight(200),
                child: RaisedButton(
                  onPressed: ()  {
                    Navigator.push(context, MaterialPageRoute(builder: (context) { //跳轉(zhuǎn)至登錄頁
                      return Login(); 
                    }));
                  },
                  child: Consumer<UserInfo>(
                    builder: (context,user,child){
                        return Text(
                            user.info != null ? user.info.name : "去登錄" //如果用用戶信息顯示名字,如果沒有顯示去登錄
                        );
                    }
                  )
                ),
              )
            )
          ],
        ),
      ),
    );
  }
}

完整登錄頁代碼_Login.dart

import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_app2/common/Request.dart';
import 'package:flutter_app2/providers/UserInfo.dart';
import 'package:flutter_app2/models/UserModel.dart';
import 'dart:convert' as Convert;
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter_app2/widgets/LoadingDialog.dart';
class Login extends StatelessWidget{
 @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar:AppBar( title: Text("登錄"), ),
      body: Container(
        child: RaisedButton(
          onPressed: () async {
            Navigator.of(context).push(MaterialPageRoute(builder: (context){ //請求數(shù)據(jù)打開加載提示框简烘,這里我隨便拿了別人的LoadingDialog
              return LoadingDialog();
            }));
            var responce = await Request().getInstance().get("http://yujun.store/userInfo.php");//請求用戶數(shù)據(jù)苔严,這里用到了await跟js的await一樣,外面的方法體需要用async修飾(本人也會php跟node服務(wù)端語言)
            Map<String, dynamic> userInfo = Convert.jsonDecode(responce.toString());//轉(zhuǎn)化數(shù)據(jù)孤澎,我自己寫的用戶數(shù)據(jù)是標準的JSON届氢,但是還是需要toString之后在jsonDecode
            Provider.of<UserInfo>(context, listen: true).setInfo(userInfo);
            var prefs = await SharedPreferences.getInstance(); //存一遍本地
            prefs.setString("userInfo", Convert.jsonEncode(userInfo)); //存Sring            Navigator.of(context).pop(context);//關(guān)閉加載提示框
          },
          child:  Consumer<UserInfo>(
              builder: (context,user,child){
                return Text(
                    user.info != null ? user.info.name : "去登錄"
                );
              }
          ),
        ),
      ),
    );
  }
}

完整的請求Dio封裝代碼_Request.dart

import 'dart:io';
import 'package:dio/dio.dart';
import 'dart:convert' as Convert;
import 'package:shared_preferences/shared_preferences.dart';
import '../models/auth.dart';
class Request {
  Request _instance;
  Dio dio = new Dio();
  getInstance(){
    if(null == _instance){
      _instance = new Request();
      initial();
    }
    return _instance;
  }
  initial()async{
    dio.options.connectTimeout=10000;
    dio.options.receiveTimeout=10000;
    dio.options.responseType=ResponseType.JSON;
  }
  postFormData(String url, data) async{ //formData數(shù)據(jù)提交
   dio.options.contentType = ContentType.parse("application/x-www-form-urlencoded");
   var response = await post(url,data);
   return response;
  }
  get(String url) async{
    var response = await request('get',url,null);
    return response;
  }
  post(String url, data) async{
    var response = await request('post',url,data);
    return response;
  }
  request(String type,String  url, data) async{
    Response response;
    try{
      if(type == 'get'){
        response = await dio.get(url);
      }else{
        response = await dio.post(url,data: data??{});
      }
      return response;
    }on DioError catch(error){
      print(error);
    }
  }
}

完整的加載提示框代碼_LoadingDialog.dart

import 'package:flutter/material.dart';
class LoadingDialog extends Dialog {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: new Material(
        ///背景透明
        color: Colors.transparent,
        ///保證控件居中效果
        child: new Center(
          ///彈框大小
          child: new SizedBox(
            width: 120.0,
            height: 120.0,
            child: new Container(
              ///彈框背景和圓角
              decoration: ShapeDecoration(
                color: Color(0xffffffff),
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.all(
                    Radius.circular(8.0),
                  ),
                ),
              ),
              child: new Column(
                mainAxisAlignment: MainAxisAlignment.center,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: <Widget>[
                  new CircularProgressIndicator(),
                  new Padding(
                    padding: const EdgeInsets.only(
                      top: 20.0,
                    ),
                    child: new Text(
                      "加載中",
                      style: new TextStyle(fontSize: 16.0),
                    ),
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

providers下的UserInfo.dart

import 'package:flutter/material.dart';
import 'package:flutter_app2/models/UserModel.dart';
class UserInfo extends ChangeNotifier{
  UserModel _info;
  UserModel get info => _info ?? null;
  void setInfo(info){
    _info = UserModel.fromJson(info);
    notifyListeners();
  }
}

我的代碼組織如下:


image.png

項目依賴如下:

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^0.1.2
  shared_preferences: ^0.5.4+8
  dio: ^1.0.9
  provider : ^3.2.0
  flutter_screenutil: ^1.0.1

這樣就實現(xiàn)了一個有緩存功能的全局用戶數(shù)據(jù)共享項目架構(gòu),謝謝閱讀覆旭。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末退子,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子型将,更是在濱河造成了極大的恐慌寂祥,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件七兜,死亡現(xiàn)場離奇詭異丸凭,居然都是意外死亡,警方通過查閱死者的電腦和手機腕铸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進店門惜犀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人狠裹,你說我怎么就攤上這事虽界。” “怎么了涛菠?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵浓恳,是天一觀的道長。 經(jīng)常有香客問我碗暗,道長,這世上最難降的妖魔是什么梢夯? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任言疗,我火速辦了婚禮,結(jié)果婚禮上颂砸,老公的妹妹穿的比我還像新娘噪奄。我一直安慰自己死姚,他們只是感情好,可當我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布勤篮。 她就那樣靜靜地躺著都毒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪碰缔。 梳的紋絲不亂的頭發(fā)上账劲,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天,我揣著相機與錄音金抡,去河邊找鬼瀑焦。 笑死,一個胖子當著我的面吹牛梗肝,可吹牛的內(nèi)容都是我干的榛瓮。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼巫击,長吁一口氣:“原來是場噩夢啊……” “哼禀晓!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起坝锰,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤粹懒,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后什黑,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體崎淳,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年愕把,在試婚紗的時候發(fā)現(xiàn)自己被綠了拣凹。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡恨豁,死狀恐怖嚣镜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情橘蜜,我是刑警寧澤菊匿,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站计福,受9級特大地震影響跌捆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜象颖,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一佩厚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧说订,春花似錦抄瓦、人聲如沸潮瓶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽毯辅。三九已至,卻和暖如春煞额,著一層夾襖步出監(jiān)牢的瞬間思恐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工立镶, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留壁袄,地道東北人。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓媚媒,卻偏偏與公主長得像嗜逻,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子缭召,可洞房花燭夜當晚...
    茶點故事閱讀 45,077評論 2 355

推薦閱讀更多精彩內(nèi)容