mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Flutter 1.21.0-9.2.pre cherry picks (#64737)
* update engine hash * allow null in compute for weak mode (#63515) * [Material] Relanding fix to ensure time picker input mode lays out correctly in RTL (#64097) * pin customer-testing * [flutter_tool] Handle Windows line endings in packages_test.dart (#63806) * [flutter_tool] Fix some create_test.dart tests on Windows (#63796) Co-authored-by: Alexandre Ardhuin <alexandre.ardhuin@gmail.com> Co-authored-by: Rami <2364772+rami-a@users.noreply.github.com> Co-authored-by: Zachary Anderson <zanderso@users.noreply.github.com>
This commit is contained in:
parent
4680f49978
commit
81a45ec2e5
@ -277,6 +277,7 @@ task:
|
||||
script:
|
||||
- rm -rf bin/cache/pkg/tests
|
||||
- git clone https://github.com/flutter/tests.git bin/cache/pkg/tests
|
||||
- (cd bin/cache/pkg/tests; git checkout c72bcde)
|
||||
- dart --enable-asserts dev/customer_testing/run_tests.dart --skip-on-fetch-failure --skip-template bin/cache/pkg/tests/registry/*.test
|
||||
|
||||
# firebase_test_lab_tests are linux-only
|
||||
@ -458,6 +459,9 @@ task:
|
||||
script:
|
||||
- CMD /S /C "IF EXIST "bin\cache\pkg\tests\" RMDIR /S /Q bin\cache\pkg\tests"
|
||||
- git clone https://github.com/flutter/tests.git bin\cache\pkg\tests
|
||||
- cd bin\cache\pkg\tests
|
||||
- git checkout c72bcde
|
||||
- cd ..\..\..\..
|
||||
- dart --enable-asserts dev\customer_testing\run_tests.dart --skip-on-fetch-failure --skip-template bin/cache/pkg/tests/registry/*.test
|
||||
|
||||
# MACOS SHARDS
|
||||
@ -568,6 +572,7 @@ task:
|
||||
- ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976
|
||||
- rm -rf bin/cache/pkg/tests
|
||||
- git clone https://github.com/flutter/tests.git bin/cache/pkg/tests
|
||||
- (cd bin/cache/pkg/tests; git checkout c72bcde)
|
||||
- dart --enable-asserts dev/customer_testing/run_tests.dart --skip-on-fetch-failure --skip-template bin/cache/pkg/tests/registry/*.test
|
||||
|
||||
- name: deploy_gallery-macos # linux- and macos- only
|
||||
|
||||
@ -1 +1 @@
|
||||
267070c17a6956de1a03dbe09cda56f0c485f41b
|
||||
20a953183580250aac2e15d36007664118bda5ab
|
||||
|
||||
@ -50,7 +50,7 @@ Future<R> compute<Q, R>(isolates.ComputeCallback<Q, R> callback, Q message, { St
|
||||
}
|
||||
});
|
||||
resultPort.listen((dynamic resultData) {
|
||||
assert(resultData is R);
|
||||
assert(resultData == null || resultData is R);
|
||||
if (!result.isCompleted)
|
||||
result.complete(resultData as R);
|
||||
});
|
||||
|
||||
@ -1414,58 +1414,69 @@ class _TimePickerInputState extends State<_TimePickerInput> {
|
||||
),
|
||||
const SizedBox(width: 12.0),
|
||||
],
|
||||
Expanded(child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
const SizedBox(height: 8.0),
|
||||
_HourMinuteTextField(
|
||||
selectedTime: _selectedTime,
|
||||
isHour: true,
|
||||
style: hourMinuteStyle,
|
||||
validator: _validateHour,
|
||||
onSavedSubmitted: _handleHourSavedSubmitted,
|
||||
onChanged: _handleHourChanged,
|
||||
),
|
||||
const SizedBox(height: 8.0),
|
||||
if (!hourHasError && !minuteHasError)
|
||||
ExcludeSemantics(
|
||||
child: Text(
|
||||
MaterialLocalizations.of(context).timePickerHourLabel,
|
||||
style: theme.textTheme.caption,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
Expanded(
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// Hour/minutes should not change positions in RTL locales.
|
||||
textDirection: TextDirection.ltr,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
const SizedBox(height: 8.0),
|
||||
_HourTextField(
|
||||
selectedTime: _selectedTime,
|
||||
style: hourMinuteStyle,
|
||||
validator: _validateHour,
|
||||
onSavedSubmitted: _handleHourSavedSubmitted,
|
||||
onChanged: _handleHourChanged,
|
||||
),
|
||||
const SizedBox(height: 8.0),
|
||||
if (!hourHasError && !minuteHasError)
|
||||
ExcludeSemantics(
|
||||
child: Text(
|
||||
MaterialLocalizations.of(context).timePickerHourLabel,
|
||||
style: theme.textTheme.caption,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
Container(
|
||||
margin: const EdgeInsets.only(top: 8.0),
|
||||
height: _kTimePickerHeaderControlHeight,
|
||||
child: _StringFragment(timeOfDayFormat: timeOfDayFormat),
|
||||
Container(
|
||||
margin: const EdgeInsets.only(top: 8.0),
|
||||
height: _kTimePickerHeaderControlHeight,
|
||||
child: _StringFragment(timeOfDayFormat: timeOfDayFormat),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
const SizedBox(height: 8.0),
|
||||
_MinuteTextField(
|
||||
selectedTime: _selectedTime,
|
||||
style: hourMinuteStyle,
|
||||
validator: _validateMinute,
|
||||
onSavedSubmitted: _handleMinuteSavedSubmitted,
|
||||
),
|
||||
const SizedBox(height: 8.0),
|
||||
if (!hourHasError && !minuteHasError)
|
||||
ExcludeSemantics(
|
||||
child: Text(
|
||||
MaterialLocalizations.of(context).timePickerMinuteLabel,
|
||||
style: theme.textTheme.caption,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
const SizedBox(height: 8.0),
|
||||
_HourMinuteTextField(
|
||||
selectedTime: _selectedTime,
|
||||
isHour: false,
|
||||
style: hourMinuteStyle,
|
||||
validator: _validateMinute,
|
||||
onSavedSubmitted: _handleMinuteSavedSubmitted,
|
||||
),
|
||||
const SizedBox(height: 8.0),
|
||||
if (!hourHasError && !minuteHasError)
|
||||
ExcludeSemantics(
|
||||
child: Text(
|
||||
MaterialLocalizations.of(context).timePickerMinuteLabel,
|
||||
style: theme.textTheme.caption,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
if (!use24HourDials && timeOfDayFormat != TimeOfDayFormat.a_space_h_colon_mm) ...<Widget>[
|
||||
const SizedBox(width: 12.0),
|
||||
_DayPeriodControl(
|
||||
@ -1489,6 +1500,61 @@ class _TimePickerInputState extends State<_TimePickerInput> {
|
||||
}
|
||||
}
|
||||
|
||||
class _HourTextField extends StatelessWidget {
|
||||
const _HourTextField({
|
||||
Key key,
|
||||
@required this.selectedTime,
|
||||
@required this.style,
|
||||
@required this.validator,
|
||||
@required this.onSavedSubmitted,
|
||||
@required this.onChanged,
|
||||
}) : super(key: key);
|
||||
|
||||
final TimeOfDay selectedTime;
|
||||
final TextStyle style;
|
||||
final FormFieldValidator<String> validator;
|
||||
final ValueChanged<String> onSavedSubmitted;
|
||||
final ValueChanged<String> onChanged;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _HourMinuteTextField(
|
||||
selectedTime: selectedTime,
|
||||
isHour: true,
|
||||
style: style,
|
||||
validator: validator,
|
||||
onSavedSubmitted: onSavedSubmitted,
|
||||
onChanged: onChanged,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MinuteTextField extends StatelessWidget {
|
||||
const _MinuteTextField({
|
||||
Key key,
|
||||
@required this.selectedTime,
|
||||
@required this.style,
|
||||
@required this.validator,
|
||||
@required this.onSavedSubmitted,
|
||||
}) : super(key: key);
|
||||
|
||||
final TimeOfDay selectedTime;
|
||||
final TextStyle style;
|
||||
final FormFieldValidator<String> validator;
|
||||
final ValueChanged<String> onSavedSubmitted;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _HourMinuteTextField(
|
||||
selectedTime: selectedTime,
|
||||
isHour: false,
|
||||
style: style,
|
||||
validator: validator,
|
||||
onSavedSubmitted: onSavedSubmitted,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _HourMinuteTextField extends StatefulWidget {
|
||||
const _HourMinuteTextField({
|
||||
Key key,
|
||||
|
||||
@ -806,6 +806,16 @@ void _testsInput() {
|
||||
await finishPicker(tester);
|
||||
expect(result, equals(const TimeOfDay(hour: 8, minute: 15)));
|
||||
});
|
||||
|
||||
// Fixes regression that was reverted in https://github.com/flutter/flutter/pull/64094#pullrequestreview-469836378.
|
||||
testWidgets('Ensure hour/minute fields are top-aligned with the separator', (WidgetTester tester) async {
|
||||
await startPicker(tester, (TimeOfDay time) { }, entryMode: TimePickerEntryMode.input);
|
||||
final double hourFieldTop = tester.getTopLeft(find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_HourTextField')).dy;
|
||||
final double minuteFieldTop = tester.getTopLeft(find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_MinuteTextField')).dy;
|
||||
final double separatorTop = tester.getTopLeft(find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_StringFragment')).dy;
|
||||
expect(hourFieldTop, separatorTop);
|
||||
expect(minuteFieldTop, separatorTop);
|
||||
});
|
||||
}
|
||||
|
||||
final Finder findDialPaint = find.descendant(
|
||||
|
||||
@ -8,10 +8,16 @@ 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);
|
||||
const _TimePickerLauncher({
|
||||
Key key,
|
||||
this.onChanged,
|
||||
this.locale,
|
||||
this.entryMode = TimePickerEntryMode.dial,
|
||||
}) : super(key: key);
|
||||
|
||||
final ValueChanged<TimeOfDay> onChanged;
|
||||
final Locale locale;
|
||||
final TimePickerEntryMode entryMode;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -28,6 +34,7 @@ class _TimePickerLauncher extends StatelessWidget {
|
||||
onPressed: () async {
|
||||
onChanged(await showTimePicker(
|
||||
context: context,
|
||||
initialEntryMode: entryMode,
|
||||
initialTime: const TimeOfDay(hour: 7, minute: 0),
|
||||
));
|
||||
},
|
||||
@ -207,6 +214,73 @@ void main() {
|
||||
tester.binding.window.devicePixelRatioTestValue = null;
|
||||
});
|
||||
|
||||
testWidgets('can localize input mode in all known formats', (WidgetTester tester) async {
|
||||
final Finder stringFragmentTextFinder = find.descendant(
|
||||
of: find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_StringFragment'),
|
||||
matching: find.byType(Text),
|
||||
).first;
|
||||
final Finder hourControlFinder = find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_HourTextField');
|
||||
final Finder minuteControlFinder = find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_MinuteTextField');
|
||||
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 List<Locale> 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 locale in locales) {
|
||||
await tester.pumpWidget(_TimePickerLauncher(onChanged: (TimeOfDay time) { }, locale: locale, entryMode: TimePickerEntryMode.input));
|
||||
await tester.tap(find.text('X'));
|
||||
await tester.pumpAndSettle(const Duration(seconds: 1));
|
||||
|
||||
final Text stringFragmentText = tester.widget(stringFragmentTextFinder);
|
||||
final double hourLeftOffset = tester.getTopLeft(hourControlFinder).dx;
|
||||
final double minuteLeftOffset = tester.getTopLeft(minuteControlFinder).dx;
|
||||
final double stringFragmentLeftOffset = tester.getTopLeft(stringFragmentTextFinder).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);
|
||||
}
|
||||
});
|
||||
|
||||
testWidgets('uses single-ring 24-hour dial for all formats', (WidgetTester tester) async {
|
||||
const List<Locale> locales = <Locale>[
|
||||
Locale('en', 'US'), // h
|
||||
|
||||
@ -1121,7 +1121,7 @@ void main() {
|
||||
await runner.run(<String>['create', '--no-pub', projectDir.path]);
|
||||
|
||||
final String metadata = globals.fs.file(globals.fs.path.join(projectDir.path, '.metadata')).readAsStringSync();
|
||||
expect(metadata, contains('project_type: app\n'));
|
||||
expect(LineSplitter.split(metadata), contains('project_type: app'));
|
||||
});
|
||||
|
||||
testUsingContext('can re-gen default template over existing app project with no metadta and detect the type', () async {
|
||||
@ -1138,7 +1138,7 @@ void main() {
|
||||
await runner.run(<String>['create', '--no-pub', projectDir.path]);
|
||||
|
||||
final String metadata = globals.fs.file(globals.fs.path.join(projectDir.path, '.metadata')).readAsStringSync();
|
||||
expect(metadata, contains('project_type: app\n'));
|
||||
expect(LineSplitter.split(metadata), contains('project_type: app'));
|
||||
});
|
||||
|
||||
testUsingContext('can re-gen app template over existing app project and detect the type', () async {
|
||||
@ -1152,7 +1152,7 @@ void main() {
|
||||
await runner.run(<String>['create', '--no-pub', projectDir.path]);
|
||||
|
||||
final String metadata = globals.fs.file(globals.fs.path.join(projectDir.path, '.metadata')).readAsStringSync();
|
||||
expect(metadata, contains('project_type: app\n'));
|
||||
expect(LineSplitter.split(metadata), contains('project_type: app'));
|
||||
});
|
||||
|
||||
testUsingContext('can re-gen template over existing module project and detect the type', () async {
|
||||
@ -1166,7 +1166,7 @@ void main() {
|
||||
await runner.run(<String>['create', '--no-pub', projectDir.path]);
|
||||
|
||||
final String metadata = globals.fs.file(globals.fs.path.join(projectDir.path, '.metadata')).readAsStringSync();
|
||||
expect(metadata, contains('project_type: module\n'));
|
||||
expect(LineSplitter.split(metadata), contains('project_type: module'));
|
||||
});
|
||||
|
||||
testUsingContext('can re-gen default template over existing plugin project and detect the type', () async {
|
||||
@ -1180,7 +1180,7 @@ void main() {
|
||||
await runner.run(<String>['create', '--no-pub', projectDir.path]);
|
||||
|
||||
final String metadata = globals.fs.file(globals.fs.path.join(projectDir.path, '.metadata')).readAsStringSync();
|
||||
expect(metadata, contains('project_type: plugin'));
|
||||
expect(LineSplitter.split(metadata), contains('project_type: plugin'));
|
||||
});
|
||||
|
||||
testUsingContext('can re-gen default template over existing package project and detect the type', () async {
|
||||
@ -1194,7 +1194,7 @@ void main() {
|
||||
await runner.run(<String>['create', '--no-pub', projectDir.path]);
|
||||
|
||||
final String metadata = globals.fs.file(globals.fs.path.join(projectDir.path, '.metadata')).readAsStringSync();
|
||||
expect(metadata, contains('project_type: package'));
|
||||
expect(LineSplitter.split(metadata), contains('project_type: package'));
|
||||
});
|
||||
|
||||
testUsingContext('can re-gen module .android/ folder, reusing custom org', () async {
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:args/command_runner.dart';
|
||||
import 'package:flutter_tools/src/base/bot_detector.dart';
|
||||
@ -37,10 +38,14 @@ void main() {
|
||||
final String projectPath = await createProject(tempDir, arguments: arguments);
|
||||
final File pubspec = globals.fs.file(globals.fs.path.join(projectPath, 'pubspec.yaml'));
|
||||
String content = await pubspec.readAsString();
|
||||
content = content.replaceFirst(
|
||||
'\ndependencies:\n',
|
||||
'\ndependencies:\n $plugin:\n',
|
||||
);
|
||||
final List<String> contentLines = LineSplitter.split(content).toList();
|
||||
final int depsIndex = contentLines.indexOf('dependencies:');
|
||||
expect(depsIndex, isNot(-1));
|
||||
contentLines.replaceRange(depsIndex, depsIndex + 1, <String>[
|
||||
'dependencies:',
|
||||
' $plugin:',
|
||||
]);
|
||||
content = contentLines.join('\n');
|
||||
await pubspec.writeAsString(content, flush: true);
|
||||
return projectPath;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user