登陸token的處理,數據本地存儲,路由攔截html
續flutter - 登錄界面&表單校驗,登陸後的處理api
flutter暫時沒有內置本地存儲,官方推薦shared_preferences
bash
shared_preferences: ^0.5.4+1
複製代碼
import 'package:shared_preferences/shared_preferences.dart';
複製代碼
實例下less
final prefs = await SharedPreferences.getInstance();
複製代碼
保存字符類型數據setString
保存int類型數據setInt
設置布爾類型數據setBool
設置Double類型數據setDouble
異步
獲取字符類型數據getString
獲取int類型數據getInt
獲取布爾類型數據getBool
獲取Double類型數據getDouble
async
移除數據remove
編輯器
清空全部數據clear
ide
(更多更詳細請看官方文檔,最下面的相關連接裏面的SharedPreferences class
)函數
Map<String, Object> data = response_map['data'];
final prefs = await SharedPreferences.getInstance();
final setTokenResult = await prefs.setString('user_token', data['token']);
await prefs.setInt('user_phone', data['phone']);
await prefs.setString('user_phone', data['name']);
if(setTokenResult){
debugPrint('保存登陸token成功');
Navigator.of(context).pushNamedAndRemoveUntil('/', (route) => route == null,);
}else{
debugPrint('error, 保存登陸token失敗');
}
複製代碼
handleToken() async{
final prefs = await SharedPreferences.getInstance();
final token = prefs.getString('user_token') ?? '';
debugPrint('user_token: $token');
if(token == ''){
Navigator.of(context).pushNamedAndRemoveUntil('/login', (route) => route == null,);
}
}
複製代碼
GestureDetector(
onTap: () async{
final prefs = await SharedPreferences.getInstance();
final result = await prefs.clear();
if(result){
debugPrint('退出登陸成功');
Navigator.of(context).pushNamedAndRemoveUntil('/login', (route) => route == null,);
}
},
child: ListTile(
leading: const Icon(Icons.outlined_flag),
title: const Text('退出登陸'),
),
)
複製代碼
移除登陸的按鈕我是寫在抽屜組件裏面post
drawer
組件Scaffold
組件有一個
drawer
參數, 接收一個widget
Scaffold(
drawer: new MyDrawer(),
)
複製代碼
MyDrawer
class MyDrawer extends StatelessWidget {
const MyDrawer({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Drawer(
child: MediaQuery.removePadding(
context: context,
removeTop: true,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(height: 100,),
Expanded(
child: ListView(
children: <Widget>[
GestureDetector(
onTap: () async{
final prefs = await SharedPreferences.getInstance();
final result = await prefs.clear();
if(result){
debugPrint('退出登陸成功');
Navigator.of(context).pushNamedAndRemoveUntil('/login', (route) => route == null,);
}
},
child: ListTile(
leading: const Icon(Icons.outlined_flag),
title: const Text('退出登陸'),
),
)
],
),
),
],
),
),
);
}
}
複製代碼
退出登陸能夠二次確認下
AlertDialog(
content: Text('是否確認退出登陸?'),
actions: <Widget>[
FlatButton(
child: Text('取消'),
onPressed: () {
debugPrint('取消');
Navigator.pop(context);
},
),
FlatButton(
child: Text('確認'),
onPressed: () async{
debugPrint('確認退出');
Navigator.pop(context);
final prefs = await SharedPreferences.getInstance();
final result = await prefs.clear();
if(result){
debugPrint('退出登陸成功');
Navigator.of(context).pushNamedAndRemoveUntil('/login', (route) => route == null,);
}
},
),
],
);
複製代碼
咱們實現了登陸,和退出,可是未登陸還未處理,未登陸須要路由攔截
onGenerateRoute
裏面能夠監聽到路由(在routes和home匹配不到的時候纔會執行)
MaterialApp(
...代碼
// home: IndexHome(),
onGenerateRoute: onGenerateRoute,
// routes: routes,
);
複製代碼
Route<dynamic> onGenerateRoute(RouteSettings setting) {
if(setting.name == 路由名稱){
return aterialPageRoute(builder: (context) => Widget);
}
return MaterialPageRoute(builder: (BuildContext context) => Container(child: Text('404'),));
}
複製代碼
Map<String, Widget> routes = {
你的路由
};
bool mathMap = false;
Route<dynamic> mathWidget;
routes.forEach((key, v){
if(key == setting.name){
mathMap = true;
mathWidget = MaterialPageRoute(builder: (BuildContext context) => v);
}
});
if(mathMap){
return mathWidget;
}
複製代碼
這時候, 咱們在裏面加一個鉤子管理就能夠了
Route<dynamic> onGenerateRoute(RouteSettings setting) {
routeHook(setting);
Map<String, Widget> routes = {
你的路由
};
bool mathMap = false;
Route<dynamic> mathWidget;
routes.forEach((key, v){
if(key == setting.name){
mathMap = true;
mathWidget = MaterialPageRoute(builder: (BuildContext context) => v);
}
});
if(mathMap){
return mathWidget;
}
return MaterialPageRoute(builder: (BuildContext context) => Container(child: Text('404'),));
}
複製代碼
這時候咱們須要在裏面寫鉤子,這個就比較複雜了,我原本打開寫一個beforeHook,接收一個next函數,而後忽然發現,本地存儲是異步的。這就很惆悵了,這時候只能用路由轉跳返回到登陸頁面。
Navigator.of(context).pushNamedAndRemoveUntil('/login', (route) => route == null,);
複製代碼
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
routeBeforeHook(RouteSettings setting, navigatorKey) async{
String LoginPath = '/login';
if(setting.name == LoginPath || setting.name == '/rigister'){
return;
}
final prefs = await SharedPreferences.getInstance();
final token = prefs.getString('user_token') ?? '';
if(token == ''){
navigatorKey.currentState.pushNamedAndRemoveUntil(LoginPath, (route) => route == null,);
}
}
複製代碼
Navigator須要依賴context
,這時候沒context
或者context
在頂層,就會出問題,這時候可使用全局的key。
在路由攔截和Api攔截中均可以用到
final GlobalKey<NavigatorState> navigatorKey = new GlobalKey<NavigatorState>();
複製代碼
MaterialApp下面的navigatorKey
navigatorKey: navigatorKey,
複製代碼
context.currentState
等同於Navigator.of(context)
如轉跳到登陸頁
context.currentState.pushNamedAndRemoveUntil(LoginPath, (route) => route == null,);
複製代碼
功能寫完了,能夠牛刀小試了
@override
void initState() {
setUserData();
super.initState();
}
setUserData() async{
final prefs = await SharedPreferences.getInstance();
setState(() {
user_name = prefs.getString('user_name') ?? '';
phone = (prefs.getInt('user_phone') ?? 0).toString();
});
}
複製代碼
引入的時候出現了小插曲
Target of URI doesn't exist: 'package:shared_preferences/shared_preferences.dart'. Try creating the file referenced by the URI, or Try using a URI for a file that does exist.dart(uri_does_not_exist)
我從.packages
文件找到shared_preferences的目錄,而後找到pubspec.yaml
,看了下name,是shared_preferences
,lib文件下也有shared_preferences
文件,這時候就奇怪了, 怎麼會找不到呢。
(而後想不到問題,有多是編輯器的問題,因而重啓,就沒事了QAQ)
flutter - 登錄界面&表單校驗
flutter - 圖文講解表單組件基本使用 & 註冊實戰
SharedPreferences class