Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit d79295a

Browse filesBrowse files
christopherfujinojonahwilliamsxu-baolingoderbauer
authored
[flutter_releases] Flutter Stable 2.2.2 Framework Cherrypicks (flutter#84364)
* [flutter_tools] throw a tool exit if pub cannot be run (flutter#83293) * Re-add the removed MediaQuery.removePadding of PopupMenuButton (flutter#82986) * import pkg:intl when DateFormat or NumberFormat is used (flutter#83122) Co-authored-by: Jonah Williams <jonahwilliams@google.com> Co-authored-by: xubaolin <xubaolin@oppo.com> Co-authored-by: Michael Goderbauer <goderbauer@google.com>
1 parent 02c026b commit d79295a
Copy full SHA for d79295a

File tree

Expand file treeCollapse file tree

7 files changed

+227
-43
lines changed
Filter options
Expand file treeCollapse file tree

7 files changed

+227
-43
lines changed

‎bin/internal/engine.version

Copy file name to clipboard
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0fdb562ac8068ce3dda6b69aca3f355f4d1d2718
1+
91c9fc8fe011352879e3bb6660966eafc0847233

‎packages/flutter/lib/src/material/popup_menu.dart

Copy file name to clipboardExpand all lines: packages/flutter/lib/src/material/popup_menu.dart
+36-34Lines changed: 36 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -618,8 +618,7 @@ class _PopupMenuRouteLayout extends SingleChildLayoutDelegate {
618618
this.itemSizes,
619619
this.selectedItemIndex,
620620
this.textDirection,
621-
this.topPadding,
622-
this.bottomPadding,
621+
this.padding,
623622
);
624623

625624
// Rectangle of underlying button, relative to the overlay's dimensions.
@@ -636,11 +635,8 @@ class _PopupMenuRouteLayout extends SingleChildLayoutDelegate {
636635
// Whether to prefer going to the left or to the right.
637636
final TextDirection textDirection;
638637

639-
// Top padding of unsafe area.
640-
final double topPadding;
641-
642-
// Bottom padding of unsafe area.
643-
final double bottomPadding;
638+
// The padding of unsafe area.
639+
EdgeInsets padding;
644640

645641
// We put the child wherever position specifies, so long as it will fit within
646642
// the specified parent size padded (inset) by 8. If necessary, we adjust the
@@ -651,7 +647,8 @@ class _PopupMenuRouteLayout extends SingleChildLayoutDelegate {
651647
// The menu can be at most the size of the overlay minus 8.0 pixels in each
652648
// direction.
653649
return BoxConstraints.loose(constraints.biggest).deflate(
654-
const EdgeInsets.all(_kMenuScreenPadding) + EdgeInsets.only(top: topPadding, bottom: bottomPadding));
650+
const EdgeInsets.all(_kMenuScreenPadding) + padding,
651+
);
655652
}
656653

657654
@override
@@ -694,14 +691,15 @@ class _PopupMenuRouteLayout extends SingleChildLayoutDelegate {
694691

695692
// Avoid going outside an area defined as the rectangle 8.0 pixels from the
696693
// edge of the screen in every direction.
697-
if (x < _kMenuScreenPadding)
698-
x = _kMenuScreenPadding;
699-
else if (x + childSize.width > size.width - _kMenuScreenPadding)
700-
x = size.width - childSize.width - _kMenuScreenPadding;
701-
if (y < _kMenuScreenPadding + topPadding)
702-
y = _kMenuScreenPadding + topPadding;
703-
else if (y + childSize.height > size.height - _kMenuScreenPadding - bottomPadding)
704-
y = size.height - bottomPadding - _kMenuScreenPadding - childSize.height ;
694+
if (x < _kMenuScreenPadding + padding.left)
695+
x = _kMenuScreenPadding + padding.left;
696+
else if (x + childSize.width > size.width - _kMenuScreenPadding - padding.right)
697+
x = size.width - childSize.width - _kMenuScreenPadding - padding.right ;
698+
if (y < _kMenuScreenPadding + padding.top)
699+
y = _kMenuScreenPadding + padding.top;
700+
else if (y + childSize.height > size.height - _kMenuScreenPadding - padding.bottom)
701+
y = size.height - padding.bottom - _kMenuScreenPadding - childSize.height ;
702+
705703
return Offset(x, y);
706704
}
707705

@@ -716,8 +714,7 @@ class _PopupMenuRouteLayout extends SingleChildLayoutDelegate {
716714
|| selectedItemIndex != oldDelegate.selectedItemIndex
717715
|| textDirection != oldDelegate.textDirection
718716
|| !listEquals(itemSizes, oldDelegate.itemSizes)
719-
|| topPadding != oldDelegate.topPadding
720-
|| bottomPadding != oldDelegate.bottomPadding;
717+
|| padding != oldDelegate.padding;
721718
}
722719
}
723720

@@ -777,22 +774,27 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
777774
}
778775

779776
final Widget menu = _PopupMenu<T>(route: this, semanticLabel: semanticLabel);
780-
781-
return Builder(
782-
builder: (BuildContext context) {
783-
final MediaQueryData mediaQuery = MediaQuery.of(context);
784-
return CustomSingleChildLayout(
785-
delegate: _PopupMenuRouteLayout(
786-
position,
787-
itemSizes,
788-
selectedItemIndex,
789-
Directionality.of(context),
790-
mediaQuery.padding.top,
791-
mediaQuery.padding.bottom,
792-
),
793-
child: capturedThemes.wrap(menu),
794-
);
795-
},
777+
final MediaQueryData mediaQuery = MediaQuery.of(context);
778+
return MediaQuery.removePadding(
779+
context: context,
780+
removeTop: true,
781+
removeBottom: true,
782+
removeLeft: true,
783+
removeRight: true,
784+
child: Builder(
785+
builder: (BuildContext context) {
786+
return CustomSingleChildLayout(
787+
delegate: _PopupMenuRouteLayout(
788+
position,
789+
itemSizes,
790+
selectedItemIndex,
791+
Directionality.of(context),
792+
mediaQuery.padding,
793+
),
794+
child: capturedThemes.wrap(menu),
795+
);
796+
},
797+
),
796798
);
797799
}
798800
}

‎packages/flutter/test/material/popup_menu_test.dart

Copy file name to clipboardExpand all lines: packages/flutter/test/material/popup_menu_test.dart
+73Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2058,6 +2058,79 @@ void main() {
20582058
expect(popupMenu, Offset(button.dx - 8.0, button.dy + 8.0));
20592059
});
20602060

2061+
// Regression test for https://github.com/flutter/flutter/issues/82874
2062+
testWidgets('PopupMenu position test when have unsafe area - left/right padding', (WidgetTester tester) async {
2063+
final GlobalKey buttonKey = GlobalKey();
2064+
const EdgeInsets padding = EdgeInsets.only(left: 300.0, top: 32.0, right: 310.0, bottom: 64.0);
2065+
EdgeInsets? mediaQueryPadding;
2066+
2067+
Widget buildFrame(double width, double height) {
2068+
return MaterialApp(
2069+
builder: (BuildContext context, Widget? child) {
2070+
return MediaQuery(
2071+
data: const MediaQueryData(
2072+
padding: padding,
2073+
),
2074+
child: child!,
2075+
);
2076+
},
2077+
home: Scaffold(
2078+
appBar: AppBar(
2079+
title: const Text('PopupMenu Test'),
2080+
actions: <Widget>[PopupMenuButton<int>(
2081+
child: SizedBox(
2082+
key: buttonKey,
2083+
height: height,
2084+
width: width,
2085+
child: const ColoredBox(
2086+
color: Colors.pink,
2087+
),
2088+
),
2089+
itemBuilder: (BuildContext context) {
2090+
return <PopupMenuEntry<int>>[
2091+
PopupMenuItem<int>(
2092+
value: 1,
2093+
child: Builder(
2094+
builder: (BuildContext context) {
2095+
mediaQueryPadding = MediaQuery.of(context).padding;
2096+
return Text('-1-' * 500); // A long long text string.
2097+
},
2098+
),
2099+
),
2100+
const PopupMenuItem<int>(value: 2, child: Text('-2-')),
2101+
];
2102+
},
2103+
)],
2104+
),
2105+
body: const SizedBox.shrink(),
2106+
),
2107+
);
2108+
}
2109+
2110+
await tester.pumpWidget(buildFrame(20.0, 20.0));
2111+
2112+
await tester.tap(find.byKey(buttonKey));
2113+
await tester.pumpAndSettle();
2114+
2115+
final Offset button = tester.getTopRight(find.byKey(buttonKey));
2116+
expect(button, Offset(800.0 - padding.right, padding.top)); // The topPadding is 32.0.
2117+
2118+
final Offset popupMenuTopRight = tester.getTopRight(find.byType(SingleChildScrollView));
2119+
2120+
// The menu should be positioned directly next to the top of the button.
2121+
// The 8.0 pixels is [_kMenuScreenPadding].
2122+
expect(popupMenuTopRight, Offset(800.0 - padding.right - 8.0, padding.top + 8.0));
2123+
2124+
final Offset popupMenuTopLeft = tester.getTopLeft(find.byType(SingleChildScrollView));
2125+
expect(popupMenuTopLeft, Offset(padding.left + 8.0, padding.top + 8.0));
2126+
2127+
final Offset popupMenuBottomLeft = tester.getBottomLeft(find.byType(SingleChildScrollView));
2128+
expect(popupMenuBottomLeft, Offset(padding.left + 8.0, 600.0 - padding.bottom - 8.0));
2129+
2130+
// The `MediaQueryData.padding` should be removed.
2131+
expect(mediaQueryPadding, EdgeInsets.zero);
2132+
});
2133+
20612134
group('feedback', () {
20622135
late FeedbackTester feedback;
20632136

‎packages/flutter_tools/lib/src/dart/pub.dart

Copy file name to clipboardExpand all lines: packages/flutter_tools/lib/src/dart/pub.dart
+11-5Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -151,14 +151,16 @@ class _DefaultPub implements Pub {
151151
_processUtils = ProcessUtils(
152152
logger: logger,
153153
processManager: processManager,
154-
);
154+
),
155+
_processManager = processManager;
155156

156157
final FileSystem _fileSystem;
157158
final Logger _logger;
158159
final ProcessUtils _processUtils;
159160
final Platform _platform;
160161
final BotDetector _botDetector;
161162
final Usage _usage;
163+
final ProcessManager _processManager;
162164

163165
@override
164166
Future<void> get({
@@ -393,11 +395,15 @@ class _DefaultPub implements Pub {
393395
'cache',
394396
'dart-sdk',
395397
'bin',
396-
if (_platform.isWindows)
397-
'pub.bat'
398-
else
399-
'pub'
398+
'pub',
400399
]);
400+
if (!_processManager.canRun(sdkPath)) {
401+
throwToolExit(
402+
'Your Flutter SDK download may be corrupt or missing permissions to run. '
403+
'Try re-downloading the Flutter SDK into a directory that has read/write '
404+
'permissions for the current user.'
405+
);
406+
}
401407
return <String>[sdkPath, ...arguments];
402408
}
403409

‎packages/flutter_tools/lib/src/localizations/gen_l10n.dart

Copy file name to clipboardExpand all lines: packages/flutter_tools/lib/src/localizations/gen_l10n.dart
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,7 +1029,7 @@ class LocalizationsGenerator {
10291029
.replaceAll('@(class)', '$className${locale.camelCase()}')
10301030
.replaceAll('@(localeName)', locale.toString())
10311031
.replaceAll('@(methods)', methods.join('\n\n'))
1032-
.replaceAll('@(requiresIntlImport)', _containsPluralMessage() ? "import 'package:intl/intl.dart' as intl;" : '');
1032+
.replaceAll('@(requiresIntlImport)', _requiresIntlImport() ? "import 'package:intl/intl.dart' as intl;" : '');
10331033
}
10341034

10351035
String _generateSubclass(
@@ -1170,12 +1170,12 @@ class LocalizationsGenerator {
11701170
.replaceAll('@(messageClassImports)', sortedClassImports.join('\n'))
11711171
.replaceAll('@(delegateClass)', delegateClass)
11721172
.replaceAll('@(requiresFoundationImport)', _useDeferredLoading ? '' : "import 'package:flutter/foundation.dart';")
1173-
.replaceAll('@(requiresIntlImport)', _containsPluralMessage() ? "import 'package:intl/intl.dart' as intl;" : '')
1173+
.replaceAll('@(requiresIntlImport)', _requiresIntlImport() ? "import 'package:intl/intl.dart' as intl;" : '')
11741174
.replaceAll('@(canBeNullable)', _usesNullableGetter ? '?' : '')
11751175
.replaceAll('@(needsNullCheck)', _usesNullableGetter ? '' : '!');
11761176
}
11771177

1178-
bool _containsPluralMessage() => _allMessages.any((Message message) => message.isPlural);
1178+
bool _requiresIntlImport() => _allMessages.any((Message message) => message.isPlural || message.placeholdersRequireFormatting);
11791179

11801180
void writeOutputFiles(Logger logger, { bool isFromYaml = false }) {
11811181
// First, generate the string contents of all necessary files.

‎packages/flutter_tools/test/general.shard/dart/pub_get_test.dart

Copy file name to clipboardExpand all lines: packages/flutter_tools/test/general.shard/dart/pub_get_test.dart
+28Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,31 @@ void main() {
2626
Cache.flutterRoot = '';
2727
});
2828

29+
testWithoutContext('Throws a tool exit if pub cannot be run', () async {
30+
final FakeProcessManager processManager = FakeProcessManager.any();
31+
final BufferLogger logger = BufferLogger.test();
32+
final MemoryFileSystem fileSystem = MemoryFileSystem.test();
33+
processManager.excludedExecutables.add('bin/cache/dart-sdk/bin/pub');
34+
35+
fileSystem.file('pubspec.yaml').createSync();
36+
37+
final Pub pub = Pub(
38+
fileSystem: fileSystem,
39+
logger: logger,
40+
processManager: processManager,
41+
usage: TestUsage(),
42+
platform: FakePlatform(
43+
environment: const <String, String>{},
44+
),
45+
botDetector: const BotDetectorAlwaysNo(),
46+
);
47+
48+
await expectLater(() => pub.get(
49+
context: PubContext.pubGet,
50+
checkUpToDate: true,
51+
), throwsToolExit(message: 'Your Flutter SDK download may be corrupt or missing permissions to run'));
52+
});
53+
2954
testWithoutContext('checkUpToDate skips pub get if the package config is newer than the pubspec '
3055
'and the current framework version is the same as the last version', () async {
3156
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[]);
@@ -716,6 +741,9 @@ class MockProcessManager implements ProcessManager {
716741
));
717742
}
718743

744+
@override
745+
bool canRun(dynamic executable, {String workingDirectory}) => true;
746+
719747
@override
720748
dynamic noSuchMethod(Invocation invocation) => null;
721749
}

‎packages/flutter_tools/test/general.shard/generate_localizations_test.dart

Copy file name to clipboardExpand all lines: packages/flutter_tools/test/general.shard/generate_localizations_test.dart
+75Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1568,6 +1568,42 @@ import 'output-localization-file_en.dart' deferred as output-localization-file_e
15681568
});
15691569

15701570
group('DateTime tests', () {
1571+
testUsingContext('imports package:intl', () {
1572+
const String singleDateMessageArbFileString = '''
1573+
{
1574+
"@@locale": "en",
1575+
"springBegins": "Spring begins on {springStartDate}",
1576+
"@springBegins": {
1577+
"description": "The first day of spring",
1578+
"placeholders": {
1579+
"springStartDate": {
1580+
"type": "DateTime",
1581+
"format": "yMd"
1582+
}
1583+
}
1584+
}
1585+
}''';
1586+
fs.currentDirectory.childDirectory('lib').childDirectory('l10n')..createSync(recursive: true)
1587+
..childFile(defaultTemplateArbFileName).writeAsStringSync(singleDateMessageArbFileString);
1588+
1589+
LocalizationsGenerator(
1590+
fs,
1591+
)
1592+
..initialize(
1593+
inputPathString: defaultL10nPathString,
1594+
outputPathString: defaultL10nPathString,
1595+
templateArbFileName: defaultTemplateArbFileName,
1596+
outputFileString: defaultOutputFileString,
1597+
classNameString: defaultClassNameString)
1598+
..loadResources()
1599+
..writeOutputFiles(BufferLogger.test());
1600+
1601+
final String localizationsFile = fs.file(
1602+
fs.path.join(syntheticL10nPackagePath, 'output-localization-file_en.dart'),
1603+
).readAsStringSync();
1604+
expect(localizationsFile, contains(intlImportDartCode));
1605+
});
1606+
15711607
testUsingContext('throws an exception when improperly formatted date is passed in', () {
15721608
const String singleDateMessageArbFileString = '''
15731609
{
@@ -1644,6 +1680,45 @@ import 'output-localization-file_en.dart' deferred as output-localization-file_e
16441680

16451681
fail('Improper date formatting should throw an exception');
16461682
});
1683+
});
1684+
1685+
group('NumberFormat tests', () {
1686+
testUsingContext('imports package:intl', () {
1687+
const String singleDateMessageArbFileString = '''
1688+
{
1689+
"courseCompletion": "You have completed {progress} of the course.",
1690+
"@courseCompletion": {
1691+
"description": "The amount of progress the student has made in their class.",
1692+
"placeholders": {
1693+
"progress": {
1694+
"type": "double",
1695+
"format": "percentPattern"
1696+
}
1697+
}
1698+
}
1699+
}''';
1700+
fs.currentDirectory.childDirectory('lib').childDirectory('l10n')
1701+
..createSync(recursive: true)
1702+
..childFile(defaultTemplateArbFileName).writeAsStringSync(
1703+
singleDateMessageArbFileString);
1704+
1705+
LocalizationsGenerator(
1706+
fs,
1707+
)
1708+
..initialize(
1709+
inputPathString: defaultL10nPathString,
1710+
outputPathString: defaultL10nPathString,
1711+
templateArbFileName: defaultTemplateArbFileName,
1712+
outputFileString: defaultOutputFileString,
1713+
classNameString: defaultClassNameString)
1714+
..loadResources()
1715+
..writeOutputFiles(BufferLogger.test());
1716+
1717+
final String localizationsFile = fs.file(
1718+
fs.path.join(syntheticL10nPackagePath, 'output-localization-file_en.dart'),
1719+
).readAsStringSync();
1720+
expect(localizationsFile, contains(intlImportDartCode));
1721+
});
16471722

16481723
testUsingContext('throws an exception when improperly formatted number is passed in', () {
16491724
const String singleDateMessageArbFileString = '''

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.