mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
WIP Commits separated as follows: - Update lints in analysis_options files - Run `dart fix --apply` - Clean up leftover analysis issues - Run `dart format .` in the right places. Local analysis and testing passes. Checking CI now. Part of https://github.com/flutter/flutter/issues/178827 - Adoption of flutter_lints in examples/api coming in a separate change (cc @loic-sharma) ## Pre-launch Checklist - [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ ] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [ ] I signed the [CLA]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I added new tests to check the change I am making, or this PR is [test-exempt]. - [ ] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
947 lines
37 KiB
Dart
947 lines
37 KiB
Dart
// 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_localizations/flutter_localizations.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
final Finder dialCustomPaintFinder = find.descendant(
|
|
of: find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_Dial'),
|
|
matching: find.byType(CustomPaint),
|
|
);
|
|
|
|
void main() {
|
|
testWidgets('Material2 - can localize the header in all known formats - portrait', (
|
|
WidgetTester tester,
|
|
) async {
|
|
// Ensure picker is displayed in portrait mode.
|
|
tester.view.physicalSize = const Size(400, 800);
|
|
tester.view.devicePixelRatio = 1;
|
|
addTearDown(tester.view.reset);
|
|
|
|
final Finder timeSelectorSeparatorFinder = find
|
|
.descendant(
|
|
of: find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_TimeSelectorSeparator'),
|
|
matching: find.byType(Text),
|
|
)
|
|
.first;
|
|
final Finder dialHourControlFinder = find.byWidgetPredicate(
|
|
(Widget w) => '${w.runtimeType}' == '_DialHourControl',
|
|
);
|
|
final Finder dialMinuteControlFinder = find.byWidgetPredicate(
|
|
(Widget w) => '${w.runtimeType}' == '_DialMinuteControl',
|
|
);
|
|
final Finder dayPeriodControlFinder = find.byWidgetPredicate(
|
|
(Widget w) => '${w.runtimeType}' == '_DayPeriodControl',
|
|
);
|
|
|
|
// 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 locales = <Locale>[
|
|
const Locale('en', 'US'), //'h:mm a'
|
|
const Locale('en', 'GB'), //'HH:mm'
|
|
const Locale('es', 'ES'), //'H:mm'
|
|
const Locale('fr', 'CA'), //'HH \'h\' mm'
|
|
const Locale('zh', 'ZH'), //'ah:mm'
|
|
const Locale('fa', 'IR'), //'H:mm' but RTL
|
|
];
|
|
|
|
for (final locale in locales) {
|
|
final Offset center = await startPicker(
|
|
tester,
|
|
(TimeOfDay? time) {},
|
|
locale: locale,
|
|
useMaterial3: false,
|
|
);
|
|
final Text stringFragmentText = tester.widget(timeSelectorSeparatorFinder);
|
|
final double hourLeftOffset = tester.getTopLeft(dialHourControlFinder).dx;
|
|
final double minuteLeftOffset = tester.getTopLeft(dialMinuteControlFinder).dx;
|
|
final double stringFragmentLeftOffset = tester.getTopLeft(timeSelectorSeparatorFinder).dx;
|
|
|
|
if (locale == const Locale('en', 'US')) {
|
|
final double dayPeriodLeftOffset = tester.getTopLeft(dayPeriodControlFinder).dx;
|
|
expect(stringFragmentText.data, ':');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(minuteLeftOffset, lessThan(dayPeriodLeftOffset));
|
|
} else if (locale == const Locale('en', 'GB')) {
|
|
expect(stringFragmentText.data, ':');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(dayPeriodControlFinder, findsNothing);
|
|
} else if (locale == const Locale('es', 'ES')) {
|
|
expect(stringFragmentText.data, ':');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(dayPeriodControlFinder, findsNothing);
|
|
} else if (locale == const Locale('fr', 'CA')) {
|
|
expect(stringFragmentText.data, 'h');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(dayPeriodControlFinder, findsNothing);
|
|
} else if (locale == const Locale('zh', 'ZH')) {
|
|
final double dayPeriodLeftOffset = tester.getTopLeft(dayPeriodControlFinder).dx;
|
|
expect(stringFragmentText.data, ':');
|
|
expect(dayPeriodLeftOffset, lessThan(hourLeftOffset));
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
} else if (locale == const Locale('fa', 'IR')) {
|
|
// Even though this is an RTL locale, the hours and minutes positions should remain the same.
|
|
expect(stringFragmentText.data, ':');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(dayPeriodControlFinder, findsNothing);
|
|
}
|
|
await tester.tapAt(Offset(center.dx, center.dy - 50.0));
|
|
await finishPicker(tester);
|
|
}
|
|
});
|
|
|
|
testWidgets('Material3 - can localize the header in all known formats - portrait', (
|
|
WidgetTester tester,
|
|
) async {
|
|
// Ensure picker is displayed in portrait mode.
|
|
tester.view.physicalSize = const Size(400, 800);
|
|
tester.view.devicePixelRatio = 1;
|
|
addTearDown(tester.view.reset);
|
|
|
|
final Finder timeSelectorSeparatorFinder = find
|
|
.descendant(
|
|
of: find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_TimeSelectorSeparator'),
|
|
matching: find.byType(Text),
|
|
)
|
|
.first;
|
|
final Finder dialHourControlFinder = find.byWidgetPredicate(
|
|
(Widget w) => '${w.runtimeType}' == '_DialHourControl',
|
|
);
|
|
final Finder dialMinuteControlFinder = find.byWidgetPredicate(
|
|
(Widget w) => '${w.runtimeType}' == '_DialMinuteControl',
|
|
);
|
|
final Finder dayPeriodControlFinder = find.byWidgetPredicate(
|
|
(Widget w) => '${w.runtimeType}' == '_DayPeriodControl',
|
|
);
|
|
|
|
// 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 locales = <Locale>[
|
|
const Locale('en', 'US'), //'h:mm a'
|
|
const Locale('en', 'GB'), //'HH:mm'
|
|
const Locale('es', 'ES'), //'H:mm'
|
|
const Locale('fr', 'CA'), //'HH \'h\' mm'
|
|
const Locale('zh', 'ZH'), //'ah:mm'
|
|
const Locale('fa', 'IR'), //'H:mm' but RTL
|
|
];
|
|
|
|
for (final locale in locales) {
|
|
final Offset center = await startPicker(
|
|
tester,
|
|
(TimeOfDay? time) {},
|
|
locale: locale,
|
|
useMaterial3: true,
|
|
);
|
|
final Text stringFragmentText = tester.widget(timeSelectorSeparatorFinder);
|
|
final double hourLeftOffset = tester.getTopLeft(dialHourControlFinder).dx;
|
|
final double minuteLeftOffset = tester.getTopLeft(dialMinuteControlFinder).dx;
|
|
final double stringFragmentLeftOffset = tester.getTopLeft(timeSelectorSeparatorFinder).dx;
|
|
|
|
if (locale == const Locale('en', 'US')) {
|
|
final double dayPeriodLeftOffset = tester.getTopLeft(dayPeriodControlFinder).dx;
|
|
expect(stringFragmentText.data, ':');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(minuteLeftOffset, lessThan(dayPeriodLeftOffset));
|
|
} else if (locale == const Locale('en', 'GB')) {
|
|
expect(stringFragmentText.data, ':');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(dayPeriodControlFinder, findsNothing);
|
|
} else if (locale == const Locale('es', 'ES')) {
|
|
expect(stringFragmentText.data, ':');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(dayPeriodControlFinder, findsNothing);
|
|
} else if (locale == const Locale('fr', 'CA')) {
|
|
expect(stringFragmentText.data, 'h');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(dayPeriodControlFinder, findsNothing);
|
|
} else if (locale == const Locale('zh', 'ZH')) {
|
|
final double dayPeriodLeftOffset = tester.getTopLeft(dayPeriodControlFinder).dx;
|
|
expect(stringFragmentText.data, ':');
|
|
expect(dayPeriodLeftOffset, lessThan(hourLeftOffset));
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
} else if (locale == const Locale('fa', 'IR')) {
|
|
// Even though this is an RTL locale, the hours and minutes positions should remain the same.
|
|
expect(stringFragmentText.data, ':');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(dayPeriodControlFinder, findsNothing);
|
|
}
|
|
await tester.tapAt(Offset(center.dx, center.dy - 50.0));
|
|
await finishPicker(tester);
|
|
}
|
|
});
|
|
|
|
testWidgets('Material2 - can localize the header in all known formats - landscape', (
|
|
WidgetTester tester,
|
|
) async {
|
|
// Ensure picker is displayed in landscape mode.
|
|
tester.view.physicalSize = const Size(800, 400);
|
|
tester.view.devicePixelRatio = 1;
|
|
addTearDown(tester.view.reset);
|
|
|
|
final Finder timeSelectorSeparatorFinder = find
|
|
.descendant(
|
|
of: find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_TimeSelectorSeparator'),
|
|
matching: find.byType(Text),
|
|
)
|
|
.first;
|
|
final Finder dialHourControlFinder = find.byWidgetPredicate(
|
|
(Widget w) => '${w.runtimeType}' == '_DialHourControl',
|
|
);
|
|
final Finder dialMinuteControlFinder = find.byWidgetPredicate(
|
|
(Widget w) => '${w.runtimeType}' == '_DialMinuteControl',
|
|
);
|
|
final Finder dayPeriodControlFinder = find.byWidgetPredicate(
|
|
(Widget w) => '${w.runtimeType}' == '_DayPeriodControl',
|
|
);
|
|
|
|
// 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 locales = <Locale>[
|
|
const Locale('en', 'US'), //'h:mm a'
|
|
const Locale('en', 'GB'), //'HH:mm'
|
|
const Locale('es', 'ES'), //'H:mm'
|
|
const Locale('fr', 'CA'), //'HH \'h\' mm'
|
|
const Locale('zh', 'ZH'), //'ah:mm'
|
|
const Locale('fa', 'IR'), //'H:mm' but RTL
|
|
];
|
|
|
|
for (final locale in locales) {
|
|
final Offset center = await startPicker(
|
|
tester,
|
|
(TimeOfDay? time) {},
|
|
locale: locale,
|
|
useMaterial3: false,
|
|
);
|
|
final Text stringFragmentText = tester.widget(timeSelectorSeparatorFinder);
|
|
final double hourLeftOffset = tester.getTopLeft(dialHourControlFinder).dx;
|
|
final double hourTopOffset = tester.getTopLeft(dialHourControlFinder).dy;
|
|
final double minuteLeftOffset = tester.getTopLeft(dialMinuteControlFinder).dx;
|
|
final double stringFragmentLeftOffset = tester.getTopLeft(timeSelectorSeparatorFinder).dx;
|
|
|
|
if (locale == const Locale('en', 'US')) {
|
|
final double dayPeriodLeftOffset = tester.getTopLeft(dayPeriodControlFinder).dx;
|
|
final double dayPeriodTopOffset = tester.getTopLeft(dayPeriodControlFinder).dy;
|
|
expect(stringFragmentText.data, ':');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(hourLeftOffset, dayPeriodLeftOffset);
|
|
expect(hourTopOffset, lessThan(dayPeriodTopOffset));
|
|
} else if (locale == const Locale('en', 'GB')) {
|
|
expect(stringFragmentText.data, ':');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(dayPeriodControlFinder, findsNothing);
|
|
} else if (locale == const Locale('es', 'ES')) {
|
|
expect(stringFragmentText.data, ':');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(dayPeriodControlFinder, findsNothing);
|
|
} else if (locale == const Locale('fr', 'CA')) {
|
|
expect(stringFragmentText.data, 'h');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(dayPeriodControlFinder, findsNothing);
|
|
} else if (locale == const Locale('zh', 'ZH')) {
|
|
final double dayPeriodLeftOffset = tester.getTopLeft(dayPeriodControlFinder).dx;
|
|
final double dayPeriodTopOffset = tester.getTopLeft(dayPeriodControlFinder).dy;
|
|
expect(stringFragmentText.data, ':');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(hourLeftOffset, dayPeriodLeftOffset);
|
|
expect(hourTopOffset, greaterThan(dayPeriodTopOffset));
|
|
} else if (locale == const Locale('fa', 'IR')) {
|
|
// Even though this is an RTL locale, the hours and minutes positions should remain the same.
|
|
expect(stringFragmentText.data, ':');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(dayPeriodControlFinder, findsNothing);
|
|
}
|
|
await tester.tapAt(Offset(center.dx, center.dy - 50.0));
|
|
await finishPicker(tester);
|
|
}
|
|
});
|
|
|
|
testWidgets('Material3 - can localize the header in all known formats - landscape', (
|
|
WidgetTester tester,
|
|
) async {
|
|
// Ensure picker is displayed in landscape mode.
|
|
tester.view.physicalSize = const Size(800, 400);
|
|
tester.view.devicePixelRatio = 1;
|
|
addTearDown(tester.view.reset);
|
|
|
|
final Finder timeSelectorSeparatorFinder = find
|
|
.descendant(
|
|
of: find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_TimeSelectorSeparator'),
|
|
matching: find.byType(Text),
|
|
)
|
|
.first;
|
|
final Finder dialHourControlFinder = find.byWidgetPredicate(
|
|
(Widget w) => '${w.runtimeType}' == '_DialHourControl',
|
|
);
|
|
final Finder dialMinuteControlFinder = find.byWidgetPredicate(
|
|
(Widget w) => '${w.runtimeType}' == '_DialMinuteControl',
|
|
);
|
|
final Finder dayPeriodControlFinder = find.byWidgetPredicate(
|
|
(Widget w) => '${w.runtimeType}' == '_DayPeriodControl',
|
|
);
|
|
|
|
// 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 locales = <Locale>[
|
|
const Locale('en', 'US'), //'h:mm a'
|
|
const Locale('en', 'GB'), //'HH:mm'
|
|
const Locale('es', 'ES'), //'H:mm'
|
|
const Locale('fr', 'CA'), //'HH \'h\' mm'
|
|
const Locale('zh', 'ZH'), //'ah:mm'
|
|
const Locale('fa', 'IR'), //'H:mm' but RTL
|
|
];
|
|
|
|
for (final locale in locales) {
|
|
final Offset center = await startPicker(
|
|
tester,
|
|
(TimeOfDay? time) {},
|
|
locale: locale,
|
|
useMaterial3: true,
|
|
);
|
|
final Text stringFragmentText = tester.widget(timeSelectorSeparatorFinder);
|
|
final double hourLeftOffset = tester.getTopLeft(dialHourControlFinder).dx;
|
|
final double hourTopOffset = tester.getTopLeft(dialHourControlFinder).dy;
|
|
final double minuteLeftOffset = tester.getTopLeft(dialMinuteControlFinder).dx;
|
|
final double stringFragmentLeftOffset = tester.getTopLeft(timeSelectorSeparatorFinder).dx;
|
|
|
|
if (locale == const Locale('en', 'US')) {
|
|
final double dayPeriodLeftOffset = tester.getTopLeft(dayPeriodControlFinder).dx;
|
|
final double dayPeriodTopOffset = tester.getTopLeft(dayPeriodControlFinder).dy;
|
|
expect(stringFragmentText.data, ':');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(hourLeftOffset, dayPeriodLeftOffset);
|
|
expect(hourTopOffset, lessThan(dayPeriodTopOffset));
|
|
} else if (locale == const Locale('en', 'GB')) {
|
|
expect(stringFragmentText.data, ':');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(dayPeriodControlFinder, findsNothing);
|
|
} else if (locale == const Locale('es', 'ES')) {
|
|
expect(stringFragmentText.data, ':');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(dayPeriodControlFinder, findsNothing);
|
|
} else if (locale == const Locale('fr', 'CA')) {
|
|
expect(stringFragmentText.data, 'h');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(dayPeriodControlFinder, findsNothing);
|
|
} else if (locale == const Locale('zh', 'ZH')) {
|
|
final double dayPeriodLeftOffset = tester.getTopLeft(dayPeriodControlFinder).dx;
|
|
final double dayPeriodTopOffset = tester.getTopLeft(dayPeriodControlFinder).dy;
|
|
expect(stringFragmentText.data, ':');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(hourLeftOffset, dayPeriodLeftOffset);
|
|
expect(hourTopOffset, greaterThan(dayPeriodTopOffset));
|
|
} else if (locale == const Locale('fa', 'IR')) {
|
|
// Even though this is an RTL locale, the hours and minutes positions should remain the same.
|
|
expect(stringFragmentText.data, ':');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(dayPeriodControlFinder, findsNothing);
|
|
}
|
|
await tester.tapAt(Offset(center.dx, center.dy - 50.0));
|
|
await finishPicker(tester);
|
|
}
|
|
});
|
|
|
|
testWidgets('Material2 - can localize input mode in all known formats', (
|
|
WidgetTester tester,
|
|
) async {
|
|
final Finder inputHourControlFinder = find.byWidgetPredicate(
|
|
(Widget w) => '${w.runtimeType}' == '_HourTextField',
|
|
);
|
|
final Finder inputMinuteControlFinder = find.byWidgetPredicate(
|
|
(Widget w) => '${w.runtimeType}' == '_MinuteTextField',
|
|
);
|
|
final Finder dayPeriodControlFinder = find.byWidgetPredicate(
|
|
(Widget w) => '${w.runtimeType}' == '_DayPeriodControl',
|
|
);
|
|
final Finder timeSelectorSeparatorFinder = find
|
|
.descendant(
|
|
of: find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_TimeSelectorSeparator'),
|
|
matching: find.byType(Text),
|
|
)
|
|
.first;
|
|
|
|
// 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 locales = <Locale>[
|
|
const Locale('en', 'US'), //'h:mm a'
|
|
const Locale('en', 'GB'), //'HH:mm'
|
|
const Locale('es', 'ES'), //'H:mm'
|
|
const Locale('fr', 'CA'), //'HH \'h\' mm'
|
|
const Locale('zh', 'ZH'), //'ah:mm'
|
|
const Locale('fa', 'IR'), //'H:mm' but RTL
|
|
];
|
|
|
|
for (final locale in locales) {
|
|
await tester.pumpWidget(
|
|
_TimePickerLauncher(
|
|
onChanged: (TimeOfDay? time) {},
|
|
locale: locale,
|
|
entryMode: TimePickerEntryMode.input,
|
|
useMaterial3: false,
|
|
),
|
|
);
|
|
await tester.tap(find.text('X'));
|
|
await tester.pumpAndSettle(const Duration(seconds: 1));
|
|
|
|
final Text stringFragmentText = tester.widget(timeSelectorSeparatorFinder);
|
|
final double hourLeftOffset = tester.getTopLeft(inputHourControlFinder).dx;
|
|
final double minuteLeftOffset = tester.getTopLeft(inputMinuteControlFinder).dx;
|
|
final double stringFragmentLeftOffset = tester.getTopLeft(timeSelectorSeparatorFinder).dx;
|
|
|
|
if (locale == const Locale('en', 'US')) {
|
|
final double dayPeriodLeftOffset = tester.getTopLeft(dayPeriodControlFinder).dx;
|
|
expect(stringFragmentText.data, ':');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(minuteLeftOffset, lessThan(dayPeriodLeftOffset));
|
|
} else if (locale == const Locale('en', 'GB')) {
|
|
expect(stringFragmentText.data, ':');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(dayPeriodControlFinder, findsNothing);
|
|
} else if (locale == const Locale('es', 'ES')) {
|
|
expect(stringFragmentText.data, ':');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(dayPeriodControlFinder, findsNothing);
|
|
} else if (locale == const Locale('fr', 'CA')) {
|
|
expect(stringFragmentText.data, 'h');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(dayPeriodControlFinder, findsNothing);
|
|
} else if (locale == const Locale('zh', 'ZH')) {
|
|
final double dayPeriodLeftOffset = tester.getTopLeft(dayPeriodControlFinder).dx;
|
|
expect(stringFragmentText.data, ':');
|
|
expect(dayPeriodLeftOffset, lessThan(hourLeftOffset));
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
} else if (locale == const Locale('fa', 'IR')) {
|
|
// Even though this is an RTL locale, the hours and minutes positions should remain the same.
|
|
expect(stringFragmentText.data, ':');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(dayPeriodControlFinder, findsNothing);
|
|
}
|
|
await finishPicker(tester);
|
|
expect(tester.takeException(), isNot(throwsFlutterError));
|
|
}
|
|
});
|
|
|
|
testWidgets('Material3 - can localize input mode in all known formats', (
|
|
WidgetTester tester,
|
|
) async {
|
|
final Finder inputHourControlFinder = find.byWidgetPredicate(
|
|
(Widget w) => '${w.runtimeType}' == '_HourTextField',
|
|
);
|
|
final Finder inputMinuteControlFinder = find.byWidgetPredicate(
|
|
(Widget w) => '${w.runtimeType}' == '_MinuteTextField',
|
|
);
|
|
final Finder dayPeriodControlFinder = find.byWidgetPredicate(
|
|
(Widget w) => '${w.runtimeType}' == '_DayPeriodControl',
|
|
);
|
|
final Finder timeSelectorSeparatorFinder = find
|
|
.descendant(
|
|
of: find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_TimeSelectorSeparator'),
|
|
matching: find.byType(Text),
|
|
)
|
|
.first;
|
|
|
|
// 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 locales = <Locale>[
|
|
const Locale('en', 'US'), //'h:mm a'
|
|
const Locale('en', 'GB'), //'HH:mm'
|
|
const Locale('es', 'ES'), //'H:mm'
|
|
const Locale('fr', 'CA'), //'HH \'h\' mm'
|
|
const Locale('zh', 'ZH'), //'ah:mm'
|
|
const Locale('fa', 'IR'), //'H:mm' but RTL
|
|
];
|
|
|
|
for (final locale in locales) {
|
|
await tester.pumpWidget(
|
|
_TimePickerLauncher(
|
|
onChanged: (TimeOfDay? time) {},
|
|
locale: locale,
|
|
entryMode: TimePickerEntryMode.input,
|
|
useMaterial3: true,
|
|
),
|
|
);
|
|
await tester.tap(find.text('X'));
|
|
await tester.pumpAndSettle(const Duration(seconds: 1));
|
|
|
|
final Text stringFragmentText = tester.widget(timeSelectorSeparatorFinder);
|
|
final double hourLeftOffset = tester.getTopLeft(inputHourControlFinder).dx;
|
|
final double minuteLeftOffset = tester.getTopLeft(inputMinuteControlFinder).dx;
|
|
final double stringFragmentLeftOffset = tester.getTopLeft(timeSelectorSeparatorFinder).dx;
|
|
|
|
if (locale == const Locale('en', 'US')) {
|
|
final double dayPeriodLeftOffset = tester.getTopLeft(dayPeriodControlFinder).dx;
|
|
expect(stringFragmentText.data, ':');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(minuteLeftOffset, lessThan(dayPeriodLeftOffset));
|
|
} else if (locale == const Locale('en', 'GB')) {
|
|
expect(stringFragmentText.data, ':');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(dayPeriodControlFinder, findsNothing);
|
|
} else if (locale == const Locale('es', 'ES')) {
|
|
expect(stringFragmentText.data, ':');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(dayPeriodControlFinder, findsNothing);
|
|
} else if (locale == const Locale('fr', 'CA')) {
|
|
expect(stringFragmentText.data, 'h');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(dayPeriodControlFinder, findsNothing);
|
|
} else if (locale == const Locale('zh', 'ZH')) {
|
|
final double dayPeriodLeftOffset = tester.getTopLeft(dayPeriodControlFinder).dx;
|
|
expect(stringFragmentText.data, ':');
|
|
expect(dayPeriodLeftOffset, lessThan(hourLeftOffset));
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
} else if (locale == const Locale('fa', 'IR')) {
|
|
// Even though this is an RTL locale, the hours and minutes positions should remain the same.
|
|
expect(stringFragmentText.data, ':');
|
|
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
|
|
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
|
|
expect(dayPeriodControlFinder, findsNothing);
|
|
}
|
|
await finishPicker(tester);
|
|
expect(tester.takeException(), isNot(throwsFlutterError));
|
|
}
|
|
});
|
|
|
|
testWidgets('Material2 uses single-ring 24-hour dial for all locales', (
|
|
WidgetTester tester,
|
|
) async {
|
|
const locales = <Locale>[
|
|
Locale('en', 'US'), // h
|
|
Locale('en', 'GB'), // HH
|
|
Locale('es', 'ES'), // H
|
|
];
|
|
for (final locale in locales) {
|
|
// Tap along the segment stretching from the center to the edge at
|
|
// 12:00 AM position. Because there's only one ring, in the M2
|
|
// DatePicker no matter where you tap the time will be the same.
|
|
for (var i = 1; i < 10; i++) {
|
|
TimeOfDay? result;
|
|
final Offset center = await startPicker(
|
|
tester,
|
|
(TimeOfDay? time) {
|
|
result = time;
|
|
},
|
|
locale: locale,
|
|
useMaterial3: false,
|
|
);
|
|
final Size size = tester.getSize(dialCustomPaintFinder);
|
|
final double dy = (size.height / 2.0 / 10) * i;
|
|
await tester.tapAt(Offset(center.dx, center.dy - dy));
|
|
await finishPicker(tester);
|
|
expect(result, equals(const TimeOfDay(hour: 0, minute: 0)));
|
|
}
|
|
}
|
|
});
|
|
|
|
testWidgets('Material3 uses a double-ring 24-hour dial for 24 hour locales', (
|
|
WidgetTester tester,
|
|
) async {
|
|
Future<void> testLocale(
|
|
Locale locale,
|
|
int startFactor,
|
|
int endFactor,
|
|
TimeOfDay expectedTime,
|
|
) async {
|
|
// For locales that display 24 hour time, factors 1-5 put the tap on the
|
|
// inner ring's "12" (the inner ring goes from 12-23). Otherwise the offset
|
|
// should land on the outer ring's "00".
|
|
for (var factor = startFactor; factor < endFactor; factor += 1) {
|
|
TimeOfDay? result;
|
|
final Offset center = await startPicker(
|
|
tester,
|
|
(TimeOfDay? time) {
|
|
result = time;
|
|
},
|
|
locale: locale,
|
|
useMaterial3: true,
|
|
);
|
|
final Size size = tester.getSize(dialCustomPaintFinder);
|
|
final double dy = (size.height / 2.0 / 10) * factor;
|
|
await tester.tapAt(Offset(center.dx, center.dy - dy));
|
|
await finishPicker(tester);
|
|
expect(
|
|
result,
|
|
equals(expectedTime),
|
|
reason: 'Failed for locale=$locale with factor=$factor',
|
|
);
|
|
}
|
|
}
|
|
|
|
await testLocale(
|
|
const Locale('en', 'US'),
|
|
1,
|
|
10,
|
|
const TimeOfDay(hour: 0, minute: 0),
|
|
); // 12 hour
|
|
await testLocale(
|
|
const Locale('en', 'ES'),
|
|
1,
|
|
10,
|
|
const TimeOfDay(hour: 0, minute: 0),
|
|
); // 12 hour
|
|
await testLocale(
|
|
const Locale('en', 'GB'),
|
|
1,
|
|
5,
|
|
const TimeOfDay(hour: 12, minute: 0),
|
|
); // 24 hour, inner ring
|
|
await testLocale(
|
|
const Locale('en', 'GB'),
|
|
6,
|
|
10,
|
|
const TimeOfDay(hour: 0, minute: 0),
|
|
); // 24 hour, outer ring
|
|
});
|
|
|
|
const labels12To11 = <String>['12', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11'];
|
|
const labels00To22TwoDigit = <String>[
|
|
'00',
|
|
'02',
|
|
'04',
|
|
'06',
|
|
'08',
|
|
'10',
|
|
'12',
|
|
'14',
|
|
'16',
|
|
'18',
|
|
'20',
|
|
'22',
|
|
]; // Material 2
|
|
const labels00To23TwoDigit = <String>[
|
|
// Material 3
|
|
'00',
|
|
'1',
|
|
'2',
|
|
'3',
|
|
'4',
|
|
'5',
|
|
'6',
|
|
'7',
|
|
'8',
|
|
'9',
|
|
'10',
|
|
'11',
|
|
'12',
|
|
'13',
|
|
'14',
|
|
'15',
|
|
'16',
|
|
'17',
|
|
'18',
|
|
'19',
|
|
'20',
|
|
'21',
|
|
'22',
|
|
'23',
|
|
];
|
|
|
|
Future<void> mediaQueryBoilerplate(
|
|
WidgetTester tester, {
|
|
required bool alwaysUse24HourFormat,
|
|
required bool useMaterial3,
|
|
}) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(useMaterial3: useMaterial3),
|
|
builder: (BuildContext context, Widget? child) {
|
|
return MediaQuery(
|
|
data: MediaQueryData(alwaysUse24HourFormat: alwaysUse24HourFormat),
|
|
child: child!,
|
|
);
|
|
},
|
|
home: Material(
|
|
child: Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: Navigator(
|
|
onGenerateRoute: (RouteSettings settings) {
|
|
return MaterialPageRoute<void>(
|
|
builder: (BuildContext context) {
|
|
return TextButton(
|
|
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('Material2 respects MediaQueryData.alwaysUse24HourFormat == false', (
|
|
WidgetTester tester,
|
|
) async {
|
|
await mediaQueryBoilerplate(tester, alwaysUse24HourFormat: false, useMaterial3: false);
|
|
|
|
final CustomPaint dialPaint = tester.widget(dialCustomPaintFinder);
|
|
final dynamic dialPainter = dialPaint.painter;
|
|
// ignore: avoid_dynamic_calls
|
|
final primaryLabels = dialPainter.primaryLabels as List<dynamic>;
|
|
expect(
|
|
primaryLabels.map<String>(
|
|
// ignore: avoid_dynamic_calls
|
|
(dynamic tp) => ((tp.painter as TextPainter).text! as TextSpan).text!,
|
|
),
|
|
labels12To11,
|
|
);
|
|
|
|
// ignore: avoid_dynamic_calls
|
|
final selectedLabels = dialPainter.selectedLabels as List<dynamic>;
|
|
expect(
|
|
selectedLabels.map<String>(
|
|
// ignore: avoid_dynamic_calls
|
|
(dynamic tp) => ((tp.painter as TextPainter).text! as TextSpan).text!,
|
|
),
|
|
labels12To11,
|
|
);
|
|
});
|
|
|
|
testWidgets('Material3 respects MediaQueryData.alwaysUse24HourFormat == false', (
|
|
WidgetTester tester,
|
|
) async {
|
|
await mediaQueryBoilerplate(tester, alwaysUse24HourFormat: false, useMaterial3: true);
|
|
|
|
final CustomPaint dialPaint = tester.widget(dialCustomPaintFinder);
|
|
final dynamic dialPainter = dialPaint.painter;
|
|
// ignore: avoid_dynamic_calls
|
|
final primaryLabels = dialPainter.primaryLabels as List<dynamic>;
|
|
expect(
|
|
primaryLabels.map<String>(
|
|
// ignore: avoid_dynamic_calls
|
|
(dynamic tp) => ((tp.painter as TextPainter).text! as TextSpan).text!,
|
|
),
|
|
labels12To11,
|
|
);
|
|
|
|
// ignore: avoid_dynamic_calls
|
|
final selectedLabels = dialPainter.selectedLabels as List<dynamic>;
|
|
expect(
|
|
selectedLabels.map<String>(
|
|
// ignore: avoid_dynamic_calls
|
|
(dynamic tp) => ((tp.painter as TextPainter).text! as TextSpan).text!,
|
|
),
|
|
labels12To11,
|
|
);
|
|
});
|
|
|
|
testWidgets('Material3 respects MediaQueryData.alwaysUse24HourFormat == true', (
|
|
WidgetTester tester,
|
|
) async {
|
|
await mediaQueryBoilerplate(tester, alwaysUse24HourFormat: true, useMaterial3: true);
|
|
|
|
final CustomPaint dialPaint = tester.widget(dialCustomPaintFinder);
|
|
final dynamic dialPainter = dialPaint.painter;
|
|
// ignore: avoid_dynamic_calls
|
|
final primaryLabels = dialPainter.primaryLabels as List<dynamic>;
|
|
expect(
|
|
primaryLabels.map<String>(
|
|
// ignore: avoid_dynamic_calls
|
|
(dynamic tp) => ((tp.painter as TextPainter).text! as TextSpan).text!,
|
|
),
|
|
labels00To23TwoDigit,
|
|
);
|
|
|
|
// ignore: avoid_dynamic_calls
|
|
final selectedLabels = dialPainter.selectedLabels as List<dynamic>;
|
|
expect(
|
|
selectedLabels.map<String>(
|
|
// ignore: avoid_dynamic_calls
|
|
(dynamic tp) => ((tp.painter as TextPainter).text! as TextSpan).text!,
|
|
),
|
|
labels00To23TwoDigit,
|
|
);
|
|
});
|
|
|
|
testWidgets('Material2 respects MediaQueryData.alwaysUse24HourFormat == true', (
|
|
WidgetTester tester,
|
|
) async {
|
|
await mediaQueryBoilerplate(tester, alwaysUse24HourFormat: true, useMaterial3: false);
|
|
|
|
final CustomPaint dialPaint = tester.widget(dialCustomPaintFinder);
|
|
final dynamic dialPainter = dialPaint.painter;
|
|
// ignore: avoid_dynamic_calls
|
|
final primaryLabels = dialPainter.primaryLabels as List<dynamic>;
|
|
expect(
|
|
primaryLabels.map<String>(
|
|
// ignore: avoid_dynamic_calls
|
|
(dynamic tp) => ((tp.painter as TextPainter).text! as TextSpan).text!,
|
|
),
|
|
labels00To22TwoDigit,
|
|
);
|
|
|
|
// ignore: avoid_dynamic_calls
|
|
final selectedLabels = dialPainter.selectedLabels as List<dynamic>;
|
|
expect(
|
|
selectedLabels.map<String>(
|
|
// ignore: avoid_dynamic_calls
|
|
(dynamic tp) => ((tp.painter as TextPainter).text! as TextSpan).text!,
|
|
),
|
|
labels00To22TwoDigit,
|
|
);
|
|
});
|
|
|
|
// Regression test for https://github.com/flutter/flutter/issues/156565
|
|
testWidgets('AM/PM buttons should be aligned to LTR in Hindi language - Portrait', (
|
|
WidgetTester tester,
|
|
) async {
|
|
const locale = Locale('hi', 'HI');
|
|
|
|
final Offset centerPortrait = await startPicker(
|
|
tester,
|
|
(TimeOfDay? time) {},
|
|
locale: locale,
|
|
useMaterial3: false,
|
|
orientation: Orientation.portrait,
|
|
);
|
|
|
|
final Finder amButtonPortrait = find.text('AM');
|
|
final Finder pmButtonPortrait = find.text('PM');
|
|
|
|
final Offset amButtonPositionPortrait = tester.getCenter(amButtonPortrait);
|
|
final Offset pmButtonPositionPortrait = tester.getCenter(pmButtonPortrait);
|
|
|
|
expect(amButtonPositionPortrait.dx, greaterThan(centerPortrait.dx));
|
|
expect(pmButtonPositionPortrait.dx, greaterThan(centerPortrait.dx));
|
|
});
|
|
|
|
// Regression test for https://github.com/flutter/flutter/issues/156565
|
|
testWidgets('AM/PM buttons should be aligned to LTR in Hindi language - Landscape', (
|
|
WidgetTester tester,
|
|
) async {
|
|
const locale = Locale('hi', 'HI');
|
|
|
|
final Offset centerLandscape = await startPicker(
|
|
tester,
|
|
(TimeOfDay? time) {},
|
|
locale: locale,
|
|
useMaterial3: false,
|
|
orientation: Orientation.landscape,
|
|
);
|
|
|
|
final Finder amButtonLandscape = find.text('AM');
|
|
final Finder pmButtonLandscape = find.text('PM');
|
|
|
|
final Offset amButtonPositionLandscape = tester.getCenter(amButtonLandscape);
|
|
final Offset pmButtonPositionLandscape = tester.getCenter(pmButtonLandscape);
|
|
|
|
expect(amButtonPositionLandscape.dy, greaterThan(centerLandscape.dy));
|
|
expect(pmButtonPositionLandscape.dy, greaterThan(centerLandscape.dy));
|
|
});
|
|
}
|
|
|
|
class _TimePickerLauncher extends StatelessWidget {
|
|
const _TimePickerLauncher({
|
|
this.onChanged,
|
|
required this.locale,
|
|
this.entryMode = TimePickerEntryMode.dial,
|
|
this.useMaterial3,
|
|
this.orientation,
|
|
});
|
|
|
|
final ValueChanged<TimeOfDay?>? onChanged;
|
|
final Locale locale;
|
|
final TimePickerEntryMode entryMode;
|
|
final bool? useMaterial3;
|
|
final Orientation? orientation;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return MaterialApp(
|
|
theme: ThemeData(useMaterial3: useMaterial3),
|
|
locale: locale,
|
|
supportedLocales: <Locale>[locale],
|
|
localizationsDelegates: GlobalMaterialLocalizations.delegates,
|
|
home: Material(
|
|
child: Center(
|
|
child: Builder(
|
|
builder: (BuildContext context) {
|
|
return ElevatedButton(
|
|
child: const Text('X'),
|
|
onPressed: () async {
|
|
onChanged?.call(
|
|
await showTimePicker(
|
|
context: context,
|
|
initialEntryMode: entryMode,
|
|
orientation: orientation,
|
|
initialTime: const TimeOfDay(hour: 7, minute: 0),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
Future<Offset> startPicker(
|
|
WidgetTester tester,
|
|
ValueChanged<TimeOfDay?> onChanged, {
|
|
Locale locale = const Locale('en', 'US'),
|
|
bool? useMaterial3,
|
|
Orientation? orientation,
|
|
}) async {
|
|
await tester.pumpWidget(
|
|
_TimePickerLauncher(
|
|
onChanged: onChanged,
|
|
locale: locale,
|
|
useMaterial3: useMaterial3,
|
|
orientation: orientation,
|
|
),
|
|
);
|
|
await tester.tap(find.text('X'));
|
|
await tester.pumpAndSettle(const Duration(seconds: 1));
|
|
return tester.getCenter(dialCustomPaintFinder);
|
|
}
|
|
|
|
Future<void> finishPicker(WidgetTester tester) async {
|
|
final MaterialLocalizations materialLocalizations = MaterialLocalizations.of(
|
|
tester.element(find.byType(ElevatedButton)),
|
|
);
|
|
await tester.tap(find.text(materialLocalizations.okButtonLabel));
|
|
await tester.pumpAndSettle(const Duration(seconds: 1));
|
|
}
|