blob: 7b683c8db7ce54003873b5af46eda96a482efa43 [file] [log] [blame]
// 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 'dart:ui';
import 'package:flutter/rendering.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/gestures.dart' show DragStartBehavior;
import '../widgets/semantics_tester.dart';
import 'feedback_tester.dart';
void main() {
group('showDatePicker', () {
_tests();
});
}
void _tests() {
DateTime firstDate;
DateTime lastDate;
DateTime initialDate;
SelectableDayPredicate selectableDayPredicate;
DatePickerMode initialDatePickerMode;
final Finder nextMonthIcon = find.byWidgetPredicate((Widget w) => w is IconButton && (w.tooltip?.startsWith('Next month') ?? false));
final Finder previousMonthIcon = find.byWidgetPredicate((Widget w) => w is IconButton && (w.tooltip?.startsWith('Previous month') ?? false));
setUp(() {
firstDate = DateTime(2001, DateTime.january, 1);
lastDate = DateTime(2031, DateTime.december, 31);
initialDate = DateTime(2016, DateTime.january, 15);
selectableDayPredicate = null;
initialDatePickerMode = null;
});
testWidgets('tap-select a day', (WidgetTester tester) async {
final Key _datePickerKey = UniqueKey();
DateTime _selectedDate = DateTime(2016, DateTime.july, 26);
await tester.pumpWidget(
MaterialApp(
home: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Container(
width: 400.0,
child: SingleChildScrollView(
dragStartBehavior: DragStartBehavior.down,
child: Material(
child: MonthPicker(
dragStartBehavior: DragStartBehavior.down,
firstDate: DateTime(0),
lastDate: DateTime(9999),
key: _datePickerKey,
selectedDate: _selectedDate,
onChanged: (DateTime value) {
setState(() {
_selectedDate = value;
});
},
),
),
),
);
},
),
),
);
expect(_selectedDate, equals(DateTime(2016, DateTime.july, 26)));
await tester.tapAt(const Offset(50.0, 100.0));
await tester.pumpAndSettle();
await tester.pump(const Duration(seconds: 2));
await tester.tap(find.text('1'));
await tester.pumpAndSettle();
expect(_selectedDate, equals(DateTime(2016, DateTime.july, 1)));
await tester.tap(nextMonthIcon);
await tester.pumpAndSettle();
expect(_selectedDate, equals(DateTime(2016, DateTime.july, 1)));
await tester.tap(find.text('5'));
await tester.pumpAndSettle();
expect(_selectedDate, equals(DateTime(2016, DateTime.august, 5)));
await tester.drag(find.byKey(_datePickerKey), const Offset(-400.0, 0.0));
await tester.pumpAndSettle();
expect(_selectedDate, equals(DateTime(2016, DateTime.august, 5)));
await tester.tap(find.text('25'));
await tester.pumpAndSettle();
expect(_selectedDate, equals(DateTime(2016, DateTime.september, 25)));
await tester.drag(find.byKey(_datePickerKey), const Offset(800.0, 0.0));
await tester.pumpAndSettle();
expect(_selectedDate, equals(DateTime(2016, DateTime.september, 25)));
await tester.tap(find.text('17'));
await tester.pumpAndSettle();
expect(_selectedDate, equals(DateTime(2016, DateTime.august, 17)));
});
testWidgets('render picker with intrinsic dimensions', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return IntrinsicWidth(
child: IntrinsicHeight(
child: Material(
child: SingleChildScrollView(
child: MonthPicker(
firstDate: DateTime(0),
lastDate: DateTime(9999),
onChanged: (DateTime value) { },
selectedDate: DateTime(2000, DateTime.january, 1),
),
),
),
),
);
},
),
),
);
await tester.pump(const Duration(seconds: 5));
});
Future<void> preparePicker(WidgetTester tester, Future<void> callback(Future<DateTime> date)) async {
BuildContext buttonContext;
await tester.pumpWidget(MaterialApp(
home: Material(
child: Builder(
builder: (BuildContext context) {
return RaisedButton(
onPressed: () {
buttonContext = context;
},
child: const Text('Go'),
);
},
),
),
));
await tester.tap(find.text('Go'));
expect(buttonContext, isNotNull);
final Future<DateTime> date = initialDatePickerMode == null
// Exercise the argument default for initialDatePickerMode.
?
showDatePicker(
context: buttonContext,
initialDate: initialDate,
firstDate: firstDate,
lastDate: lastDate,
selectableDayPredicate: selectableDayPredicate,
)
:
showDatePicker(
context: buttonContext,
initialDate: initialDate,
firstDate: firstDate,
lastDate: lastDate,
selectableDayPredicate: selectableDayPredicate,
initialDatePickerMode: initialDatePickerMode,
);
await tester.pumpAndSettle(const Duration(seconds: 1));
await callback(date);
}
testWidgets('Initial date is the default', (WidgetTester tester) async {
await preparePicker(tester, (Future<DateTime> date) async {
await tester.tap(find.text('OK'));
expect(await date, equals(DateTime(2016, DateTime.january, 15)));
});
});
testWidgets('Can cancel', (WidgetTester tester) async {
await preparePicker(tester, (Future<DateTime> date) async {
await tester.tap(find.text('CANCEL'));
expect(await date, isNull);
});
});
testWidgets('Can select a day', (WidgetTester tester) async {
await preparePicker(tester, (Future<DateTime> date) async {
await tester.tap(find.text('12'));
await tester.tap(find.text('OK'));
expect(await date, equals(DateTime(2016, DateTime.january, 12)));
});
});
testWidgets('Can select a month', (WidgetTester tester) async {
await preparePicker(tester, (Future<DateTime> date) async {
await tester.tap(previousMonthIcon);
await tester.pumpAndSettle(const Duration(seconds: 1));
await tester.tap(find.text('25'));
await tester.tap(find.text('OK'));
expect(await date, equals(DateTime(2015, DateTime.december, 25)));
});
});
testWidgets('Can select a year', (WidgetTester tester) async {
await preparePicker(tester, (Future<DateTime> date) async {
await tester.tap(find.text('2016'));
await tester.pump();
await tester.tap(find.text('2018'));
await tester.tap(find.text('OK'));
expect(await date, equals(DateTime(2018, DateTime.january, 15)));
});
});
testWidgets('Can select a year and then a day', (WidgetTester tester) async {
await preparePicker(tester, (Future<DateTime> date) async {
await tester.tap(find.text('2016'));
await tester.pump();
await tester.tap(find.text('2017'));
await tester.pump();
final MaterialLocalizations localizations = MaterialLocalizations.of(
tester.element(find.byType(DayPicker))
);
final String dayLabel = localizations.formatMediumDate(DateTime(2017, DateTime.january, 15));
await tester.tap(find.text(dayLabel));
await tester.pump();
await tester.tap(find.text('19'));
await tester.tap(find.text('OK'));
expect(await date, equals(DateTime(2017, DateTime.january, 19)));
});
});
testWidgets('Current year is initially visible in year picker', (WidgetTester tester) async {
initialDate = DateTime(2000);
firstDate = DateTime(1900);
lastDate = DateTime(2100);
await preparePicker(tester, (Future<DateTime> date) async {
await tester.tap(find.text('2000'));
await tester.pump();
expect(find.text('2000'), findsNWidgets(2));
});
});
testWidgets('Cannot select a day outside bounds', (WidgetTester tester) async {
initialDate = DateTime(2017, DateTime.january, 15);
firstDate = initialDate;
lastDate = initialDate;
await preparePicker(tester, (Future<DateTime> date) async {
await tester.tap(find.text('10')); // Earlier than firstDate. Should be ignored.
await tester.tap(find.text('20')); // Later than lastDate. Should be ignored.
await tester.tap(find.text('OK'));
// We should still be on the initial date.
expect(await date, equals(initialDate));
});
});
testWidgets('Cannot select a month past last date', (WidgetTester tester) async {
initialDate = DateTime(2017, DateTime.january, 15);
firstDate = initialDate;
lastDate = DateTime(2017, DateTime.february, 20);
await preparePicker(tester, (Future<DateTime> date) async {
await tester.tap(nextMonthIcon);
await tester.pumpAndSettle(const Duration(seconds: 1));
// Shouldn't be possible to keep going into March.
expect(nextMonthIcon, findsNothing);
});
});
testWidgets('Cannot select a month before first date', (WidgetTester tester) async {
initialDate = DateTime(2017, DateTime.january, 15);
firstDate = DateTime(2016, DateTime.december, 10);
lastDate = initialDate;
await preparePicker(tester, (Future<DateTime> date) async {
await tester.tap(previousMonthIcon);
await tester.pumpAndSettle(const Duration(seconds: 1));
// Shouldn't be possible to keep going into November.
expect(previousMonthIcon, findsNothing);
});
});
testWidgets('Selecting firstDate year respects firstDate', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/17309
initialDate = DateTime(2018, DateTime.may, 4);
firstDate = DateTime(2016, DateTime.june, 9);
lastDate = DateTime(2019, DateTime.january, 15);
await preparePicker(tester, (Future<DateTime> date) async {
await tester.tap(find.text('2018'));
await tester.pumpAndSettle();
await tester.tap(find.text('2016'));
await tester.pumpAndSettle();
await tester.tap(find.text('OK'));
await tester.pumpAndSettle();
expect(await date, DateTime(2016, DateTime.june, 9));
});
});
testWidgets('Selecting lastDate year respects lastDate', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/17309
initialDate = DateTime(2018, DateTime.may, 4);
firstDate = DateTime(2016, DateTime.june, 9);
lastDate = DateTime(2019, DateTime.january, 15);
await preparePicker(tester, (Future<DateTime> date) async {
await tester.tap(find.text('2018'));
await tester.pumpAndSettle();
await tester.tap(find.text('2019'));
await tester.pumpAndSettle();
await tester.tap(find.text('OK'));
await tester.pumpAndSettle();
expect(await date, DateTime(2019, DateTime.january, 15));
});
});
testWidgets('Only predicate days are selectable', (WidgetTester tester) async {
initialDate = DateTime(2017, DateTime.january, 16);
firstDate = DateTime(2017, DateTime.january, 10);
lastDate = DateTime(2017, DateTime.january, 20);
selectableDayPredicate = (DateTime day) => day.day.isEven;
await preparePicker(tester, (Future<DateTime> date) async {
await tester.tap(find.text('10')); // Even, works.
await tester.tap(find.text('13')); // Odd, doesn't work.
await tester.tap(find.text('17')); // Odd, doesn't work.
await tester.tap(find.text('OK'));
expect(await date, equals(DateTime(2017, DateTime.january, 10)));
});
});
testWidgets('Can select initial date picker mode', (WidgetTester tester) async {
initialDate = DateTime(2014, DateTime.january, 15);
initialDatePickerMode = DatePickerMode.year;
await preparePicker(tester, (Future<DateTime> date) async {
await tester.pump();
// 2018 wouldn't be available if the year picker wasn't showing.
// The initial current year is 2014.
await tester.tap(find.text('2018'));
await tester.tap(find.text('OK'));
expect(await date, equals(DateTime(2018, DateTime.january, 15)));
});
});
group('haptic feedback', () {
const Duration kHapticFeedbackInterval = Duration(milliseconds: 10);
FeedbackTester feedback;
setUp(() {
feedback = FeedbackTester();
initialDate = DateTime(2017, DateTime.january, 16);
firstDate = DateTime(2017, DateTime.january, 10);
lastDate = DateTime(2018, DateTime.january, 20);
selectableDayPredicate = (DateTime date) => date.day.isEven;
});
tearDown(() {
feedback?.dispose();
});
testWidgets('tap-select date vibrates', (WidgetTester tester) async {
await preparePicker(tester, (Future<DateTime> date) async {
await tester.tap(find.text('10'));
await tester.pump(kHapticFeedbackInterval);
expect(feedback.hapticCount, 1);
await tester.tap(find.text('12'));
await tester.pump(kHapticFeedbackInterval);
expect(feedback.hapticCount, 2);
await tester.tap(find.text('14'));
await tester.pump(kHapticFeedbackInterval);
expect(feedback.hapticCount, 3);
});
});
testWidgets('tap-select unselectable date does not vibrate', (WidgetTester tester) async {
await preparePicker(tester, (Future<DateTime> date) async {
await tester.tap(find.text('11'));
await tester.pump(kHapticFeedbackInterval);
expect(feedback.hapticCount, 0);
await tester.tap(find.text('13'));
await tester.pump(kHapticFeedbackInterval);
expect(feedback.hapticCount, 0);
await tester.tap(find.text('15'));
await tester.pump(kHapticFeedbackInterval);
expect(feedback.hapticCount, 0);
});
});
testWidgets('mode, year change vibrates', (WidgetTester tester) async {
await preparePicker(tester, (Future<DateTime> date) async {
await tester.tap(find.text('2017'));
await tester.pump(kHapticFeedbackInterval);
expect(feedback.hapticCount, 1);
await tester.tap(find.text('2018'));
await tester.pump(kHapticFeedbackInterval);
expect(feedback.hapticCount, 2);
});
});
});
test('days in month', () {
expect(DayPicker.getDaysInMonth(2017, 10), 31);
expect(DayPicker.getDaysInMonth(2017, 6), 30);
expect(DayPicker.getDaysInMonth(2017, 2), 28);
expect(DayPicker.getDaysInMonth(2016, 2), 29);
expect(DayPicker.getDaysInMonth(2000, 2), 29);
expect(DayPicker.getDaysInMonth(1900, 2), 28);
});
testWidgets('month header tap', (WidgetTester tester) async {
selectableDayPredicate = null;
await preparePicker(tester, (Future<DateTime> date) async {
// Switch into the year selector.
await tester.tap(find.text('January 2016'));
await tester.pump();
expect(find.text('2020'), isNotNull);
await tester.tap(find.text('CANCEL'));
expect(await date, isNull);
});
});
testWidgets('exports semantics', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
await preparePicker(tester, (Future<DateTime> date) async {
final TestSemantics expected = TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute],
children: <TestSemantics>[
TestSemantics(
elevation: 24.0,
thickness: 0.0,
children: <TestSemantics>[
TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.isFocusable],
actions: <SemanticsAction>[SemanticsAction.tap],
label: '2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
flags: <SemanticsFlag>[
SemanticsFlag.isSelected,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
label: 'Fri, Jan 15',
textDirection: TextDirection.ltr,
),
TestSemantics(
children: <TestSemantics>[
TestSemantics(
id: 55,
actions: <SemanticsAction>[SemanticsAction.scrollLeft, SemanticsAction.scrollRight],
children: <TestSemantics>[
TestSemantics(
children: <TestSemantics>[
TestSemantics(
children: <TestSemantics>[
TestSemantics(
id: 11,
flags: <SemanticsFlag>[SemanticsFlag.hasImplicitScrolling],
children: <TestSemantics>[
// TODO(dnfield): These shouldn't be here. https://github.com/flutter/flutter/issues/34431
TestSemantics(),
TestSemantics(),
TestSemantics(),
TestSemantics(),
TestSemantics(),
TestSemantics(),
TestSemantics(),
TestSemantics(),
TestSemantics(),
TestSemantics(),
TestSemantics(),
TestSemantics(),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '1, Friday, January 1, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '2, Saturday, January 2, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '3, Sunday, January 3, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '4, Monday, January 4, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '5, Tuesday, January 5, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '6, Wednesday, January 6, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '7, Thursday, January 7, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '8, Friday, January 8, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '9, Saturday, January 9, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '10, Sunday, January 10, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '11, Monday, January 11, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '12, Tuesday, January 12, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '13, Wednesday, January 13, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '14, Thursday, January 14, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.isSelected],
actions: <SemanticsAction>[SemanticsAction.tap],
label: '15, Friday, January 15, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '16, Saturday, January 16, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '17, Sunday, January 17, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '18, Monday, January 18, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '19, Tuesday, January 19, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '20, Wednesday, January 20, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '21, Thursday, January 21, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '22, Friday, January 22, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '23, Saturday, January 23, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '24, Sunday, January 24, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '25, Monday, January 25, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '26, Tuesday, January 26, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '27, Wednesday, January 27, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '28, Thursday, January 28, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '29, Friday, January 29, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '30, Saturday, January 30, 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: '31, Sunday, January 31, 2016',
textDirection: TextDirection.ltr,
),
],
),
],
),
],
),
],
),
],
),
TestSemantics(
flags: <SemanticsFlag>[
SemanticsFlag.hasEnabledState,
SemanticsFlag.isButton,
SemanticsFlag.isEnabled,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
label: 'Previous month December 2015',
textDirection: TextDirection.ltr,
),
TestSemantics(
flags: <SemanticsFlag>[
SemanticsFlag.hasEnabledState,
SemanticsFlag.isButton,
SemanticsFlag.isEnabled,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
label: 'Next month February 2016',
textDirection: TextDirection.ltr,
),
TestSemantics(
flags: <SemanticsFlag>[
SemanticsFlag.hasEnabledState,
SemanticsFlag.isButton,
SemanticsFlag.isEnabled,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
label: 'CANCEL',
textDirection: TextDirection.ltr,
),
TestSemantics(
flags: <SemanticsFlag>[
SemanticsFlag.hasEnabledState,
SemanticsFlag.isButton,
SemanticsFlag.isEnabled,
SemanticsFlag.isFocusable,
],
actions: <SemanticsAction>[SemanticsAction.tap],
label: 'OK',
textDirection: TextDirection.ltr,
),
],
),
],
);
expect(semantics, hasSemantics(
TestSemantics.root(children: <TestSemantics>[
TestSemantics(
children: <TestSemantics>[expected],
),
]),
ignoreId: true,
ignoreTransform: true,
ignoreRect: true,
));
});
semantics.dispose();
});
testWidgets('chervons animate when scrolling month picker', (WidgetTester tester) async {
final Key _datePickerKey = UniqueKey();
DateTime _selectedDate = DateTime(2016, DateTime.july, 26);
await tester.pumpWidget(
MaterialApp(
home: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Container(
width: 400.0,
child: SingleChildScrollView(
child: Material(
child: MonthPicker(
firstDate: DateTime(0),
lastDate: DateTime(9999),
key: _datePickerKey,
selectedDate: _selectedDate,
onChanged: (DateTime value) {
setState(() {
_selectedDate = value;
});
},
),
),
),
);
},
),
),
);
final Finder chevronFinder = find.byType(IconButton);
final List<RenderAnimatedOpacity> chevronRenderers = chevronFinder
.evaluate()
.map((Element element) => element.findAncestorRenderObjectOfType<RenderAnimatedOpacity>())
.toList();
// Initial chevron animation state should be dismissed
// An AlwaysStoppedAnimation is also found and is ignored
for (final RenderAnimatedOpacity renderer in chevronRenderers) {
expect(renderer.opacity.value, equals(1.0));
expect(renderer.opacity.status, equals(AnimationStatus.dismissed));
}
// Drag and hold the picker to test for the opacity change
final TestGesture gesture = await tester.startGesture(const Offset(100.0, 100.0));
await gesture.moveBy(const Offset(50.0, 100.0));
await tester.pumpAndSettle();
for (final RenderAnimatedOpacity renderer in chevronRenderers) {
expect(renderer.opacity.value, equals(0.0));
expect(renderer.opacity.status, equals(AnimationStatus.completed));
}
// Release the drag and test for the opacity to return to original value
await gesture.up();
await tester.pumpAndSettle();
for (final RenderAnimatedOpacity renderer in chevronRenderers) {
expect(renderer.opacity.value, equals(1.0));
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);
});
group('screen configurations', () {
// Test various combinations of screen sizes, orientations and text scales
// to ensure the layout doesn't overflow and cause an exception to be thrown.
// Regression tests for https://github.com/flutter/flutter/issues/21383
// Regression tests for https://github.com/flutter/flutter/issues/19744
// Regression tests for https://github.com/flutter/flutter/issues/17745
// Common screen size roughly based on a Pixel 1
const Size kCommonScreenSizePortrait = Size(1070, 1770);
const Size kCommonScreenSizeLandscape = Size(1770, 1070);
// Small screen size based on a LG K130
const Size kSmallScreenSizePortrait = Size(320, 521);
const Size kSmallScreenSizeLandscape = Size(521, 320);
Future<void> _showPicker(WidgetTester tester, Size size, [double textScaleFactor = 1.0]) async {
tester.binding.window.physicalSizeTestValue = size;
tester.binding.window.devicePixelRatioTestValue = 1.0;
await tester.pumpWidget(
MaterialApp(
home: Builder(
builder: (BuildContext context) {
return RaisedButton(
child: const Text('X'),
onPressed: () {
showDatePicker(
context: context,
initialDate: initialDate,
firstDate: firstDate,
lastDate: lastDate,
);
},
);
},
),
),
);
await tester.tap(find.text('X'));
await tester.pumpAndSettle();
}
testWidgets('common screen size - portrait', (WidgetTester tester) async {
await _showPicker(tester, kCommonScreenSizePortrait);
expect(tester.takeException(), isNull);
});
testWidgets('common screen size - landscape', (WidgetTester tester) async {
await _showPicker(tester, kCommonScreenSizeLandscape);
expect(tester.takeException(), isNull);
});
testWidgets('common screen size - portrait - textScale 1.3', (WidgetTester tester) async {
await _showPicker(tester, kCommonScreenSizePortrait, 1.3);
expect(tester.takeException(), isNull);
});
testWidgets('common screen size - landscape - textScale 1.3', (WidgetTester tester) async {
await _showPicker(tester, kCommonScreenSizeLandscape, 1.3);
expect(tester.takeException(), isNull);
});
testWidgets('small screen size - portrait', (WidgetTester tester) async {
await _showPicker(tester, kSmallScreenSizePortrait);
expect(tester.takeException(), isNull);
});
testWidgets('small screen size - landscape', (WidgetTester tester) async {
await _showPicker(tester, kSmallScreenSizeLandscape);
expect(tester.takeException(), isNull);
});
testWidgets('small screen size - portrait -textScale 1.3', (WidgetTester tester) async {
await _showPicker(tester, kSmallScreenSizePortrait, 1.3);
expect(tester.takeException(), isNull);
});
testWidgets('small screen size - landscape - textScale 1.3', (WidgetTester tester) async {
await _showPicker(tester, kSmallScreenSizeLandscape, 1.3);
expect(tester.takeException(), isNull);
});
});
testWidgets('uses root navigator by default', (WidgetTester tester) async {
final DatePickerObserver rootObserver = DatePickerObserver();
final DatePickerObserver nestedObserver = DatePickerObserver();
await tester.pumpWidget(MaterialApp(
navigatorObservers: <NavigatorObserver>[rootObserver],
home: Navigator(
observers: <NavigatorObserver>[nestedObserver],
onGenerateRoute: (RouteSettings settings) {
return MaterialPageRoute<dynamic>(
builder: (BuildContext context) {
return RaisedButton(
onPressed: () {
showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2018),
lastDate: DateTime(2030),
builder: (BuildContext context, Widget child) {
return const SizedBox();
},
);
},
child: const Text('Show Date Picker'),
);
},
);
},
),
));
// Open the dialog.
await tester.tap(find.byType(RaisedButton));
expect(rootObserver.datePickerCount, 1);
expect(nestedObserver.datePickerCount, 0);
});
testWidgets('uses nested navigator if useRootNavigator is false', (WidgetTester tester) async {
final DatePickerObserver rootObserver = DatePickerObserver();
final DatePickerObserver nestedObserver = DatePickerObserver();
await tester.pumpWidget(MaterialApp(
navigatorObservers: <NavigatorObserver>[rootObserver],
home: Navigator(
observers: <NavigatorObserver>[nestedObserver],
onGenerateRoute: (RouteSettings settings) {
return MaterialPageRoute<dynamic>(
builder: (BuildContext context) {
return RaisedButton(
onPressed: () {
showDatePicker(
context: context,
useRootNavigator: false,
initialDate: DateTime.now(),
firstDate: DateTime(2018),
lastDate: DateTime(2030),
builder: (BuildContext context, Widget child) => const SizedBox(),
);
},
child: const Text('Show Date Picker'),
);
},
);
},
),
));
// Open the dialog.
await tester.tap(find.byType(RaisedButton));
expect(rootObserver.datePickerCount, 0);
expect(nestedObserver.datePickerCount, 1);
});
}
class DatePickerObserver extends NavigatorObserver {
int datePickerCount = 0;
@override
void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
if (route.toString().contains('_DialogRoute')) {
datePickerCount++;
}
super.didPush(route, previousRoute);
}
}