因为随着开发时间越来越长, 对于很多东西又有了新的理解, 是时候回头对于某些东西进行查漏补缺了

本篇就来补一补路由的东西

GlobalKey

这东西要单独说一说, 设置一下这东西, 你的 WidgetsApp 会把它设置给 Navigator, 这东西一旦设置成功, 后续就可以不用 Navigator.of, 拿到 NavigatorState 的实例了

然后这东西设置成全局的, 就可以在今后跳转的时候不用 context 了, 并且可以利用这个东西里的 context 来拿到全局的 Provider 类(这个不在本篇预计范围内)

设置:

MaterialApp(
    navigatorKey: navigatorKey,
);

使用:

NavigatorState get navigator => navigatorKey.currentState;

RouteHelper

全文如下, 但是有一个问题, 这个 navigatorKey 的 context 不能用于 showDialog, 所以需要自定义 dialog 的 route,才可以无 context 调用, 所以加了新的方法

同样的, 以前的 helper 中 push 才需要 context,pop 不需要考虑太多,所以没有 pop 方法, 现在因为有了全局 navigatorKey 来做路由, 所以添加了 pop 相关的方法

import 'package:flutter/material.dart';

import 'dialog_route.dart';

class RouteHelper {
  GlobalKey<NavigatorState> stateKey;

  RouteHelper(this.stateKey);

  NavigatorState get navigator => stateKey.currentState;

  AnimUtils anim = AnimUtils();

  Future<T> pushWidget<T>(
    Widget widget, {
    bool replaceRoot = false,
    bool replaceCurrent = false,
    dynamic replaceResult,
  }) {
    final materialRoute = MaterialPageRoute<T>(builder: (BuildContext context) {
      return widget;
    });
    return pushRoute<T>(
      materialRoute,
      replaceRoot: replaceRoot,
      replaceCurrent: replaceCurrent,
      replaceResult: replaceResult,
    );
  }

  Future<T> pushRoute<T>(
    Route<T> newRoute, {
    bool replaceRoot = false,
    bool replaceCurrent = false,
    dynamic replaceResult,
  }) async {
    assert(!(replaceCurrent && replaceRoot), "这两货只有一个可以为true");
    if (replaceRoot) {
      while (true) {
        if (!(await navigator.maybePop())) {
          break;
        }
      }
      return navigator.pushReplacement<T, dynamic>(
        newRoute,
        result: replaceResult,
      );
    }
    if (replaceCurrent) {
      return navigator.pushReplacement<T, dynamic>(
        newRoute,
        result: replaceResult,
      );
    }
    return navigator.push<T>(newRoute);
  }

  Future<T> showDialog<T>(
    Widget dialog, {
    TransitionsWidgetBuilder transitionsWidgetBuilder,
    TransitionsWidgetBuilder pageBuilder,
    Duration transitionsDuration = const Duration(milliseconds: 300),
  }) {
    // pageBuilder ??= anim.opacity;
    pageBuilder ??= anim.empty;
    transitionsWidgetBuilder ??= anim.opacity;
    return pushRoute<T>(
      DialogRoute<T>(
        child: dialog,
        pageBuilder: pageBuilder,
        transitionsWidgetBuilder: transitionsWidgetBuilder,
        transitionDuration: transitionsDuration,
      ),
    );
  }

  bool pop<T>([T result]) {
    return navigator.pop(result);
  }

  Future<bool> maybePop<T>([T result]) async {
    return navigator.maybePop(result);
  }
}

class AnimUtils {
  Widget opacity(BuildContext context, Animation<double> animation,
      Animation<double> secondaryAnimation, Widget child) {
    return AnimatedBuilder(
      builder: (BuildContext context, Widget child) {
        return Opacity(
          opacity: animation.value,
          child: child,
        );
      },
      animation: animation,
      child: child,
    );
  }

  Widget empty(BuildContext context, Animation<double> animation,
      Animation<double> secondaryAnimation, Widget child) {
    return AnimatedBuilder(
      builder: (BuildContext context, Widget child) {
        return child;
      },
      animation: animation,
      child: child,
    );
  }
}

对应的一个关联类, 内部是 dialog 的 route 动画等东西,dialog_route.dart

import 'package:flutter/material.dart';

typedef Widget DialogWidgetBuilder(
  BuildContext context,
  Animation<double> animation,
  Animation<double> secondaryAnimation,
  Widget child,
);

typedef Widget TransitionsWidgetBuilder(
  BuildContext context,
  Animation<double> animation,
  Animation<double> secondaryAnimation,
  Widget child,
);

class DialogRoute<T> extends PopupRoute<T> {
  final DialogWidgetBuilder pageBuilder;
  final TransitionsWidgetBuilder transitionsWidgetBuilder;
  final Duration transitionDuration;
  final bool maintainState;
  final bool barrierDismissible;
  final Widget child;

  DialogRoute({
    this.pageBuilder,
    this.maintainState = true,
    this.transitionDuration = const Duration(milliseconds: 250),
    this.barrierDismissible = true,
    this.child,
    this.transitionsWidgetBuilder,
  }) : assert(!(pageBuilder == null && child == null));

  @override
  Color get barrierColor => Colors.black38;

  @override
  String get barrierLabel => "title";

  @override
  Widget buildPage(BuildContext context, Animation<double> animation,
      Animation<double> secondaryAnimation) {
    if (pageBuilder == null) {
      return child;
    }
    return pageBuilder(context, animation, secondaryAnimation, child);
  }

  @override
  Widget buildTransitions(BuildContext context, Animation<double> animation,
      Animation<double> secondaryAnimation, Widget child) {
    if (transitionsWidgetBuilder == null) {
      return super
          .buildTransitions(context, animation, secondaryAnimation, child);
    }
    return transitionsWidgetBuilder(
        context, animation, secondaryAnimation, child);
  }
}

mixin TransitionsMixin {
  Widget buildTransitions(BuildContext context, Animation<double> animation,
      Animation<double> secondaryAnimation, Widget child);

  Duration transitionsDuration() => Duration(milliseconds: 300);
}

mixin TransitionsWidgetMixin on Widget, TransitionsMixin {}

mixin TransitionsStateMixin on State, TransitionsMixin {}

BaseProvider

还有一篇不得不一起说的东西: 项目中所有提供者的基类, 这个类中会封装一些项目可能用到的东西, 以方便后续的状态的提供者继承以后拥有很多能力

import 'package:flutter/material.dart';

class BaseProvider extends ChangeNotifier {
  static final navigatorKey = GlobalKey<NavigatorState>();

  static RouteHelper _routeHelper = RouteHelper(navigatorKey);

  final _notifiers = List<ChangeNotifier>();

  final _autoDisposeList = List<ChangeNotifier>();

  void bindChangeNotifier(
    ChangeNotifier notifier, {
    bool autoDispose = true,
  }) {
    if (autoDispose) {
      _autoDisposeList.add(notifier);
    }
    notifier.addListener(notifyListeners);
    _notifiers.add(notifier);
  }

  void bindChangeNotifiers(
    List<ChangeNotifier> notifiers, {
    bool autoDispose = true,
  }) {
    for (var notifier in notifiers) {
      bindChangeNotifier(notifier, autoDispose: autoDispose);
    }
  }

  void unbindChangeNotifier(ChangeNotifier notifier) {
    notifier.removeListener(notifyListeners);
    _notifiers.remove(notifier);
  }

  RouteHelper get routeHelper => _routeHelper;

  NavigatorState get navigator => routeHelper.navigator;

  @override
  void dispose() {
    for (final notifier in _notifiers.toList()) {
      unbindChangeNotifier(notifier);
    }
    for (final notifier in _autoDisposeList) {
      notifier.dispose();
    }
    super.dispose();
  }
}

这样就简单的将 RouteHelper 单例在了 provider 里, 在书写逻辑后直接就可以使用routeHelper.push(Widget())来跳转页面

而 MaterialApp 中就可以设置BaseProvider.navigatorKey到 navigatorKey 字段上

然后有一点需要说明, pop 方法在官方的‘2.0’版导航中有修改, 返回值从 bool 变成了 void

请根据你自己的 flutter 版本修改返回值即可

后记

因为遇到了 pop 返回值和 dart-pad 上不一样的问题, 然后不小心把 dartpad 关了, 懒得再复制了, 所以没有完整代码说明, 以上