flutter dio token过期/未登录 自动跳转

首先封装了dio,可以去看我之前的文章,里面有添加拦截器,这是关键。

token拦截器

import 'package:dio/dio.dart';
import 'package:cserp/datautil.dart';
import 'package:cserp/login.dart';
//QueuedInterceptors是队列,不会造成并发进入拦截器
class TokenInterceptor extends QueuedInterceptorsWrapper {
  @override
  void onRequest(
      RequestOptions options, RequestInterceptorHandler handler) async {
    // print('REQUEST[${options.method}] => PATH: ${options.path}');

    if (!options.headers.containsKey('Authorization')) {
      String? token = await getToken();
      if (token == null) {
        //走到这里,就代表这handler里没有Authorization这个字段,并且本地也没有token,所以弹出登录页面,我们会在showLoginPage里 handler.next(options);
        showLoginPage(options: options, handler: handler);
        return;
      }
      options.headers['Authorization'] = await getToken();
      return handler.next(options);
    }
    options.headers['Authorization'] = await getToken();
    return handler.next(options);
  }

  // @override
  // void onResponse(Response response, ResponseInterceptorHandler handler) {
  //   print(
  //       'RESPONSE[${response.statusCode}] => PATH: ${response.requestOptions.path}');
  //   super.onResponse(response, handler);
  // }
  @override
  Future onError(DioException err, ErrorInterceptorHandler handler) async {
    // print(
    //     'ERROR[${err.response?.statusCode}] => PATH: ${err.requestOptions.path}');
    if (err.response?.statusCode == 401) {
     //这里是发出的请求401了,需要登录,所以跳出登录页面,我们也在showLoginPage里return handler.next(err);
      showLoginPage(options: err.requestOptions, handler: handler);
      return;
    }
    return handler.next(err);
  }
}

 

showLoginPage():

import 'package:flutter/cupertino.dart';
import 'package:cserp/global.dart';
import 'package:flutter/material.dart';
import 'package:cserp/http.dart';
import 'package:dio/dio.dart';
import 'package:cserp/datautil.dart';
//重要的是这个方法,handler用的泛型,因为可能是RequestInterceptorHandler或者ErrorInterceptorHandler
showLoginPage<T>({RequestOptions? options, T? handler}) {
  late TextEditingController usernameController = TextEditingController();
  late TextEditingController passwordController = TextEditingController();
  Future login() async {
//发起登录请求,
    await loginReq(usernameController.text, passwordController.text,
        success: (data) async {
      if (data.data!.token != null) {
        await setToken(data.data!.token.toString());
        BuildContext context = Global.navigatorKey.currentState!.context;
        if (context.mounted) {
          Navigator.of(context).pop();
        }
      }
//这里是重中之重,
      if (handler != null && options != null) {
        if (handler is RequestInterceptorHandler) {
          options.headers['Authorization'] = await getToken();
       //到这里代表着,登录已经完毕,也已经获取到了token,然后把token塞到headers里,继续下一个拦截器,直到发起请求
          return handler.next(options);
        } else if (handler is ErrorInterceptorHandler) {
//发起玩请求了返回来需要登录,然后现在已经登录完毕了,再把token塞到header里
          options.headers['Authorization'] = await getToken();
          final originResult = await dio.fetch(options);
//最后再重新发起这个请求
          return handler.resolve(originResult);
        }
      }
    });
  }
//这些不重要,每个人的登录界面都不一样
  showCupertinoModalPopup<void>(
      context: Global.navigatorKey.currentState!.context,
      builder: (BuildContext context) => CupertinoPopupSurface(
              child: Container(
            color: CupertinoColors.white,
            width: double.infinity,
            height: MediaQuery.of(context).size.height - 50,
            child: Padding(
              padding: const EdgeInsets.only(left: 10, right: 10),
              child: Column(
                children: <Widget>[
                  Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: <Widget>[
                      CupertinoButton(
                        padding: EdgeInsets.all(0),
                        onPressed: () {
                          Navigator.of(context).pop();
                        },
                        child: const Text('取消', textAlign: TextAlign.left),
                      ),
                      CupertinoButton(
                        padding: EdgeInsets.all(0),
                        onPressed: () => login(),
                        child: const Text('登录', textAlign: TextAlign.right),
                      ),
                    ],
                  ),
                  Text("登录"),
                  Text("重生ERP"),
                  Column(
                    children: <Widget>[
                      Container(
                        padding: EdgeInsets.all(10),
                        decoration: BoxDecoration(
                          borderRadius: BorderRadius.only(
                              topLeft: Radius.circular(10),
                              topRight: Radius.circular(10)),
                          color: CupertinoColors.lightBackgroundGray,
                        ),
                        child: CupertinoTextField(
                          controller: usernameController,
                          prefix: Padding(
                            padding: const EdgeInsets.only(left: 10, right: 10),
                            child: Text("账号"),
                          ),
                          keyboardType: TextInputType.text,
                          textInputAction: TextInputAction.next,
                          decoration: BoxDecoration(
                            color: CupertinoColors.lightBackgroundGray,
                            border: Border(),
                          ),
                        ),
                      ),
                      Divider(
                        height: 0,
                        color: CupertinoColors.inactiveGray,
                      ),
                      Container(
                        padding: EdgeInsets.all(10),
                        decoration: BoxDecoration(
                          borderRadius: BorderRadius.only(
                              bottomLeft: Radius.circular(10),
                              bottomRight: Radius.circular(10)),
                          color: CupertinoColors.lightBackgroundGray,
                        ),
                        child: CupertinoTextField(
                          controller: passwordController,
                          prefix: Padding(
                            padding: const EdgeInsets.only(left: 10, right: 10),
                            child: Text("密码"),
                          ),
                          keyboardType: TextInputType.text,
                          obscureText: true,
                          textInputAction: TextInputAction.done,
                          onEditingComplete: () => login(),
                          decoration: BoxDecoration(
                            color: CupertinoColors.lightBackgroundGray,
                            border: Border(),
                          ),
                        ),
                      ),
                    ],
                  ),
                ],
              ),
            ),

            // child: CupertinoPageScaffold(
            //     navigationBar: CupertinoNavigationBar(
            //       // Try removing opacity to observe the lack of a blur effect and of sliding content.
            //       //   backgroundColor: CupertinoColors.systemGrey.withOpacity(0.5),
            //       // middle: const Text('CupertinoNavigationBar Sample'),
            //       leading: Text("取消"),
            //       // This title is visible in both collapsed and expanded states.
            //       // When the "middle" parameter is omitted, the widget provided
            //       // in the "largeTitle" parameter is used instead in the collapsed state.

            //       trailing: Text("登录"),
            //       backgroundColor: CupertinoColors.white,
            //     ),
            //     // A ScrollView that creates custom scroll effects using slivers.
            //     child: Container()),
          )));
}

 

我的dio封装代码

import 'package:dio/dio.dart';
import 'package:cserp/token_interceptor.dart';
import 'package:cserp/err_interceptor.dart';
import 'package:cserp/result/login_result.dart';

final BaseOptions _baseOption = BaseOptions(
    connectTimeout: Duration(seconds: 3),
    baseUrl: "http://192.168.50.13/erp/",
    contentType: "application/json; charset=utf-8");

final dio = Dio(_baseOption);
final lgoinDio = Dio(_baseOption);

dioInit() {
  dio.interceptors
    ..add(TokenInterceptor())
    ..add(ErrInterceptor());
  lgoinDio.interceptors.add(ErrInterceptor());
}

Future<Response> post(String url, {String? data, Function? success}) async {
  Response response =
      await dio.request(url, data: data, options: Options(method: "POST"));
  if (success != null) {
    success();
  }
  return response;
}

Future<Response?> loginReq(String username, String password,
    {Function(LoginResult)? success}) async {
  Response response = await lgoinDio
      .post("/user/login", data: {"username": username, "password": password});

  LoginResult lr = LoginResult.fromJson(response.data);
  if (success != null) {
    success(lr);
  }
  return response;
}

 

ErrInterceptor代码

import 'package:dio/dio.dart';
import 'package:flutter/cupertino.dart';
import 'package:cserp/global.dart';

class ErrInterceptor extends QueuedInterceptor {
  @override
  Future onError(DioException err, ErrorInterceptorHandler handler) async {
    if (err.response?.statusCode == 500) {
      showCupertinoModalPopup<void>(
        context: Global.navigatorKey.currentState!.context,
        builder: (BuildContext context) => CupertinoAlertDialog(
          title: Text("提示"),
          content: Text(err.response!.data['msg']),
          actions: <CupertinoDialogAction>[
            CupertinoDialogAction(
              /// This parameter indicates this action is the default,
              /// and turns the action's text to bold text.
              isDefaultAction: true,
              onPressed: () {
                Navigator.pop(context);
                return handler.reject(
                  DioException(requestOptions: err.requestOptions),
                );
              },
              child: const Text('好'),
            ),
          ],
        ),
      );
    }
    return handler.next(err);
  }
}

 

本文章为原创,转载请标明出处

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注