Flutter 路由懒加载,动态路由,按需加载。路由没有懒加载

Flutter 路由懒加载,动态路由,按需加载

  • 如下配置后,即可实现路由懒加载

如未实现懒加载:请检查RoutePath中是否定义了static const String XXX = ‘/’
原因:MaterialApp设置了路由后,会先访问一次’/‘地址,再访问initialRoute设置的路由。
我之前设置了static const String tab = ‘/’,但是tab中会从服务器拿数据,又做了登录校验,导致每次启动都被拦截。
所以做了登录校验后,’/'只能为登录页或不被拦截的页面。

入口文件

日志:pretty_dio_logger: ^1.4.0


  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      navigatorKey: RouteUtils.navigatorKey,
      // 懒加载,动态路由,按需加载
      onGenerateRoute: Routes.generateRoute,
      // 初始页面
      initialRoute: RoutePath.launch,
    );
  }

路由管理类

// 路由管理类
class Routes {
  static Route<dynamic> generateRoute(RouteSettings settings) {
    switch (settings.name) {
      case RoutePath.launch:
        return pageRoute(const LaunchPage(), settings: settings);
      case RoutePath.login:
        return pageRoute(const LoginPage(), settings: settings);
      case RoutePath.tab:
        return pageRoute(const TabPage(), settings: settings);
    }

    return MaterialPageRoute(
        builder: (context) => Scaffold(
            body:
                Center(child: Text('No route defined for ${settings.name}'))));
  }

  static MaterialPageRoute pageRoute(
    Widget page, {
    RouteSettings? settings,
    bool? fullscreenDialog,
    bool? maintainState,
    bool? allowSnapshotting,
  }) {
    return MaterialPageRoute(
        builder: (context) => page,
        settings: settings,
        fullscreenDialog: fullscreenDialog ?? false,
        maintainState: maintainState ?? true,
        allowSnapshotting: allowSnapshotting ?? true);
  }
}

/// 路由路径
class RoutePath {
  /// 启动页
  static const String launch = '/launch';

  /// 首页
  static const String tab = '/tab';

  /// 登录
  static const String login = '/login';
}

路由工具类

/// 路由工具类
class RouteUtils {
  RouteUtils._();

  static final navigatorKey = GlobalKey<NavigatorState>();

  // App 根节点Context
  static BuildContext get context => navigatorKey.currentContext!;

  static NavigatorState get navigator => navigatorKey.currentState!;

  /// 普通动态跳转-->page
  static Future push(
    BuildContext context,
    Widget page, {
    bool? fullscreenDialog,
    RouteSettings? settings,
    bool maintainState = true,
  }) {
    return Navigator.push(
        context,
        MaterialPageRoute(
          builder: (_) => page,
          fullscreenDialog: fullscreenDialog ?? false,
          settings: settings,
          maintainState: maintainState,
        ));
  }

  /// 根据路由路径跳转
  static Future pushForNamed(
    BuildContext context,
    String name, {
    Object? arguments,
  }) {
    return Navigator.pushNamed(context, name, arguments: arguments);
  }

  /// 自定义route动态跳转
  static Future pushForPageRoute(BuildContext context, Route route) {
    return Navigator.push(context, route);
  }

  /// 清空栈,只留目标页面
  static Future pushNamedAndRemoveUntil(
    BuildContext context,
    String name, {
    Object? arguments,
  }) {
    return Navigator.pushNamedAndRemoveUntil(context, name, (route) => false,
        arguments: arguments);
  }

  /// 清空栈,只留目标页面
  static Future pushAndRemoveUntil(
    BuildContext context,
    Widget page, {
    bool? fullscreenDialog,
    RouteSettings? settings,
    bool maintainState = true,
  }) {
    return Navigator.pushAndRemoveUntil(
        context,
        MaterialPageRoute(
          builder: (_) => page,
          fullscreenDialog: fullscreenDialog ?? false,
          settings: settings,
          maintainState: maintainState,
        ),
        (route) => false);
  }

  /// 用新的路由替换当路由
  static Future pushReplacement(BuildContext context, Route route,
      {Object? result}) {
    return Navigator.pushReplacement(context, route, result: result);
  }

  /// 用新的路由替换当路由
  static Future pushReplacementNamed(
    BuildContext context,
    String name, {
    Object? result,
    Object? arguments,
  }) {
    return Navigator.pushReplacementNamed(context, name,
        arguments: arguments, result: result);
  }

  /// 关闭当前页面
  static void pop(BuildContext context) {
    Navigator.pop(context);
  }

  /// 关闭当前页面:包含返回值
  static void popOfData<T extends Object?>(BuildContext context, {T? data}) {
    Navigator.of(context).pop(data);
  }
}

DIO拦截器

DIO:dio: ^5.7.0

/// 自定义拦截器
class CustomInterceptors extends Interceptor {
  /// 请求拦截
  
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    // http header 头加入 Authorization
    // final token = tokenStorage.getAccessToken() as String;
    // options.headers['Authorization'] = 'Bearer $token';

    handler.next(options);
  }

  /// 响应拦截
  
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    // 200 请求成功
    if (response.statusCode != 200 || response.data['code'] != 200) {
      handler.reject(
        DioException(
          requestOptions: response.requestOptions,
          response: response,
          type: DioExceptionType.badResponse,
        ),
        true,
      );
    } else {
      // 截取response.data
      response.data = response.data['data'];
      handler.next(response);
    }
  }

  // // 退出并重新登录
  // Future<void> _errorNoAuthLogout() async {
  //   await UserService.to.logout();
  //   IMService.to.logout();
  //   Get.toNamed(RouteNames.systemLogin);
  // }

  /// 错误拦截
  
  Future<void> onError(
      DioException err, ErrorInterceptorHandler handler) async {
    final exception = HttpException(err.message ?? "error message");
    switch (err.type) {
      case DioExceptionType.badResponse: // 服务端自定义错误体处理
        {
          final response = err.response;
          final errorMessage = ErrorMessageModel.fromJson(response?.data);
          final code = response!.data['code'];
          switch (code) {
            // 401 未登录
            case 401:
              consoleLog(msg: errorMessage.message ?? '401 未登录');
              // 刷新令牌校验
              // _refreshToken(err, handler);
              // 注销 并跳转到登录页面
              PPC.toLaunch();
              // _errorNoAuthLogout();
              break;
            case 404:
              break;
            case 500:
              break;
            case 502:
              break;
            default:
              break;
          }
          // 显示错误信息
          // if(errorMessage.message != null){
          //   Loading.error(errorMessage.message);
          // }
        }
        break;
      case DioExceptionType.unknown:
        consoleLog(msg: '未知错误');
        break;
      case DioExceptionType.cancel:
        consoleLog(msg: '取消请求');
        break;
      case DioExceptionType.connectionTimeout:
        consoleLog(msg: '连接超时');
        break;
      default:
        break;
    }
    DioException errNext = err.copyWith(
      error: exception,
    );
    handler.next(errNext);
  }

  void _refreshToken(DioException err, ErrorInterceptorHandler handler) async {
    // 创建新Dio实例,避免循环引用
    final dio = Dio();
    // 刷新令牌地址
    const path = BASE_URL + REFRESH_TOKEN_URL;
    try {
      String refreshToken = await tokenStorage.getRefreshToken();
      final resp = await dio.post(
        path,
        data: {'refreshToken': refreshToken},
      );
      consoleLog(msg: '刷新令牌');
      // 更新访问令牌和刷新令牌
      final accessToken = resp.data['accessToken'] as String;
      tokenStorage.setToken(
          resp.data['accessToken'], resp.data['refreshToken']);
      // 重试原请求
      final opts = err.requestOptions;
      opts.headers['Authorization'] = 'Bearer $accessToken';
      final cloneReq = await dio.fetch(opts);
      return handler.resolve(cloneReq);
    } catch (e) {
      consoleLog(msg: '刷新令牌出错!');
      consoleLog(msg: e.toString());
    }
  }

  /// 日志打印
  void consoleLog({String msg = ''}) {
    log('dio_interceptors -- $msg');
  }
}

Token管理类

三方库:flutter_secure_storage: ^9.2.2

import 'package:flutter_secure_storage/flutter_secure_storage.dart';

/// Token管理类
class TokenStorage {
  static final TokenStorage instance = TokenStorage.init();

  factory TokenStorage() {
    return instance;
  }

  TokenStorage.init();

  static const _instance = FlutterSecureStorage();

  Future<void> setAccessToken(String accessToken) async {
    await _instance.write(key: 'accessToken', value: accessToken);
  }

  Future<void> setToken(String accessToken, String refreshToken) async {
    await _instance.write(key: 'accessToken', value: accessToken);
    await _instance.write(key: 'refreshToken', value: refreshToken);
  }

  Future<String> getAccessToken() async {
    String token = await _instance.read(key: 'accessToken') ?? '';
    return token;
  }

  Future<String> getRefreshToken() async {
    String token = await _instance.read(key: 'refreshToken') ?? '';
    return token;
  }

  Future<void> deleteAllTokens() async {
    await _instance.deleteAll();
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值