Skip to content

Commit

Permalink
Proposal to add barrier configs for showDatePicker, showTimePicker an…
Browse files Browse the repository at this point in the history
…d showAboutDialog. (#130484)

### Overview

Add `barrierDismissible`, `barrierColor` and `barrierLabel` parameters to  `showDatePicker`, `showTimePicker` and `showAboutDialog` which calls `showDialog` internally.
We can change these parameters with `showDialog` and Dialog widgets (like `DatePickerDialog`, `TimePickerDialog` or `AboutDialog`) directly. But, I think it is prefer to provide interfaces same as `showDialog` to keep application wide unified looks if it is used internally.

Fixes #130971
  • Loading branch information
ronnnnn committed Jul 25, 2023
1 parent c8b9b15 commit 9def8f6
Show file tree
Hide file tree
Showing 7 changed files with 564 additions and 14 deletions.
12 changes: 9 additions & 3 deletions packages/flutter/lib/src/material/about.dart
Original file line number Diff line number Diff line change
Expand Up @@ -170,22 +170,28 @@ class AboutListTile extends StatelessWidget {
/// The licenses shown on the [LicensePage] are those returned by the
/// [LicenseRegistry] API, which can be used to add more licenses to the list.
///
/// The [context], [useRootNavigator], [routeSettings] and [anchorPoint]
/// arguments are passed to [showDialog], the documentation for which discusses
/// how it is used.
/// The [context], [barrierDismissible], [barrierColor], [barrierLabel],
/// [useRootNavigator], [routeSettings] and [anchorPoint] arguments are
/// passed to [showDialog], the documentation for which discusses how it is used.
void showAboutDialog({
required BuildContext context,
String? applicationName,
String? applicationVersion,
Widget? applicationIcon,
String? applicationLegalese,
List<Widget>? children,
bool barrierDismissible = true,
Color? barrierColor,
String? barrierLabel,
bool useRootNavigator = true,
RouteSettings? routeSettings,
Offset? anchorPoint,
}) {
showDialog<void>(
context: context,
barrierDismissible: barrierDismissible,
barrierColor: barrierColor,
barrierLabel: barrierLabel,
useRootNavigator: useRootNavigator,
builder: (BuildContext context) {
return AboutDialog(
Expand Down
26 changes: 20 additions & 6 deletions packages/flutter/lib/src/material/date_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,10 @@ const double _kMaxTextScaleFactor = 1.3;
/// [locale] and [textDirection] are non-null, [textDirection] overrides the
/// direction chosen for the [locale].
///
/// The [context], [useRootNavigator] and [routeSettings] arguments are passed to
/// [showDialog], the documentation for which discusses how it is used. [context]
/// and [useRootNavigator] must be non-null.
/// The [context], [barrierDismissible], [barrierColor], [barrierLabel],
/// [useRootNavigator] and [routeSettings] arguments are passed to [showDialog],
/// the documentation for which discusses how it is used.
/// [context], [barrierDismissible] and [useRootNavigator] must be non-null.
///
/// The [builder] parameter can be used to wrap the dialog widget
/// to add inherited widgets like [Theme].
Expand Down Expand Up @@ -169,6 +170,9 @@ Future<DateTime?> showDatePicker({
String? cancelText,
String? confirmText,
Locale? locale,
bool barrierDismissible = true,
Color? barrierColor,
String? barrierLabel,
bool useRootNavigator = true,
RouteSettings? routeSettings,
TextDirection? textDirection,
Expand Down Expand Up @@ -243,6 +247,9 @@ Future<DateTime?> showDatePicker({

return showDialog<DateTime>(
context: context,
barrierDismissible: barrierDismissible,
barrierColor: barrierColor,
barrierLabel: barrierLabel,
useRootNavigator: useRootNavigator,
routeSettings: routeSettings,
builder: (BuildContext context) {
Expand Down Expand Up @@ -967,9 +974,10 @@ class _DatePickerHeader extends StatelessWidget {
/// [locale] and [textDirection] are non-null, [textDirection] overrides the
/// direction chosen for the [locale].
///
/// The [context], [useRootNavigator] and [routeSettings] arguments are passed
/// to [showDialog], the documentation for which discusses how it is used.
/// [context] and [useRootNavigator] must be non-null.
/// The [context], [barrierDismissible], [barrierColor], [barrierLabel],
/// [useRootNavigator] and [routeSettings] arguments are passed to [showDialog],
/// the documentation for which discusses how it is used.
/// [context], [barrierDismissible] and [useRootNavigator] must be non-null.
///
/// The [builder] parameter can be used to wrap the dialog widget
/// to add inherited widgets like [Theme].
Expand Down Expand Up @@ -1022,6 +1030,9 @@ Future<DateTimeRange?> showDateRangePicker({
String? fieldStartLabelText,
String? fieldEndLabelText,
Locale? locale,
bool barrierDismissible = true,
Color? barrierColor,
String? barrierLabel,
bool useRootNavigator = true,
RouteSettings? routeSettings,
TextDirection? textDirection,
Expand Down Expand Up @@ -1100,6 +1111,9 @@ Future<DateTimeRange?> showDateRangePicker({

return showDialog<DateTimeRange>(
context: context,
barrierDismissible: barrierDismissible,
barrierColor: barrierColor,
barrierLabel: barrierLabel,
useRootNavigator: useRootNavigator,
routeSettings: routeSettings,
useSafeArea: false,
Expand Down
6 changes: 3 additions & 3 deletions packages/flutter/lib/src/material/dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1404,7 +1404,7 @@ Future<T?> showDialog<T>({
required BuildContext context,
required WidgetBuilder builder,
bool barrierDismissible = true,
Color? barrierColor = Colors.black54,
Color? barrierColor,
String? barrierLabel,
bool useSafeArea = true,
bool useRootNavigator = true,
Expand All @@ -1426,7 +1426,7 @@ Future<T?> showDialog<T>({
return Navigator.of(context, rootNavigator: useRootNavigator).push<T>(DialogRoute<T>(
context: context,
builder: builder,
barrierColor: barrierColor,
barrierColor: barrierColor ?? Colors.black54,
barrierDismissible: barrierDismissible,
barrierLabel: barrierLabel,
useSafeArea: useSafeArea,
Expand All @@ -1449,7 +1449,7 @@ Future<T?> showAdaptiveDialog<T>({
required BuildContext context,
required WidgetBuilder builder,
bool? barrierDismissible,
Color? barrierColor = Colors.black54,
Color? barrierColor,
String? barrierLabel,
bool useSafeArea = true,
bool useRootNavigator = true,
Expand Down
11 changes: 9 additions & 2 deletions packages/flutter/lib/src/material/time_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2874,8 +2874,9 @@ class _TimePickerState extends State<_TimePicker> with RestorationMixin {
/// ```
/// {@end-tool}
///
/// The [context], [useRootNavigator] and [routeSettings] arguments are passed
/// to [showDialog], the documentation for which discusses how it is used.
/// The [context], [barrierDismissible], [barrierColor], [barrierLabel],
/// [useRootNavigator] and [routeSettings] arguments are passed to [showDialog],
/// the documentation for which discusses how it is used.
///
/// The [builder] parameter can be used to wrap the dialog widget to add
/// inherited widgets like [Localizations.override], [Directionality], or
Expand Down Expand Up @@ -2954,6 +2955,9 @@ Future<TimeOfDay?> showTimePicker({
required BuildContext context,
required TimeOfDay initialTime,
TransitionBuilder? builder,
bool barrierDismissible = true,
Color? barrierColor,
String? barrierLabel,
bool useRootNavigator = true,
TimePickerEntryMode initialEntryMode = TimePickerEntryMode.dial,
String? cancelText,
Expand Down Expand Up @@ -2983,6 +2987,9 @@ Future<TimeOfDay?> showTimePicker({
);
return showDialog<TimeOfDay>(
context: context,
barrierDismissible: barrierDismissible,
barrierColor: barrierColor,
barrierLabel: barrierLabel,
useRootNavigator: useRootNavigator,
builder: (BuildContext context) {
return builder == null ? dialog : builder(context, dialog);
Expand Down
162 changes: 162 additions & 0 deletions packages/flutter/test/material/about_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,160 @@ void main() {
expect(nestedObserver.licensePageCount, 0);
});

group('Barrier dismissible', () {
late AboutDialogObserver rootObserver;

setUpAll(() {
rootObserver = AboutDialogObserver();
});

testWidgets('Barrier is dismissible with default parameter', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
navigatorObservers: <NavigatorObserver>[rootObserver],
home: Material(
child: Center(
child: Builder(
builder: (BuildContext context) {
return ElevatedButton(
child: const Text('X'),
onPressed: () => showAboutDialog(
context: context,
),
);
},
),
),
),
),
);

// Open the dialog.
await tester.tap(find.byType(ElevatedButton));
await tester.pumpAndSettle();
expect(rootObserver.dialogCount, 1);

// Tap on the barrier.
await tester.tapAt(const Offset(10.0, 10.0));
await tester.pumpAndSettle();
expect(rootObserver.dialogCount, 0);
});

testWidgets('Barrier is not dismissible with barrierDismissible is false', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
navigatorObservers: <NavigatorObserver>[rootObserver],
home: Material(
child: Center(
child: Builder(
builder: (BuildContext context) {
return ElevatedButton(
child: const Text('X'),
onPressed: () => showAboutDialog(
context: context,
barrierDismissible: false
),
);
},
),
),
),
),
);

// Open the dialog.
await tester.tap(find.byType(ElevatedButton));
await tester.pumpAndSettle();
expect(rootObserver.dialogCount, 1);

// Tap on the barrier, which shouldn't do anything this time.
await tester.tapAt(const Offset(10.0, 10.0));
await tester.pumpAndSettle();
expect(rootObserver.dialogCount, 1);
});
});

testWidgets('Barrier color', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Material(
child: Center(
child: Builder(
builder: (BuildContext context) {
return ElevatedButton(
child: const Text('X'),
onPressed: () => showAboutDialog(
context: context,
),
);
},
),
),
),
),
);

// Open the dialog.
await tester.tap(find.byType(ElevatedButton));
await tester.pumpAndSettle();
expect(tester.widget<ModalBarrier>(find.byType(ModalBarrier).last).color, Colors.black54);

// Dismiss the dialog.
await tester.tapAt(const Offset(10.0, 10.0));

await tester.pumpWidget(
MaterialApp(
home: Material(
child: Center(
child: Builder(
builder: (BuildContext context) {
return ElevatedButton(
child: const Text('X'),
onPressed: () => showAboutDialog(
context: context,
barrierColor: Colors.pink,
),
);
},
),
),
),
),
);

// Open the dialog.
await tester.tap(find.byType(ElevatedButton));
await tester.pumpAndSettle();
expect(tester.widget<ModalBarrier>(find.byType(ModalBarrier).last).color, Colors.pink);
});

testWidgets('Barrier Label', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Material(
child: Center(
child: Builder(
builder: (BuildContext context) {
return ElevatedButton(
child: const Text('X'),
onPressed: () => showAboutDialog(
context: context,
barrierLabel: 'Custom Label',
),
);
},
),
),
),
),
);

// Open the dialog.
await tester.tap(find.byType(ElevatedButton));
await tester.pumpAndSettle();
expect(tester.widget<ModalBarrier>(find.byType(ModalBarrier).last).semanticsLabel, 'Custom Label');
});

testWidgetsWithLeakTracking('showAboutDialog uses root navigator by default', (WidgetTester tester) async {
final AboutDialogObserver rootObserver = AboutDialogObserver();
final AboutDialogObserver nestedObserver = AboutDialogObserver();
Expand Down Expand Up @@ -1741,4 +1895,12 @@ class AboutDialogObserver extends NavigatorObserver {
}
super.didPush(route, previousRoute);
}

@override
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
if (route is DialogRoute) {
dialogCount--;
}
super.didPop(route, previousRoute);
}
}

0 comments on commit 9def8f6

Please sign in to comment.