繼前篇flutter初探之后沮焕,此次我來聊聊從傳統(tǒng)web項目角度剖析我是怎么一步步搭建flutter基礎(chǔ)項目的弓坞。本文主要講述如何實現(xiàn)flutter用戶數(shù)據(jù)多頁面共享隧甚,用戶數(shù)據(jù)本地緩存。
首先渡冻,我們引入數(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類
你可能會問為啥要轉(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();
}
}
我的代碼組織如下:
項目依賴如下:
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),謝謝閱讀覆旭。