page.dart 6.59 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
// @dart = 2.8

7
import 'package:flutter/cupertino.dart';
8
import 'package:flutter/widgets.dart';
9

10
import 'page_transitions_theme.dart';
11
import 'theme.dart';
12

13 14
/// A modal route that replaces the entire screen with a platform-adaptive
/// transition.
15
///
16
/// {@macro flutter.material.materialRouteTransitionMixin}
17
///
18 19 20
/// By default, when a modal route is replaced by another, the previous route
/// remains in memory. To free all the resources when this is not necessary, set
/// [maintainState] to false.
21
///
22 23
/// The `fullscreenDialog` property specifies whether the incoming route is a
/// fullscreen modal dialog. On iOS, those routes animate from the bottom to the
24
/// top rather than horizontally.
25
///
26
/// The type `T` specifies the return type of the route which can be supplied as
27 28
/// the route is popped from the stack via [Navigator.pop] by providing the
/// optional `result` argument.
29
///
30 31
/// See also:
///
32 33 34 35
///  * [MaterialRouteTransitionMixin], which provides the material transition
///    for this route.
///  * [MaterialPage], which is a [Page] of this class.
class MaterialPageRoute<T> extends PageRoute<T> with MaterialRouteTransitionMixin<T> {
36 37
  /// Construct a MaterialPageRoute whose contents are defined by [builder].
  ///
38
  /// The values of [builder], [maintainState], and [PageRoute.fullscreenDialog]
39
  /// must not be null.
40
  MaterialPageRoute({
41
    @required this.builder,
42
    RouteSettings settings,
43 44
    this.maintainState = true,
    bool fullscreenDialog = false,
45
  }) : assert(builder != null),
46 47
       assert(maintainState != null),
       assert(fullscreenDialog != null),
48 49
       assert(opaque),
       super(settings: settings, fullscreenDialog: fullscreenDialog);
50

51
  @override
52
  final WidgetBuilder builder;
Adam Barth's avatar
Adam Barth committed
53

54 55 56
  @override
  final bool maintainState;

57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
  @override
  String get debugLabel => '${super.debugLabel}(${settings.name})';
}


/// A mixin that provides platform-adaptive transitions for a [PageRoute].
///
/// {@template flutter.material.materialRouteTransitionMixin}
/// For Android, the entrance transition for the page slides the route upwards
/// and fades it in. The exit transition is the same, but in reverse.
///
/// The transition is adaptive to the platform and on iOS, the route slides in
/// from the right and exits in reverse. The route also shifts to the left in
/// parallax when another page enters to cover it. (These directions are flipped
/// in environments with a right-to-left reading direction.)
/// {@endtemplate}
///
/// See also:
///
///  * [PageTransitionsTheme], which defines the default page transitions used
///    by the [MaterialRouteTransitionMixin.buildTransitions].
mixin MaterialRouteTransitionMixin<T> on PageRoute<T> {
  /// Builds the primary contents of the route.
  WidgetBuilder get builder;

82
  @override
83
  Duration get transitionDuration => const Duration(milliseconds: 300);
84 85

  @override
86
  Color get barrierColor => null;
87

88 89 90
  @override
  String get barrierLabel => null;

91 92 93
  @override
  bool canTransitionTo(TransitionRoute<dynamic> nextRoute) {
    // Don't perform outgoing animation if the next route is a fullscreen dialog.
94 95
    return (nextRoute is MaterialRouteTransitionMixin && !nextRoute.fullscreenDialog)
      || (nextRoute is CupertinoRouteTransitionMixin && !nextRoute.fullscreenDialog);
96 97
  }

98
  @override
99 100 101 102 103
  Widget buildPage(
    BuildContext context,
    Animation<double> animation,
    Animation<double> secondaryAnimation,
  ) {
104
    final Widget result = builder(context);
105
    assert(() {
106
      if (result == null) {
107 108 109 110
        throw FlutterError(
          'The builder for route "${settings.name}" returned null.\n'
          'Route builders must never return null.'
        );
111
      }
112
      return true;
113
    }());
114 115 116 117 118
    return Semantics(
      scopesRoute: true,
      explicitChildNodes: true,
      child: result,
    );
119 120
  }

121
  @override
122
  Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
123 124
    final PageTransitionsTheme theme = Theme.of(context).pageTransitionsTheme;
    return theme.buildTransitions<T>(this, context, animation, secondaryAnimation, child);
125
  }
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
}

/// A page that creates a material style [PageRoute].
///
/// {@macro flutter.material.materialRouteTransitionMixin}
///
/// By default, when the created route is replaced by another, the previous
/// route remains in memory. To free all the resources when this is not
/// necessary, set [maintainState] to false.
///
/// The `fullscreenDialog` property specifies whether the created route is a
/// fullscreen modal dialog. On iOS, those routes animate from the bottom to the
/// top rather than horizontally.
///
/// The type `T` specifies the return type of the route which can be supplied as
/// the route is popped from the stack via [Navigator.transitionDelegate] by
/// providing the optional `result` argument to the
/// [RouteTransitionRecord.markForPop] in the [TransitionDelegate.resolve].
///
/// See also:
///
///  * [MaterialPageRoute], which is the [PageRoute] version of this class
class MaterialPage<T> extends Page<T> {
  /// Creates a material page.
  const MaterialPage({
    @required this.builder,
    this.maintainState = true,
    this.fullscreenDialog = false,
    LocalKey key,
    String name,
    Object arguments,
  }) : assert(builder != null),
       assert(maintainState != null),
       assert(fullscreenDialog != null),
       super(key: key, name: name, arguments: arguments);

  /// Builds the primary contents of the route.
  final WidgetBuilder builder;

  /// {@macro flutter.widgets.modalRoute.maintainState}
  final bool maintainState;

  /// {@macro flutter.widgets.pageRoute.fullscreenDialog}
  final bool fullscreenDialog;
170

171
  @override
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
  Route<T> createRoute(BuildContext context) {
    return _PageBasedMaterialPageRoute<T>(page: this);
  }
}

// A page-based version of MaterialPageRoute.
//
// This route uses the builder from the page to build its content. This ensures
// the content is up to date after page updates.
class _PageBasedMaterialPageRoute<T> extends PageRoute<T> with MaterialRouteTransitionMixin<T> {
  _PageBasedMaterialPageRoute({
    @required MaterialPage<T> page,
  }) : assert(page != null),
       assert(opaque),
       super(settings: page);

  MaterialPage<T> get _page => settings as MaterialPage<T>;

  @override
  WidgetBuilder get builder => _page.builder;

  @override
  bool get maintainState => _page.maintainState;

  @override
  bool get fullscreenDialog => _page.fullscreenDialog;

  @override
  String get debugLabel => '${super.debugLabel}(${_page.name})';
201
}