Unverified Commit 5ee5766c authored by Hans Muller's avatar Hans Muller Committed by GitHub

Add builder parameter to showDatePicker, showTimePicker (#27703)

parent 1e5d2e55
...@@ -25,6 +25,9 @@ import 'material_localizations.dart'; ...@@ -25,6 +25,9 @@ import 'material_localizations.dart';
import 'text_theme.dart'; import 'text_theme.dart';
import 'theme.dart'; import 'theme.dart';
// Examples can assume:
// BuildContext context;
/// Initial display mode of the date picker dialog. /// Initial display mode of the date picker dialog.
/// ///
/// Date picker UI mode for either showing a list of available years or a /// Date picker UI mode for either showing a list of available years or a
...@@ -1111,6 +1114,28 @@ typedef SelectableDayPredicate = bool Function(DateTime day); ...@@ -1111,6 +1114,28 @@ typedef SelectableDayPredicate = bool Function(DateTime day);
/// The [context] argument is passed to [showDialog], the documentation for /// The [context] argument is passed to [showDialog], the documentation for
/// which discusses how it is used. /// which discusses how it is used.
/// ///
/// The [builder] parameter can be used to wrap the dialog widget
/// to add inherited widgets like [Theme].
///
/// {@tool sample}
/// Show a date picker with the dark theme.
///
/// ```dart
/// Future<DateTime> selectedDate = showDatePicker(
/// context: context,
/// initialDate: DateTime.now(),
/// firstDate: DateTime(2018),
/// lastDate: DateTime(2030),
/// builder: (BuildContext context, Widget child) {
/// return Theme(
/// data: ThemeData.dark(),
/// child: child,
/// );
/// },
/// );
/// ```
/// {@end-tool}
///
/// The [context], [initialDate], [firstDate], and [lastDate] parameters must /// The [context], [initialDate], [firstDate], and [lastDate] parameters must
/// not be null. /// not be null.
/// ///
...@@ -1133,6 +1158,7 @@ Future<DateTime> showDatePicker({ ...@@ -1133,6 +1158,7 @@ Future<DateTime> showDatePicker({
DatePickerMode initialDatePickerMode = DatePickerMode.day, DatePickerMode initialDatePickerMode = DatePickerMode.day,
Locale locale, Locale locale,
TextDirection textDirection, TextDirection textDirection,
TransitionBuilder builder,
}) async { }) async {
assert(initialDate != null); assert(initialDate != null);
assert(firstDate != null); assert(firstDate != null);
...@@ -1173,6 +1199,8 @@ Future<DateTime> showDatePicker({ ...@@ -1173,6 +1199,8 @@ Future<DateTime> showDatePicker({
return await showDialog<DateTime>( return await showDialog<DateTime>(
context: context, context: context,
builder: (BuildContext context) => child, builder: (BuildContext context) {
return builder == null ? child : builder(context, child);
},
); );
} }
...@@ -22,6 +22,9 @@ import 'theme.dart'; ...@@ -22,6 +22,9 @@ import 'theme.dart';
import 'theme_data.dart'; import 'theme_data.dart';
import 'time.dart'; import 'time.dart';
// Examples can assume:
// BuildContext context;
const Duration _kDialAnimateDuration = Duration(milliseconds: 200); const Duration _kDialAnimateDuration = Duration(milliseconds: 200);
const double _kTwoPi = 2 * math.pi; const double _kTwoPi = 2 * math.pi;
const Duration _kVibrateCommitDelay = Duration(milliseconds: 100); const Duration _kVibrateCommitDelay = Duration(milliseconds: 100);
...@@ -1651,33 +1654,77 @@ class _TimePickerDialogState extends State<_TimePickerDialog> { ...@@ -1651,33 +1654,77 @@ class _TimePickerDialogState extends State<_TimePickerDialog> {
/// The returned Future resolves to the time selected by the user when the user /// The returned Future resolves to the time selected by the user when the user
/// closes the dialog. If the user cancels the dialog, null is returned. /// closes the dialog. If the user cancels the dialog, null is returned.
/// ///
/// To show a dialog with [initialTime] equal to the current time: /// {@tool sample}
/// Show a dialog with [initialTime] equal to the current time.
/// ///
/// ```dart /// ```dart
/// showTimePicker( /// Future<TimeOfDay> selectedTime = showTimePicker(
/// initialTime: TimeOfDay.now(), /// initialTime: TimeOfDay.now(),
/// context: context, /// context: context,
/// ); /// );
/// ``` /// ```
/// {@end-tool}
/// ///
/// The `context` argument is passed to [showDialog], the documentation for /// The [context] argument is passed to [showDialog], the documentation for
/// which discusses how it is used. /// 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 [MediaQuery].
///
/// {@tool sample}
/// Show a dialog with the text direction overridden to be [TextDirection.rtl].
///
/// ```dart
/// Future<TimeOfDay> selectedTimeRTL = showTimePicker(
/// context: context,
/// initialTime: TimeOfDay.now(),
/// builder: (BuildContext context, Widget child) {
/// return Directionality(
/// textDirection: TextDirection.rtl,
/// child: child,
/// );
/// },
/// );
/// ```
/// {@end-tool}
///
/// {@tool sample}
/// Show a dialog with time unconditionally displayed in 24 hour format.
///
/// ```dart
/// Future<TimeOfDay> selectedTime24Hour = showTimePicker(
/// context: context,
/// initialTime: TimeOfDay(hour: 10, minute: 47),
/// builder: (BuildContext context, Widget child) {
/// return MediaQuery(
/// data: MediaQuery.of(context).copyWith(alwaysUse24HourFormat: true),
/// child: child,
/// );
/// },
/// );
/// ```
/// {@end-tool}
///
/// See also: /// See also:
/// ///
/// * [showDatePicker], which shows a dialog that contains a material design /// * [showDatePicker], which shows a dialog that contains a material design
/// date picker. /// date picker.
Future<TimeOfDay> showTimePicker({ Future<TimeOfDay> showTimePicker({
@required BuildContext context, @required BuildContext context,
@required TimeOfDay initialTime @required TimeOfDay initialTime,
TransitionBuilder builder,
}) async { }) async {
assert(context != null); assert(context != null);
assert(initialTime != null); assert(initialTime != null);
assert(debugCheckHasMaterialLocalizations(context)); assert(debugCheckHasMaterialLocalizations(context));
final Widget dialog = _TimePickerDialog(initialTime: initialTime);
return await showDialog<TimeOfDay>( return await showDialog<TimeOfDay>(
context: context, context: context,
builder: (BuildContext context) => _TimePickerDialog(initialTime: initialTime), builder: (BuildContext context) {
return builder == null ? dialog : builder(context, dialog);
},
); );
} }
......
...@@ -723,4 +723,54 @@ void _tests() { ...@@ -723,4 +723,54 @@ void _tests() {
expect(renderer.opacity.status, equals(AnimationStatus.dismissed)); expect(renderer.opacity.status, equals(AnimationStatus.dismissed));
} }
}); });
testWidgets('builder parameter', (WidgetTester tester) async {
Widget buildFrame(TextDirection textDirection) {
return MaterialApp(
home: Material(
child: Center(
child: Builder(
builder: (BuildContext context) {
return RaisedButton(
child: const Text('X'),
onPressed: () {
showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2018),
lastDate: DateTime(2030),
builder: (BuildContext context, Widget child) {
return Directionality(
textDirection: textDirection,
child: child,
);
},
);
},
);
},
),
),
),
);
}
await tester.pumpWidget(buildFrame(TextDirection.ltr));
await tester.tap(find.text('X'));
await tester.pumpAndSettle();
final double ltrOkRight = tester.getBottomRight(find.text('OK')).dx;
await tester.tap(find.text('OK')); // dismiss the dialog
await tester.pumpAndSettle();
await tester.pumpWidget(buildFrame(TextDirection.rtl));
await tester.tap(find.text('X'));
await tester.pumpAndSettle();
// Verify that the time picker is being laid out RTL.
// We expect the left edge of the 'OK' button in the RTL
// layout to match the gap between right edge of the 'OK'
// button and the right edge of the 800 wide window.
expect(tester.getBottomLeft(find.text('OK')).dx, 800 - ltrOkRight);
});
} }
...@@ -505,6 +505,54 @@ void _tests() { ...@@ -505,6 +505,54 @@ void _tests() {
semantics.dispose(); semantics.dispose();
}); });
testWidgets('builder parameter', (WidgetTester tester) async {
Widget buildFrame(TextDirection textDirection) {
return MaterialApp(
home: Material(
child: Center(
child: Builder(
builder: (BuildContext context) {
return RaisedButton(
child: const Text('X'),
onPressed: () {
showTimePicker(
context: context,
initialTime: const TimeOfDay(hour: 7, minute: 0),
builder: (BuildContext context, Widget child) {
return Directionality(
textDirection: textDirection,
child: child,
);
},
);
},
);
},
),
),
),
);
}
await tester.pumpWidget(buildFrame(TextDirection.ltr));
await tester.tap(find.text('X'));
await tester.pumpAndSettle();
final double ltrOkRight = tester.getBottomRight(find.text('OK')).dx;
await tester.tap(find.text('OK')); // dismiss the dialog
await tester.pumpAndSettle();
await tester.pumpWidget(buildFrame(TextDirection.rtl));
await tester.tap(find.text('X'));
await tester.pumpAndSettle();
// Verify that the time picker is being laid out RTL.
// We expect the left edge of the 'OK' button in the RTL
// layout to match the gap between right edge of the 'OK'
// button and the right edge of the 800 wide window.
expect(tester.getBottomLeft(find.text('OK')).dx, 800 - ltrOkRight);
});
} }
final Finder findDialPaint = find.descendant( final Finder findDialPaint = find.descendant(
......
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