flutter-登录token本地存储(shared_preferences)、路由拦截

2019-11-2308:08:43APP与小程序开发Comments16,455 views字数 5601阅读模式

登录逻辑

添加token

  • 登录成功,保存token到本地,转跳到首页,移除其他栈,防止返回回到登录页面

移除token

  • 未登录 路由拦截找不到token,转跳到登录页面或者弹窗
  • token过期,后台返回token,api拦截,移除token,转跳到登录页面
  • 退出登录移除token,转跳到登录页面,移除前面所有路由栈

本地存储

flutter暂时没有内置本地存储,官方推荐shared_preferences文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/17525.html

shared_preferences: ^0.5.4+1
复制代码
import 'package:shared_preferences/shared_preferences.dart';
复制代码

基本用法

实例下文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/17525.html

final prefs = await SharedPreferences.getInstance();
复制代码
保存

保存字符类型数据setString
保存int类型数据setInt
设置布尔类型数据setBool
设置Double类型数据setDouble文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/17525.html

获取

获取字符类型数据getString
获取int类型数据getInt
获取布尔类型数据getBool
获取Double类型数据getDouble文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/17525.html

移除

移除数据remove文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/17525.html

清空

清空所有数据clear文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/17525.html

(更多更详细请看官方文档,最下面的相关链接里面的SharedPreferences class)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/17525.html

登录成功数据存储(保存登录token)

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('退出登录'),
    ),
 )
复制代码

移除登录的按钮我是写在抽屉组件里面文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/17525.html

flutter自带的drawer组件

抽屉gif

flutter-登录token本地存储(shared_preferences)、路由拦截

Scaffold组件有一个drawer参数, 接收一个widget文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/17525.html

Scaffold(
    drawer: new MyDrawer(),
)
复制代码

MyDrawer文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/17525.html

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('退出登录'),
                    ),
                  )
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}
复制代码

退出登录可以二次确认下文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/17525.html

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,);
          }
        },
      ),
    ],
 );
复制代码

我们实现了登录,和退出,但是未登录还未处理,未登录需要路由拦截文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/17525.html

路由拦截

onGenerateRoute里面可以监听到路由(在routes和home匹配不到的时候才会执行)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/17525.html

  • 把routes的代码删掉(如果有的话)
  • 把home删掉(如果有的话)
MaterialApp(
      ...代码
      // home: IndexHome(),
      onGenerateRoute: onGenerateRoute,
      // routes: routes,
);
复制代码

新建一个onGenerateRoute函数

Route<dynamic> onGenerateRoute(RouteSettings setting) {
 if(setting.name == 路由名称){
    return aterialPageRoute(builder: (context) => Widget);
 }
 return MaterialPageRoute(builder: (BuildContext context) => Container(child: Text('404'),));
}
复制代码
  • 在onGenerateRoute函数里面加入如下代码,把routes写进入
  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;
  }
复制代码

这时候, 我们在里面加一个钩子管理就可以了文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/17525.html

路由实现完整代码

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函数,然后突然发现,本地存储是异步的。这就很惆怅了,这时候只能用路由转跳返回到登录页面。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/17525.html

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,);
  }
}
复制代码

context全局化

Navigator需要依赖context,这时候没context或者context在顶层,就会出问题,这时候可以使用全局的key。
在路由拦截和Api拦截中都可以用到文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/17525.html

final GlobalKey<NavigatorState> navigatorKey = new GlobalKey<NavigatorState>();
复制代码

MaterialApp下面的navigatorKey文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/17525.html

navigatorKey: navigatorKey,
复制代码

context.currentState等同于Navigator.of(context) 如转跳到登录页文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/17525.html

context.currentState.pushNamedAndRemoveUntil(LoginPath, (route) => route == null,);
复制代码

其他

牛刀小试

功能写完了,可以牛刀小试了文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/17525.html

  @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)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/17525.html

作者:秀岳lonelyBoy
链接:https://juejin.im/post/5db6d6f16fb9a0206965a0f3
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/17525.html

文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/17525.html
  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/xcx/17525.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定