| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // @dart = 2.8 |
| |
| import 'package:flutter/cupertino.dart'; |
| import 'package:flutter/widgets.dart'; |
| |
| import 'page_transitions_theme.dart'; |
| import 'theme.dart'; |
| |
| /// A modal route that replaces the entire screen with a platform-adaptive |
| /// transition. |
| /// |
| /// {@macro flutter.material.materialRouteTransitionMixin} |
| /// |
| /// 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. |
| /// |
| /// The `fullscreenDialog` property specifies whether the incoming 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.pop] by providing the |
| /// optional `result` argument. |
| /// |
| /// See also: |
| /// |
| /// * [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> { |
| /// Construct a MaterialPageRoute whose contents are defined by [builder]. |
| /// |
| /// The values of [builder], [maintainState], and [PageRoute.fullscreenDialog] |
| /// must not be null. |
| MaterialPageRoute({ |
| @required this.builder, |
| RouteSettings settings, |
| this.maintainState = true, |
| bool fullscreenDialog = false, |
| }) : assert(builder != null), |
| assert(maintainState != null), |
| assert(fullscreenDialog != null), |
| assert(opaque), |
| super(settings: settings, fullscreenDialog: fullscreenDialog); |
| |
| /// Builds the primary contents of the route. |
| final WidgetBuilder builder; |
| |
| @override |
| Widget buildContent(BuildContext context) => builder(context); |
| |
| @override |
| final bool maintainState; |
| |
| @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. |
| @protected |
| Widget buildContent(BuildContext context); |
| |
| @override |
| Duration get transitionDuration => const Duration(milliseconds: 300); |
| |
| @override |
| Color get barrierColor => null; |
| |
| @override |
| String get barrierLabel => null; |
| |
| @override |
| bool canTransitionTo(TransitionRoute<dynamic> nextRoute) { |
| // Don't perform outgoing animation if the next route is a fullscreen dialog. |
| return (nextRoute is MaterialRouteTransitionMixin && !nextRoute.fullscreenDialog) |
| || (nextRoute is CupertinoRouteTransitionMixin && !nextRoute.fullscreenDialog); |
| } |
| |
| @override |
| Widget buildPage( |
| BuildContext context, |
| Animation<double> animation, |
| Animation<double> secondaryAnimation, |
| ) { |
| final Widget result = buildContent(context); |
| assert(() { |
| if (result == null) { |
| throw FlutterError( |
| 'The builder for route "${settings.name}" returned null.\n' |
| 'Route builders must never return null.' |
| ); |
| } |
| return true; |
| }()); |
| return Semantics( |
| scopesRoute: true, |
| explicitChildNodes: true, |
| child: result, |
| ); |
| } |
| |
| @override |
| Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) { |
| final PageTransitionsTheme theme = Theme.of(context).pageTransitionsTheme; |
| return theme.buildTransitions<T>(this, context, animation, secondaryAnimation, child); |
| } |
| } |
| |
| /// 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.child, |
| this.maintainState = true, |
| this.fullscreenDialog = false, |
| LocalKey key, |
| String name, |
| Object arguments, |
| }) : assert(child != null), |
| assert(maintainState != null), |
| assert(fullscreenDialog != null), |
| super(key: key, name: name, arguments: arguments); |
| |
| /// The content to be shown in the [Route] created by this page. |
| final Widget child; |
| |
| /// {@macro flutter.widgets.modalRoute.maintainState} |
| final bool maintainState; |
| |
| /// {@macro flutter.widgets.pageRoute.fullscreenDialog} |
| final bool fullscreenDialog; |
| |
| @override |
| 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 |
| Widget buildContent(BuildContext context) { |
| return _page.child; |
| } |
| |
| @override |
| bool get maintainState => _page.maintainState; |
| |
| @override |
| bool get fullscreenDialog => _page.fullscreenDialog; |
| |
| @override |
| String get debugLabel => '${super.debugLabel}(${_page.name})'; |
| } |