Flutter构建带缓存机制的全局用户登录信息架构

继前篇flutter初探之后,此次我来聊聊从传统web项目角度剖析我是怎么一步步搭建flutter基础项目的。本文主要讲述如何实现flutter用户数据多页面共享,用户数据本地缓存。

image.png

首先,我们引入数据共享组件Provider https://flutter.cn/docs/development/data-and-backend/state-mgmt/intro
我们用到的核心模块有:
1.ChangeNotifier ——共享数据源头部(数据存储、数据传播)
2.ChangeNotifierProvider ——共享数据视图层注入组件
3.Consumer ——共享数据作用节点
我们用到的核心方法有:
1.Provider.of(context, listen: true).Fuc

下面开始实现userInfo数据共享
第一部分:构建数据源,也就是基于ChangeNotifier的UserInfo类

import 'package:flutter/material.dart'; //ChangeNotifier需要material素材库
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();
  }
}

利用https://javiercbk.github.io/json_to_dart/在线工具可快速将后端返回的JSON数据转化成Dart JSON类

image.png

你可能会问为啥要转类型,我一开始觉得没必要,可是当我使用返回的数据,直接拿access_token参数的时候,就报错了~转换一下,可以规避这种特殊字符报错问题。

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

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

  UserModel.fromJson(Map json) {
    name = json['name'];
    accessToken = json['access_token'];
    mobile = json['mobile'];
  }

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

第二部分:用数据注入组件包裹这个项目入口

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

第三部分:实现视图结构显示

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

第四部分:设置数据

Provider.of(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全局数据源
import 'package:flutter_app2/pages/login.dart'; //登录页面
import 'package:shared_preferences/shared_preferences.dart'; //缓存依赖
import 'dart:convert' as Convert; //数据转化库
void main() => runApp(
    ChangeNotifierProvider( //将数据注入器加载最外部节点,代表UserInfo将贯穿整个项目
      builder: (context) => UserInfo(), //注入UserInfo数据
      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 {
  @override
  initState() {
    super.initState();
    getUserInfo(); //初始化项目,获取本地缓存的用户数据
  }
  getUserInfo() async {
    var prefs = await SharedPreferences.getInstance();
    var userInfo = prefs.getString("userInfo");
    var mapUserInfo = Convert.jsonDecode(userInfo); //转化成Map类型
    Provider.of(context, listen: true).setInfo(mapUserInfo);  //设置全局用户信息
    //写到这里我发现我好想没有处理找不到本地用户数据的情况,后续改正
  }
  Widget build(BuildContext context) {
    ScreenUtil.init(context, width: 750, height: 1334); //初始化布局适配参数
    return Scaffold(
      appBar: AppBar( title: Text("用户体系"),),
      body: Container(
        child: ListView(
          children: [
            Container(
                child:Text("首页")
            ),
            Center(
              child:Container(
                width: ScreenUtil().setWidth(200),
                height: ScreenUtil().setHeight(200),
                child: RaisedButton(
                  onPressed: ()  {
                    Navigator.push(context, MaterialPageRoute(builder: (context) { //跳转至登录页
                      return Login(); 
                    }));
                  },
                  child: Consumer(
                    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){ //请求数据打开加载提示框,这里我随便拿了别人的LoadingDialog
              return LoadingDialog();
            }));
            var responce = await Request().getInstance().get("http://yujun.store/userInfo.php");//请求用户数据,这里用到了await跟js的await一样,外面的方法体需要用async修饰(本人也会php跟node服务端语言)
            Map userInfo = Convert.jsonDecode(responce.toString());//转化数据,我自己写的用户数据是标准的JSON,但是还是需要toString之后在jsonDecode
            Provider.of(context, listen: true).setInfo(userInfo);
            var prefs = await SharedPreferences.getInstance(); //存一遍本地
            prefs.setString("userInfo", Convert.jsonEncode(userInfo)); //存Sring            Navigator.of(context).pop(context);//关闭加载提示框
          },
          child:  Consumer(
              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数据提交
   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: [
                  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

这样就实现了一个有缓存功能的全局用户数据共享项目架构,谢谢阅读。

你可能感兴趣的:(Flutter构建带缓存机制的全局用户登录信息架构)