Unverified Commit fd6997f0 authored by Taha Tesser's avatar Taha Tesser Committed by GitHub

Add `Dialog.fullscreen` and example (#112261)

parent c66049fc
...@@ -120,6 +120,7 @@ Future<void> main(List<String> args) async { ...@@ -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 { ...@@ -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")};
}
''';
}
// 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'),
),
],
);
}
}
// 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 ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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'; ...@@ -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() { ...@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment