blob: 73047d56a2b5db56fe0bac36d289cc5e470637ae [file] [log] [blame]
// 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,
);
}