Add Dialog.fullscreen
and example (#112261)
This commit is contained in:
parent
c66049fcef
commit
fd6997f07a
@ -120,6 +120,7 @@ Future<void> main(List<String> args) async {
|
|||||||
ButtonTemplate('md.comp.text-button', 'TextButton', '$materialLib/text_button.dart', tokens).updateFile();
|
ButtonTemplate('md.comp.text-button', 'TextButton', '$materialLib/text_button.dart', tokens).updateFile();
|
||||||
CardTemplate('Card', '$materialLib/card.dart', tokens).updateFile();
|
CardTemplate('Card', '$materialLib/card.dart', tokens).updateFile();
|
||||||
CheckboxTemplate('Checkbox', '$materialLib/checkbox.dart', tokens).updateFile();
|
CheckboxTemplate('Checkbox', '$materialLib/checkbox.dart', tokens).updateFile();
|
||||||
|
DialogFullscreenTemplate('DialogFullscreen', '$materialLib/dialog.dart', tokens).updateFile();
|
||||||
DialogTemplate('Dialog', '$materialLib/dialog.dart', tokens).updateFile();
|
DialogTemplate('Dialog', '$materialLib/dialog.dart', tokens).updateFile();
|
||||||
FABTemplate('FAB', '$materialLib/floating_action_button.dart', tokens).updateFile();
|
FABTemplate('FAB', '$materialLib/floating_action_button.dart', tokens).updateFile();
|
||||||
FilterChipTemplate('ChoiceChip', '$materialLib/choice_chip.dart', tokens).updateFile();
|
FilterChipTemplate('ChoiceChip', '$materialLib/choice_chip.dart', tokens).updateFile();
|
||||||
|
@ -47,3 +47,19 @@ class _${blockName}DefaultsM3 extends DialogTheme {
|
|||||||
}
|
}
|
||||||
''';
|
''';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DialogFullscreenTemplate extends TokenTemplate {
|
||||||
|
const DialogFullscreenTemplate(super.blockName, super.fileName, super.tokens);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String generate() => '''
|
||||||
|
class _${blockName}DefaultsM3 extends DialogTheme {
|
||||||
|
const _${blockName}DefaultsM3(this.context);
|
||||||
|
|
||||||
|
final BuildContext context;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Color? get backgroundColor => ${componentColor("md.comp.full-screen-dialog.container")};
|
||||||
|
}
|
||||||
|
''';
|
||||||
|
}
|
||||||
|
86
examples/api/lib/material/dialog/dialog.0.dart
Normal file
86
examples/api/lib/material/dialog/dialog.0.dart
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
// 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 [Dialog].
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
void main() => runApp(const MyApp());
|
||||||
|
|
||||||
|
class MyApp extends StatelessWidget {
|
||||||
|
const MyApp({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
appBar: AppBar(title: const Text('Dialog Sample')),
|
||||||
|
body: const Center(
|
||||||
|
child: DialogExample(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DialogExample extends StatelessWidget {
|
||||||
|
const DialogExample({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => showDialog<String>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) => Dialog(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
const Text('This is a typical dialog.'),
|
||||||
|
const SizedBox(height: 15),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: const Text('Close'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: const Text('Show Dialog'),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => showDialog<String>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) => Dialog.fullscreen(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
const Text('This is a fullscreen dialog.'),
|
||||||
|
const SizedBox(height: 15),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: const Text('Close'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: const Text('Show Fullscreen Dialog'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
53
examples/api/test/material/dialog/dialog.0_test.dart
Normal file
53
examples/api/test/material/dialog/dialog.0_test.dart
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// 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/material/dialog/dialog.0.dart' as example;
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets('Show Dialog', (WidgetTester tester) async {
|
||||||
|
const String dialogText = 'This is a typical dialog.';
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
const MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
body: example.MyApp(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(find.text(dialogText), findsNothing);
|
||||||
|
|
||||||
|
await tester.tap(find.text('Show Dialog'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.text(dialogText), findsOneWidget);
|
||||||
|
|
||||||
|
await tester.tap(find.text('Close'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.text(dialogText), findsNothing);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Show Dialog.fullscreen', (WidgetTester tester) async {
|
||||||
|
const String dialogText = 'This is a fullscreen dialog.';
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
const MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
body: example.MyApp(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(find.text(dialogText), findsNothing);
|
||||||
|
|
||||||
|
await tester.tap(find.text('Show Fullscreen Dialog'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.text(dialogText), findsOneWidget);
|
||||||
|
|
||||||
|
await tester.tap(find.text('Close'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.text(dialogText), findsNothing);
|
||||||
|
});
|
||||||
|
}
|
@ -31,6 +31,12 @@ const EdgeInsets _defaultInsetPadding = EdgeInsets.symmetric(horizontal: 40.0, v
|
|||||||
/// or [SimpleDialog], which implement specific kinds of Material Design
|
/// or [SimpleDialog], which implement specific kinds of Material Design
|
||||||
/// dialogs.
|
/// dialogs.
|
||||||
///
|
///
|
||||||
|
/// {@tool dartpad}
|
||||||
|
/// This sample shows the creation of [Dialog] and [Dialog.fullscreen] widgets.
|
||||||
|
///
|
||||||
|
/// ** See code in examples/api/lib/material/dialog/dialog.0.dart **
|
||||||
|
/// {@end-tool}
|
||||||
|
///
|
||||||
/// See also:
|
/// See also:
|
||||||
///
|
///
|
||||||
/// * [AlertDialog], for dialogs that have a message and some buttons.
|
/// * [AlertDialog], for dialogs that have a message and some buttons.
|
||||||
@ -55,7 +61,26 @@ class Dialog extends StatelessWidget {
|
|||||||
this.alignment,
|
this.alignment,
|
||||||
this.child,
|
this.child,
|
||||||
}) : assert(clipBehavior != null),
|
}) : assert(clipBehavior != null),
|
||||||
assert(elevation == null || elevation >= 0.0);
|
assert(elevation == null || elevation >= 0.0),
|
||||||
|
_fullscreen = false;
|
||||||
|
|
||||||
|
/// Creates a fullscreen dialog.
|
||||||
|
///
|
||||||
|
/// Typically used in conjunction with [showDialog].
|
||||||
|
const Dialog.fullscreen({
|
||||||
|
super.key,
|
||||||
|
this.backgroundColor,
|
||||||
|
this.insetAnimationDuration = Duration.zero,
|
||||||
|
this.insetAnimationCurve = Curves.decelerate,
|
||||||
|
this.child,
|
||||||
|
}) : elevation = 0,
|
||||||
|
shadowColor = null,
|
||||||
|
surfaceTintColor = null,
|
||||||
|
insetPadding = EdgeInsets.zero,
|
||||||
|
clipBehavior = Clip.none,
|
||||||
|
shape = null,
|
||||||
|
alignment = null,
|
||||||
|
_fullscreen = true;
|
||||||
|
|
||||||
/// {@template flutter.material.dialog.backgroundColor}
|
/// {@template flutter.material.dialog.backgroundColor}
|
||||||
/// The background color of the surface of this [Dialog].
|
/// The background color of the surface of this [Dialog].
|
||||||
@ -130,7 +155,8 @@ class Dialog extends StatelessWidget {
|
|||||||
/// The duration of the animation to show when the system keyboard intrudes
|
/// The duration of the animation to show when the system keyboard intrudes
|
||||||
/// into the space that the dialog is placed in.
|
/// into the space that the dialog is placed in.
|
||||||
///
|
///
|
||||||
/// Defaults to 100 milliseconds.
|
/// Defaults to 100 milliseconds when [Dialog] is used, and [Duration.zero]
|
||||||
|
/// when [Dialog.fullscreen] is used.
|
||||||
/// {@endtemplate}
|
/// {@endtemplate}
|
||||||
final Duration insetAnimationDuration;
|
final Duration insetAnimationDuration;
|
||||||
|
|
||||||
@ -184,13 +210,44 @@ class Dialog extends StatelessWidget {
|
|||||||
/// {@macro flutter.widgets.ProxyWidget.child}
|
/// {@macro flutter.widgets.ProxyWidget.child}
|
||||||
final Widget? child;
|
final Widget? child;
|
||||||
|
|
||||||
|
/// This value is used to determine if this is a fullscreen dialog.
|
||||||
|
final bool _fullscreen;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final ThemeData theme = Theme.of(context);
|
final ThemeData theme = Theme.of(context);
|
||||||
final DialogTheme dialogTheme = DialogTheme.of(context);
|
final DialogTheme dialogTheme = DialogTheme.of(context);
|
||||||
final DialogTheme defaults = theme.useMaterial3 ? _DialogDefaultsM3(context) : _DialogDefaultsM2(context);
|
|
||||||
|
|
||||||
final EdgeInsets effectivePadding = MediaQuery.of(context).viewInsets + (insetPadding ?? EdgeInsets.zero);
|
final EdgeInsets effectivePadding = MediaQuery.of(context).viewInsets + (insetPadding ?? EdgeInsets.zero);
|
||||||
|
final DialogTheme defaults = theme.useMaterial3
|
||||||
|
? (_fullscreen ? _DialogFullscreenDefaultsM3(context) : _DialogDefaultsM3(context))
|
||||||
|
: _DialogDefaultsM2(context);
|
||||||
|
|
||||||
|
Widget dialogChild;
|
||||||
|
|
||||||
|
if (_fullscreen) {
|
||||||
|
dialogChild = Material(
|
||||||
|
color: backgroundColor ?? dialogTheme.backgroundColor ?? defaults.backgroundColor,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
dialogChild = Align(
|
||||||
|
alignment: alignment ?? dialogTheme.alignment ?? defaults.alignment!,
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(minWidth: 280.0),
|
||||||
|
child: Material(
|
||||||
|
color: backgroundColor ?? dialogTheme.backgroundColor ?? Theme.of(context).dialogBackgroundColor,
|
||||||
|
elevation: elevation ?? dialogTheme.elevation ?? defaults.elevation!,
|
||||||
|
shadowColor: shadowColor ?? dialogTheme.shadowColor ?? defaults.shadowColor,
|
||||||
|
surfaceTintColor: surfaceTintColor ?? dialogTheme.surfaceTintColor ?? defaults.surfaceTintColor,
|
||||||
|
shape: shape ?? dialogTheme.shape ?? defaults.shape!,
|
||||||
|
type: MaterialType.card,
|
||||||
|
clipBehavior: clipBehavior,
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return AnimatedPadding(
|
return AnimatedPadding(
|
||||||
padding: effectivePadding,
|
padding: effectivePadding,
|
||||||
duration: insetAnimationDuration,
|
duration: insetAnimationDuration,
|
||||||
@ -201,22 +258,7 @@ class Dialog extends StatelessWidget {
|
|||||||
removeRight: true,
|
removeRight: true,
|
||||||
removeBottom: true,
|
removeBottom: true,
|
||||||
context: context,
|
context: context,
|
||||||
child: Align(
|
child: dialogChild,
|
||||||
alignment: alignment ?? dialogTheme.alignment ?? defaults.alignment!,
|
|
||||||
child: ConstrainedBox(
|
|
||||||
constraints: const BoxConstraints(minWidth: 280.0),
|
|
||||||
child: Material(
|
|
||||||
color: backgroundColor ?? dialogTheme.backgroundColor ?? Theme.of(context).dialogBackgroundColor,
|
|
||||||
elevation: elevation ?? dialogTheme.elevation ?? defaults.elevation!,
|
|
||||||
shadowColor: shadowColor ?? dialogTheme.shadowColor ?? defaults.shadowColor,
|
|
||||||
surfaceTintColor: surfaceTintColor ?? dialogTheme.surfaceTintColor ?? defaults.surfaceTintColor,
|
|
||||||
shape: shape ?? dialogTheme.shape ?? defaults.shape!,
|
|
||||||
type: MaterialType.card,
|
|
||||||
clipBehavior: clipBehavior,
|
|
||||||
child: child,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1426,3 +1468,23 @@ class _DialogDefaultsM3 extends DialogTheme {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// END GENERATED TOKEN PROPERTIES - Dialog
|
// END GENERATED TOKEN PROPERTIES - Dialog
|
||||||
|
|
||||||
|
// BEGIN GENERATED TOKEN PROPERTIES - DialogFullscreen
|
||||||
|
|
||||||
|
// Do not edit by hand. The code between the "BEGIN GENERATED" and
|
||||||
|
// "END GENERATED" comments are generated from data in the Material
|
||||||
|
// Design token database by the script:
|
||||||
|
// dev/tools/gen_defaults/bin/gen_defaults.dart.
|
||||||
|
|
||||||
|
// Token database version: v0_132
|
||||||
|
|
||||||
|
class _DialogFullscreenDefaultsM3 extends DialogTheme {
|
||||||
|
const _DialogFullscreenDefaultsM3(this.context);
|
||||||
|
|
||||||
|
final BuildContext context;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Color? get backgroundColor => Theme.of(context).colorScheme.surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
// END GENERATED TOKEN PROPERTIES - DialogFullscreen
|
||||||
|
@ -6,6 +6,7 @@ import 'dart:ui';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
import '../widgets/semantics_tester.dart';
|
import '../widgets/semantics_tester.dart';
|
||||||
@ -138,6 +139,53 @@ void main() {
|
|||||||
expect(material3Widget.elevation, 6.0);
|
expect(material3Widget.elevation, 6.0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('Dialog.fullscreen Defaults', (WidgetTester tester) async {
|
||||||
|
const String dialogTextM2 = 'Fullscreen Dialog - M2';
|
||||||
|
const String dialogTextM3 = 'Fullscreen Dialog - M3';
|
||||||
|
|
||||||
|
await tester.pumpWidget(_buildAppWithDialog(
|
||||||
|
theme: material2Theme,
|
||||||
|
const Dialog.fullscreen(
|
||||||
|
child: Text(dialogTextM2),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
await tester.tap(find.text('X'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.text(dialogTextM2), findsOneWidget);
|
||||||
|
|
||||||
|
Material materialWidget = _getMaterialFromDialog(tester);
|
||||||
|
expect(materialWidget.color, Colors.grey[800]);
|
||||||
|
|
||||||
|
// Try to dismiss the fullscreen dialog with the escape key.
|
||||||
|
await tester.sendKeyEvent(LogicalKeyboardKey.escape);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.text(dialogTextM2), findsNothing);
|
||||||
|
|
||||||
|
await tester.pumpWidget(_buildAppWithDialog(
|
||||||
|
theme: material3Theme,
|
||||||
|
const Dialog.fullscreen(
|
||||||
|
child: Text(dialogTextM3),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
await tester.tap(find.text('X'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.text(dialogTextM3), findsOneWidget);
|
||||||
|
|
||||||
|
materialWidget = _getMaterialFromDialog(tester);
|
||||||
|
expect(materialWidget.color, material3Theme.colorScheme.surface);
|
||||||
|
|
||||||
|
// Try to dismiss the fullscreen dialog with the escape key.
|
||||||
|
await tester.sendKeyEvent(LogicalKeyboardKey.escape);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.text(dialogTextM3), findsNothing);
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('Custom dialog elevation', (WidgetTester tester) async {
|
testWidgets('Custom dialog elevation', (WidgetTester tester) async {
|
||||||
const double customElevation = 12.0;
|
const double customElevation = 12.0;
|
||||||
const Color shadowColor = Color(0xFF000001);
|
const Color shadowColor = Color(0xFF000001);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user