| // Copyright 2013 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. |
| |
| import 'dart:ui' as ui; |
| |
| import 'package:flutter/material.dart'; |
| |
| import 'fade_scale_transition.dart'; |
| |
| /// Signature for a function that creates a widget that builds a |
| /// transition. |
| /// |
| /// Used by [PopupRoute]. |
| typedef _ModalTransitionBuilder = Widget Function( |
| BuildContext context, |
| Animation<double> animation, |
| Animation<double> secondaryAnimation, |
| Widget child, |
| ); |
| |
| /// Displays a modal above the current contents of the app. |
| /// |
| /// Content below the modal is dimmed with a [ModalBarrier]. |
| /// |
| /// The `context` argument is used to look up the [Navigator] for the |
| /// modal. It is only used when the method is called. Its corresponding widget |
| /// can be safely removed from the tree before the modal is closed. |
| /// |
| /// The `configuration` argument is used to determine characteristics of the |
| /// modal route that will be displayed, such as the enter and exit |
| /// transitions, the duration of the transitions, and modal barrier |
| /// properties. By default, `configuration` is |
| /// [FadeScaleTransitionConfiguration]. |
| /// |
| /// The `useRootNavigator` argument is used to determine whether to push the |
| /// modal to the [Navigator] furthest from or nearest to the given `context`. |
| /// By default, `useRootNavigator` is `true` and the modal route created by |
| /// this method is pushed to the root navigator. If the application has |
| /// multiple [Navigator] objects, it may be necessary to call |
| /// `Navigator.of(context, rootNavigator: true).pop(result)` to close the |
| /// modal rather than just `Navigator.pop(context, result)`. |
| /// |
| /// Returns a [Future] that resolves to the value (if any) that was passed to |
| /// [Navigator.pop] when the modal was closed. |
| /// |
| /// See also: |
| /// |
| /// * [ModalConfiguration], which is the configuration object used to define |
| /// the modal's characteristics. |
| Future<T?> showModal<T>({ |
| required BuildContext context, |
| ModalConfiguration configuration = const FadeScaleTransitionConfiguration(), |
| bool useRootNavigator = true, |
| required WidgetBuilder builder, |
| RouteSettings? routeSettings, |
| ui.ImageFilter? filter, |
| }) { |
| String? barrierLabel = configuration.barrierLabel; |
| // Avoid looking up [MaterialLocalizations.of(context).modalBarrierDismissLabel] |
| // if there is no dismissible barrier. |
| if (configuration.barrierDismissible && configuration.barrierLabel == null) { |
| barrierLabel = MaterialLocalizations.of(context).modalBarrierDismissLabel; |
| } |
| assert(!configuration.barrierDismissible || barrierLabel != null); |
| return Navigator.of(context, rootNavigator: useRootNavigator).push<T>( |
| _ModalRoute<T>( |
| barrierColor: configuration.barrierColor, |
| barrierDismissible: configuration.barrierDismissible, |
| barrierLabel: barrierLabel, |
| transitionBuilder: configuration.transitionBuilder, |
| transitionDuration: configuration.transitionDuration, |
| reverseTransitionDuration: configuration.reverseTransitionDuration, |
| builder: builder, |
| routeSettings: routeSettings, |
| filter: filter, |
| ), |
| ); |
| } |
| |
| // A modal route that overlays a widget on the current route. |
| class _ModalRoute<T> extends PopupRoute<T> { |
| /// Creates a route with general modal route. |
| /// |
| /// [barrierDismissible] configures whether or not tapping the modal's |
| /// scrim dismisses the modal. [barrierLabel] sets the semantic label for |
| /// a dismissible barrier. [barrierDismissible] cannot be null. If |
| /// [barrierDismissible] is true, the [barrierLabel] cannot be null. |
| /// |
| /// [transitionBuilder] takes in a function that creates a widget. This |
| /// widget is typically used to configure the modal's transition. |
| _ModalRoute({ |
| this.barrierColor, |
| this.barrierDismissible = true, |
| this.barrierLabel, |
| required this.transitionDuration, |
| required this.reverseTransitionDuration, |
| required _ModalTransitionBuilder transitionBuilder, |
| required this.builder, |
| RouteSettings? routeSettings, |
| ui.ImageFilter? filter, |
| }) : assert(!barrierDismissible || barrierLabel != null), |
| _transitionBuilder = transitionBuilder, |
| super(filter: filter, settings: routeSettings); |
| |
| @override |
| final Color? barrierColor; |
| |
| @override |
| final bool barrierDismissible; |
| |
| @override |
| final String? barrierLabel; |
| |
| @override |
| final Duration transitionDuration; |
| |
| @override |
| final Duration reverseTransitionDuration; |
| |
| /// The primary contents of the modal. |
| final WidgetBuilder builder; |
| |
| final _ModalTransitionBuilder _transitionBuilder; |
| |
| @override |
| Widget buildPage( |
| BuildContext context, |
| Animation<double> animation, |
| Animation<double> secondaryAnimation, |
| ) { |
| final ThemeData theme = Theme.of(context); |
| return Semantics( |
| scopesRoute: true, |
| explicitChildNodes: true, |
| child: SafeArea( |
| child: Builder( |
| builder: (BuildContext context) { |
| final Widget child = Builder(builder: builder); |
| return Theme(data: theme, child: child); |
| }, |
| ), |
| ), |
| ); |
| } |
| |
| @override |
| Widget buildTransitions( |
| BuildContext context, |
| Animation<double> animation, |
| Animation<double> secondaryAnimation, |
| Widget child, |
| ) { |
| return _transitionBuilder( |
| context, |
| animation, |
| secondaryAnimation, |
| child, |
| ); |
| } |
| } |
| |
| /// A configuration object containing the properties needed to implement a |
| /// modal route. |
| /// |
| /// The `barrierDismissible` argument is used to determine whether this route |
| /// can be dismissed by tapping the modal barrier. This argument defaults |
| /// to true. If `barrierDismissible` is true, a non-null `barrierLabel` must be |
| /// provided. |
| /// |
| /// The `barrierLabel` argument is the semantic label used for a dismissible |
| /// barrier. This argument defaults to "Dismiss". |
| abstract class ModalConfiguration { |
| /// Creates a modal configuration object that provides the necessary |
| /// properties to implement a modal route. |
| /// |
| /// [barrierDismissible] configures whether or not tapping the modal's |
| /// scrim dismisses the modal. [barrierLabel] sets the semantic label for |
| /// a dismissible barrier. [barrierDismissible] cannot be null. If |
| /// [barrierDismissible] is true, the [barrierLabel] cannot be null. |
| /// |
| /// [transitionDuration] and [reverseTransitionDuration] determine the |
| /// duration of the transitions when the modal enters and exits the |
| /// application. [transitionDuration] and [reverseTransitionDuration] |
| /// cannot be null. |
| const ModalConfiguration({ |
| required this.barrierColor, |
| required this.barrierDismissible, |
| this.barrierLabel, |
| required this.transitionDuration, |
| required this.reverseTransitionDuration, |
| }) : assert(!barrierDismissible || barrierLabel != null); |
| |
| /// The color to use for the modal barrier. If this is null, the barrier will |
| /// be transparent. |
| final Color barrierColor; |
| |
| /// Whether you can dismiss this route by tapping the modal barrier. |
| final bool barrierDismissible; |
| |
| /// The semantic label used for a dismissible barrier. |
| final String? barrierLabel; |
| |
| /// The duration of the transition running forwards. |
| final Duration transitionDuration; |
| |
| /// The duration of the transition running in reverse. |
| final Duration reverseTransitionDuration; |
| |
| /// A builder that defines how the route arrives on and leaves the screen. |
| /// |
| /// The [buildTransitions] method is typically used to define transitions |
| /// that animate the new topmost route's comings and goings. When the |
| /// [Navigator] pushes a route on the top of its stack, the new route's |
| /// primary [animation] runs from 0.0 to 1.0. When the [Navigator] pops the |
| /// topmost route, e.g. because the use pressed the back button, the |
| /// primary animation runs from 1.0 to 0.0. |
| Widget transitionBuilder( |
| BuildContext context, |
| Animation<double> animation, |
| Animation<double> secondaryAnimation, |
| Widget child, |
| ); |
| } |