Navigator Helper2

文章目录

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

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

GlobalKey

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

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

设置:

1MaterialApp(
2    navigatorKey: navigatorKey,
3);

使用:

1NavigatorState get navigator => navigatorKey.currentState;

RouteHelper

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

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

  1import 'package:flutter/material.dart';
  2
  3import 'dialog_route.dart';
  4
  5class RouteHelper {
  6  GlobalKey<NavigatorState> stateKey;
  7
  8  RouteHelper(this.stateKey);
  9
 10  NavigatorState get navigator => stateKey.currentState;
 11
 12  AnimUtils anim = AnimUtils();
 13
 14  Future<T> pushWidget<T>(
 15    Widget widget, {
 16    bool replaceRoot = false,
 17    bool replaceCurrent = false,
 18    dynamic replaceResult,
 19  }) {
 20    final materialRoute = MaterialPageRoute<T>(builder: (BuildContext context) {
 21      return widget;
 22    });
 23    return pushRoute<T>(
 24      materialRoute,
 25      replaceRoot: replaceRoot,
 26      replaceCurrent: replaceCurrent,
 27      replaceResult: replaceResult,
 28    );
 29  }
 30
 31  Future<T> pushRoute<T>(
 32    Route<T> newRoute, {
 33    bool replaceRoot = false,
 34    bool replaceCurrent = false,
 35    dynamic replaceResult,
 36  }) async {
 37    assert(!(replaceCurrent && replaceRoot), "这两货只有一个可以为true");
 38    if (replaceRoot) {
 39      while (true) {
 40        if (!(await navigator.maybePop())) {
 41          break;
 42        }
 43      }
 44      return navigator.pushReplacement<T, dynamic>(
 45        newRoute,
 46        result: replaceResult,
 47      );
 48    }
 49    if (replaceCurrent) {
 50      return navigator.pushReplacement<T, dynamic>(
 51        newRoute,
 52        result: replaceResult,
 53      );
 54    }
 55    return navigator.push<T>(newRoute);
 56  }
 57
 58  Future<T> showDialog<T>(
 59    Widget dialog, {
 60    TransitionsWidgetBuilder transitionsWidgetBuilder,
 61    TransitionsWidgetBuilder pageBuilder,
 62    Duration transitionsDuration = const Duration(milliseconds: 300),
 63  }) {
 64    // pageBuilder ??= anim.opacity;
 65    pageBuilder ??= anim.empty;
 66    transitionsWidgetBuilder ??= anim.opacity;
 67    return pushRoute<T>(
 68      DialogRoute<T>(
 69        child: dialog,
 70        pageBuilder: pageBuilder,
 71        transitionsWidgetBuilder: transitionsWidgetBuilder,
 72        transitionDuration: transitionsDuration,
 73      ),
 74    );
 75  }
 76
 77  bool pop<T>([T result]) {
 78    return navigator.pop(result);
 79  }
 80
 81  Future<bool> maybePop<T>([T result]) async {
 82    return navigator.maybePop(result);
 83  }
 84}
 85
 86class AnimUtils {
 87  Widget opacity(BuildContext context, Animation<double> animation,
 88      Animation<double> secondaryAnimation, Widget child) {
 89    return AnimatedBuilder(
 90      builder: (BuildContext context, Widget child) {
 91        return Opacity(
 92          opacity: animation.value,
 93          child: child,
 94        );
 95      },
 96      animation: animation,
 97      child: child,
 98    );
 99  }
100
101  Widget empty(BuildContext context, Animation<double> animation,
102      Animation<double> secondaryAnimation, Widget child) {
103    return AnimatedBuilder(
104      builder: (BuildContext context, Widget child) {
105        return child;
106      },
107      animation: animation,
108      child: child,
109    );
110  }
111}

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

 1import 'package:flutter/material.dart';
 2
 3typedef Widget DialogWidgetBuilder(
 4  BuildContext context,
 5  Animation<double> animation,
 6  Animation<double> secondaryAnimation,
 7  Widget child,
 8);
 9
10typedef Widget TransitionsWidgetBuilder(
11  BuildContext context,
12  Animation<double> animation,
13  Animation<double> secondaryAnimation,
14  Widget child,
15);
16
17class DialogRoute<T> extends PopupRoute<T> {
18  final DialogWidgetBuilder pageBuilder;
19  final TransitionsWidgetBuilder transitionsWidgetBuilder;
20  final Duration transitionDuration;
21  final bool maintainState;
22  final bool barrierDismissible;
23  final Widget child;
24
25  DialogRoute({
26    this.pageBuilder,
27    this.maintainState = true,
28    this.transitionDuration = const Duration(milliseconds: 250),
29    this.barrierDismissible = true,
30    this.child,
31    this.transitionsWidgetBuilder,
32  }) : assert(!(pageBuilder == null && child == null));
33
34  @override
35  Color get barrierColor => Colors.black38;
36
37  @override
38  String get barrierLabel => "title";
39
40  @override
41  Widget buildPage(BuildContext context, Animation<double> animation,
42      Animation<double> secondaryAnimation) {
43    if (pageBuilder == null) {
44      return child;
45    }
46    return pageBuilder(context, animation, secondaryAnimation, child);
47  }
48
49  @override
50  Widget buildTransitions(BuildContext context, Animation<double> animation,
51      Animation<double> secondaryAnimation, Widget child) {
52    if (transitionsWidgetBuilder == null) {
53      return super
54          .buildTransitions(context, animation, secondaryAnimation, child);
55    }
56    return transitionsWidgetBuilder(
57        context, animation, secondaryAnimation, child);
58  }
59}
60
61mixin TransitionsMixin {
62  Widget buildTransitions(BuildContext context, Animation<double> animation,
63      Animation<double> secondaryAnimation, Widget child);
64
65  Duration transitionsDuration() => Duration(milliseconds: 300);
66}
67
68mixin TransitionsWidgetMixin on Widget, TransitionsMixin {}
69
70mixin TransitionsStateMixin on State, TransitionsMixin {}

BaseProvider

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

 1import 'package:flutter/material.dart';
 2
 3class BaseProvider extends ChangeNotifier {
 4  static final navigatorKey = GlobalKey<NavigatorState>();
 5
 6  static RouteHelper _routeHelper = RouteHelper(navigatorKey);
 7
 8  final _notifiers = List<ChangeNotifier>();
 9
10  final _autoDisposeList = List<ChangeNotifier>();
11
12  void bindChangeNotifier(
13    ChangeNotifier notifier, {
14    bool autoDispose = true,
15  }) {
16    if (autoDispose) {
17      _autoDisposeList.add(notifier);
18    }
19    notifier.addListener(notifyListeners);
20    _notifiers.add(notifier);
21  }
22
23  void bindChangeNotifiers(
24    List<ChangeNotifier> notifiers, {
25    bool autoDispose = true,
26  }) {
27    for (var notifier in notifiers) {
28      bindChangeNotifier(notifier, autoDispose: autoDispose);
29    }
30  }
31
32  void unbindChangeNotifier(ChangeNotifier notifier) {
33    notifier.removeListener(notifyListeners);
34    _notifiers.remove(notifier);
35  }
36
37  RouteHelper get routeHelper => _routeHelper;
38
39  NavigatorState get navigator => routeHelper.navigator;
40
41  @override
42  void dispose() {
43    for (final notifier in _notifiers.toList()) {
44      unbindChangeNotifier(notifier);
45    }
46    for (final notifier in _autoDisposeList) {
47      notifier.dispose();
48    }
49    super.dispose();
50  }
51}

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

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

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

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

后记

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