继前篇flutter初探之后,此次我来聊聊从传统web项目角度剖析我是怎么一步步搭建flutter基础项目的。本文主要讲述如何实现flutter用户数据多页面共享,用户数据本地缓存。
首先,我们引入数据共享组件Provider https://flutter.cn/docs/development/data-and-backend/state-mgmt/intro
我们用到的核心模块有:
1.ChangeNotifier ——共享数据源头部(数据存储、数据传播)
2.ChangeNotifierProvider ——共享数据视图层注入组件
3.Consumer ——共享数据作用节点
我们用到的核心方法有:
1.Provider.of
下面开始实现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类
你可能会问为啥要转类型,我一开始觉得没必要,可是当我使用返回的数据,直接拿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();
}
}
我的代码组织如下:
项目依赖如下:
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
这样就实现了一个有缓存功能的全局用户数据共享项目架构,谢谢阅读。