Update PopupRoute
docs and add an example (#106948)
This commit is contained in:
parent
a8d047923b
commit
66cd09dd82
84
examples/api/lib/widgets/routes/popup_route.0.dart
Normal file
84
examples/api/lib/widgets/routes/popup_route.0.dart
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// Flutter code sample for PopupRoute
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
void main() => runApp(const PopupRouteApp());
|
||||||
|
|
||||||
|
class PopupRouteApp extends StatelessWidget {
|
||||||
|
const PopupRouteApp({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return const MaterialApp(
|
||||||
|
home: PopupRouteExample(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PopupRouteExample extends StatelessWidget {
|
||||||
|
const PopupRouteExample({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: OutlinedButton(
|
||||||
|
onPressed: () {
|
||||||
|
/// This shows a dismissible dialog.
|
||||||
|
Navigator.of(context).push(DismissibleDialog<void>());
|
||||||
|
},
|
||||||
|
child: const Text('Open DismissibleDialog'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DismissibleDialog<T> extends PopupRoute<T> {
|
||||||
|
@override
|
||||||
|
Color? get barrierColor => Colors.black.withAlpha(0x50);
|
||||||
|
|
||||||
|
/// This allows the popup to be dismissed by tapping the scrim or by
|
||||||
|
/// pressing escape key on the keyboard.
|
||||||
|
@override
|
||||||
|
bool get barrierDismissible => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get barrierLabel => 'Dismissible Dialog';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Duration get transitionDuration => const Duration(milliseconds: 300);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
|
||||||
|
return Center(
|
||||||
|
/// Provide DefaultTextStyle to ensure that the dialog's text style matches
|
||||||
|
/// the rest of the text in the app.
|
||||||
|
child: DefaultTextStyle(
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium!,
|
||||||
|
/// `UnconstrainedBox` is used to make the dialog size itself
|
||||||
|
/// to fit to the size of the content.
|
||||||
|
child: UnconstrainedBox(
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(20.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Text('Dismissible Dialog', style: Theme.of(context).textTheme.headlineSmall),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
const Text('Tap in the scrim or press escape key to dismiss.'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -6,25 +6,22 @@
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
void main() => runApp(const MyApp());
|
void main() => runApp(const GeneralDialogApp());
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class GeneralDialogApp extends StatelessWidget {
|
||||||
const MyApp({super.key});
|
const GeneralDialogApp({super.key});
|
||||||
|
|
||||||
static const String _title = 'Flutter Code Sample';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return const MaterialApp(
|
return const MaterialApp(
|
||||||
restorationScopeId: 'app',
|
restorationScopeId: 'app',
|
||||||
title: _title,
|
home: GeneralDialogExample(),
|
||||||
home: MyStatelessWidget(),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyStatelessWidget extends StatelessWidget {
|
class GeneralDialogExample extends StatelessWidget {
|
||||||
const MyStatelessWidget({super.key});
|
const GeneralDialogExample({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -32,6 +29,7 @@ class MyStatelessWidget extends StatelessWidget {
|
|||||||
body: Center(
|
body: Center(
|
||||||
child: OutlinedButton(
|
child: OutlinedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
/// This shows an alert dialog.
|
||||||
Navigator.of(context).restorablePush(_dialogBuilder);
|
Navigator.of(context).restorablePush(_dialogBuilder);
|
||||||
},
|
},
|
||||||
child: const Text('Open Dialog'),
|
child: const Text('Open Dialog'),
|
||||||
|
40
examples/api/test/widgets/routes/popup_route.0_test.dart
Normal file
40
examples/api/test/widgets/routes/popup_route.0_test.dart
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_api_samples/widgets/routes/popup_route.0.dart' as example;
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets('Dismiss dialog with tap on the scrim and escape key', (WidgetTester tester) async {
|
||||||
|
const String dialogText = 'Tap in the scrim or press escape key to dismiss.';
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
const example.PopupRouteApp(),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(find.text(dialogText), findsNothing);
|
||||||
|
|
||||||
|
// Tap on the button to show the dialog.
|
||||||
|
await tester.tap(find.byType(OutlinedButton));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.text(dialogText), findsOneWidget);
|
||||||
|
|
||||||
|
// Try to dismiss the dialog with a tap on the scrim.
|
||||||
|
await tester.tapAt(const Offset(10.0, 10.0));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.text(dialogText), findsNothing);
|
||||||
|
|
||||||
|
// Open the dialog again.
|
||||||
|
await tester.tap(find.byType(OutlinedButton));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.text(dialogText), findsOneWidget);
|
||||||
|
|
||||||
|
// Try to dismiss the dialog with the escape key.
|
||||||
|
await tester.sendKeyEvent(LogicalKeyboardKey.escape);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.text(dialogText), findsNothing);
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_api_samples/widgets/routes/show_general_dialog.0.dart' as example;
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets('Open and dismiss general dialog', (WidgetTester tester) async {
|
||||||
|
const String dialogText = 'Alert!';
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
const example.GeneralDialogApp(),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(find.text(dialogText), findsNothing);
|
||||||
|
|
||||||
|
// Tap on the button to show the dialog.
|
||||||
|
await tester.tap(find.byType(OutlinedButton));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.text(dialogText), findsOneWidget);
|
||||||
|
|
||||||
|
// Try to dismiss the dialog with a tap on the scrim.
|
||||||
|
await tester.tapAt(const Offset(10.0, 10.0));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.text(dialogText), findsNothing);
|
||||||
|
});
|
||||||
|
}
|
@ -1210,8 +1210,10 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
|
|||||||
/// For example, when a dialog is on the screen, the page below the dialog is
|
/// For example, when a dialog is on the screen, the page below the dialog is
|
||||||
/// usually darkened by the modal barrier.
|
/// usually darkened by the modal barrier.
|
||||||
///
|
///
|
||||||
/// If [barrierDismissible] is true, then tapping this barrier will cause the
|
/// If [barrierDismissible] is true, then tapping this barrier, pressing
|
||||||
/// current route to be popped (see [Navigator.pop]) with null as the value.
|
/// the escape key on the keyboard, or calling route popping functions
|
||||||
|
/// such as [Navigator.pop] will cause the current route to be popped
|
||||||
|
/// with null as the value.
|
||||||
///
|
///
|
||||||
/// If [barrierDismissible] is false, then tapping the barrier has no effect.
|
/// If [barrierDismissible] is false, then tapping the barrier has no effect.
|
||||||
///
|
///
|
||||||
@ -1226,6 +1228,7 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
|
|||||||
///
|
///
|
||||||
/// See also:
|
/// See also:
|
||||||
///
|
///
|
||||||
|
/// * [Navigator.pop], which is used to dismiss the route.
|
||||||
/// * [barrierColor], which controls the color of the scrim for this route.
|
/// * [barrierColor], which controls the color of the scrim for this route.
|
||||||
/// * [ModalBarrier], the widget that implements this feature.
|
/// * [ModalBarrier], the widget that implements this feature.
|
||||||
/// {@endtemplate}
|
/// {@endtemplate}
|
||||||
@ -1691,6 +1694,19 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A modal route that overlays a widget over the current route.
|
/// A modal route that overlays a widget over the current route.
|
||||||
|
///
|
||||||
|
/// {@macro flutter.widgets.ModalRoute.barrierDismissible}
|
||||||
|
///
|
||||||
|
/// {@tool dartpad}
|
||||||
|
/// This example shows how to create a dialog box that is dismissible.
|
||||||
|
///
|
||||||
|
/// ** See code in examples/api/lib/widgets/routes/popup_route.0.dart **
|
||||||
|
/// {@end-tool}
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [ModalRoute], which is the base class for this class.
|
||||||
|
/// * [Navigator.pop], which is used to dismiss the route.
|
||||||
abstract class PopupRoute<T> extends ModalRoute<T> {
|
abstract class PopupRoute<T> extends ModalRoute<T> {
|
||||||
/// Initializes the [PopupRoute].
|
/// Initializes the [PopupRoute].
|
||||||
PopupRoute({
|
PopupRoute({
|
||||||
|
Loading…
x
Reference in New Issue
Block a user