mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

This is not a grand refactor yet, it's just cleaning up what we have already, so that people who keep using this API (e.g. dialogs) have something coherent to deal with. The major changes are that Navigator and NavigatorState have the same API now, that most of the examples use `<void>` instead of `<Null>`, that the navigator observer can see replaces, and that the `settings` is moved from ModalRoute to Route. I also cleaned up some of the API documentation.
198 lines
8.8 KiB
Dart
198 lines
8.8 KiB
Dart
// Copyright 2016 The Chromium 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/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
class _TimePickerLauncher extends StatelessWidget {
|
|
const _TimePickerLauncher({ Key key, this.onChanged, this.locale }) : super(key: key);
|
|
|
|
final ValueChanged<TimeOfDay> onChanged;
|
|
final Locale locale;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return new MaterialApp(
|
|
locale: locale,
|
|
localizationsDelegates: GlobalMaterialLocalizations.delegates,
|
|
home: new Material(
|
|
child: new Center(
|
|
child: new Builder(
|
|
builder: (BuildContext context) {
|
|
return new RaisedButton(
|
|
child: const Text('X'),
|
|
onPressed: () async {
|
|
onChanged(await showTimePicker(
|
|
context: context,
|
|
initialTime: const TimeOfDay(hour: 7, minute: 0)
|
|
));
|
|
}
|
|
);
|
|
}
|
|
)
|
|
)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
Future<Offset> startPicker(WidgetTester tester, ValueChanged<TimeOfDay> onChanged,
|
|
{ Locale locale: const Locale('en', 'US') }) async {
|
|
await tester.pumpWidget(new _TimePickerLauncher(onChanged: onChanged, locale: locale,));
|
|
await tester.tap(find.text('X'));
|
|
await tester.pumpAndSettle(const Duration(seconds: 1));
|
|
return tester.getCenter(find.byKey(const Key('time-picker-dial')));
|
|
}
|
|
|
|
Future<Null> finishPicker(WidgetTester tester) async {
|
|
final MaterialLocalizations materialLocalizations = MaterialLocalizations.of(tester.element(find.byType(RaisedButton)));
|
|
await tester.tap(find.text(materialLocalizations.okButtonLabel));
|
|
await tester.pumpAndSettle(const Duration(seconds: 1));
|
|
}
|
|
|
|
void main() {
|
|
testWidgets('can localize the header in all known formats', (WidgetTester tester) async {
|
|
// TODO(yjbanov): also test `HH.mm` (in_ID), `a h:mm` (ko_KR) and `HH:mm น.` (th_TH) when we have .arb files for them
|
|
final Map<Locale, List<String>> locales = <Locale, List<String>>{
|
|
const Locale('en', 'US'): const <String>['hour', 'string :', 'minute', 'period'], //'h:mm a'
|
|
const Locale('en', 'GB'): const <String>['hour', 'string :', 'minute'], //'HH:mm'
|
|
const Locale('es', 'ES'): const <String>['hour', 'string :', 'minute'], //'H:mm'
|
|
const Locale('fr', 'CA'): const <String>['hour', 'string h', 'minute'], //'HH \'h\' mm'
|
|
const Locale('zh', 'ZH'): const <String>['period', 'hour', 'string :', 'minute'], //'ah:mm'
|
|
};
|
|
|
|
for (Locale locale in locales.keys) {
|
|
final Offset center = await startPicker(tester, (TimeOfDay time) { }, locale: locale);
|
|
final List<String> actual = <String>[];
|
|
tester.element(find.byType(CustomMultiChildLayout)).visitChildren((Element child) {
|
|
final LayoutId layout = child.widget;
|
|
final String fragmentType = '${layout.child.runtimeType}';
|
|
final dynamic widget = layout.child;
|
|
if (fragmentType == '_MinuteControl') {
|
|
actual.add('minute');
|
|
} else if (fragmentType == '_DayPeriodControl') {
|
|
actual.add('period');
|
|
} else if (fragmentType == '_HourControl') {
|
|
actual.add('hour');
|
|
} else if (fragmentType == '_StringFragment') {
|
|
actual.add('string ${widget.value}');
|
|
} else {
|
|
fail('Unsupported fragment type: $fragmentType');
|
|
}
|
|
});
|
|
expect(actual, locales[locale]);
|
|
await tester.tapAt(new Offset(center.dx, center.dy - 50.0));
|
|
await finishPicker(tester);
|
|
}
|
|
});
|
|
|
|
testWidgets('uses single-ring 12-hour dial for h hour format', (WidgetTester tester) async {
|
|
// Tap along the segment stretching from the center to the edge at
|
|
// 12:00 AM position. Because there's only one ring, no matter where you
|
|
// tap the time will be the same. See the 24-hour dial test that behaves
|
|
// differently.
|
|
for (int i = 1; i < 10; i++) {
|
|
TimeOfDay result;
|
|
final Offset center = await startPicker(tester, (TimeOfDay time) { result = time; });
|
|
final Size size = tester.getSize(find.byKey(const Key('time-picker-dial')));
|
|
final double dy = (size.height / 2.0 / 10) * i;
|
|
await tester.tapAt(new Offset(center.dx, center.dy - dy));
|
|
await finishPicker(tester);
|
|
expect(result, equals(const TimeOfDay(hour: 0, minute: 0)));
|
|
}
|
|
});
|
|
|
|
testWidgets('uses two-ring 24-hour dial for H and HH hour formats', (WidgetTester tester) async {
|
|
const List<Locale> locales = const <Locale>[
|
|
const Locale('en', 'GB'), // HH
|
|
const Locale('es', 'ES'), // H
|
|
];
|
|
for (Locale locale in locales) {
|
|
// Tap along the segment stretching from the center to the edge at
|
|
// 12:00 AM position. There are two rings. At ~70% mark, the ring
|
|
// switches between inner ring and outer ring.
|
|
for (int i = 1; i < 10; i++) {
|
|
TimeOfDay result;
|
|
final Offset center = await startPicker(tester, (TimeOfDay time) { result = time; }, locale: locale);
|
|
final Size size = tester.getSize(find.byKey(const Key('time-picker-dial')));
|
|
final double dy = (size.height / 2.0 / 10) * i;
|
|
await tester.tapAt(new Offset(center.dx, center.dy - dy));
|
|
await finishPicker(tester);
|
|
expect(result, equals(new TimeOfDay(hour: i < 7 ? 12 : 0, minute: 0)));
|
|
}
|
|
}
|
|
});
|
|
|
|
const List<String> labels12To11 = const <String>['12', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11'];
|
|
const List<String> labels12To11TwoDigit = const <String>['12', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11'];
|
|
const List<String> labels00To23 = const <String>['00', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23'];
|
|
|
|
Future<Null> mediaQueryBoilerplate(WidgetTester tester, bool alwaysUse24HourFormat) async {
|
|
await tester.pumpWidget(
|
|
new Localizations(
|
|
locale: const Locale('en', 'US'),
|
|
delegates: const <LocalizationsDelegate<dynamic>>[
|
|
GlobalMaterialLocalizations.delegate,
|
|
DefaultWidgetsLocalizations.delegate,
|
|
],
|
|
child: new MediaQuery(
|
|
data: new MediaQueryData(alwaysUse24HourFormat: alwaysUse24HourFormat),
|
|
child: new Material(
|
|
child: new Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: new Navigator(
|
|
onGenerateRoute: (RouteSettings settings) {
|
|
return new MaterialPageRoute<void>(builder: (BuildContext context) {
|
|
return new FlatButton(
|
|
onPressed: () {
|
|
showTimePicker(context: context, initialTime: const TimeOfDay(hour: 7, minute: 0));
|
|
},
|
|
child: const Text('X'),
|
|
);
|
|
});
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.tap(find.text('X'));
|
|
await tester.pumpAndSettle();
|
|
}
|
|
|
|
testWidgets('respects MediaQueryData.alwaysUse24HourFormat == false', (WidgetTester tester) async {
|
|
await mediaQueryBoilerplate(tester, false);
|
|
|
|
final CustomPaint dialPaint = tester.widget(find.byKey(const ValueKey<String>('time-picker-dial')));
|
|
final dynamic dialPainter = dialPaint.painter;
|
|
final List<dynamic> primaryOuterLabels = dialPainter.primaryOuterLabels;
|
|
expect(primaryOuterLabels.map<String>((dynamic tp) => tp.painter.text.text), labels12To11);
|
|
expect(dialPainter.primaryInnerLabels, null);
|
|
|
|
final List<dynamic> secondaryOuterLabels = dialPainter.secondaryOuterLabels;
|
|
expect(secondaryOuterLabels.map<String>((dynamic tp) => tp.painter.text.text), labels12To11);
|
|
expect(dialPainter.secondaryInnerLabels, null);
|
|
});
|
|
|
|
testWidgets('respects MediaQueryData.alwaysUse24HourFormat == true', (WidgetTester tester) async {
|
|
await mediaQueryBoilerplate(tester, true);
|
|
|
|
final CustomPaint dialPaint = tester.widget(find.byKey(const ValueKey<String>('time-picker-dial')));
|
|
final dynamic dialPainter = dialPaint.painter;
|
|
final List<dynamic> primaryOuterLabels = dialPainter.primaryOuterLabels;
|
|
expect(primaryOuterLabels.map<String>((dynamic tp) => tp.painter.text.text), labels00To23);
|
|
final List<dynamic> primaryInnerLabels = dialPainter.primaryInnerLabels;
|
|
expect(primaryInnerLabels.map<String>((dynamic tp) => tp.painter.text.text), labels12To11TwoDigit);
|
|
|
|
final List<dynamic> secondaryOuterLabels = dialPainter.secondaryOuterLabels;
|
|
expect(secondaryOuterLabels.map<String>((dynamic tp) => tp.painter.text.text), labels00To23);
|
|
final List<dynamic> secondaryInnerLabels = dialPainter.secondaryInnerLabels;
|
|
expect(secondaryInnerLabels.map<String>((dynamic tp) => tp.painter.text.text), labels12To11TwoDigit);
|
|
});
|
|
}
|