From ab0a33597328772fe27dbc0b217ac930373ef5f0 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Sun, 12 Dec 2021 13:05:03 -0800 Subject: [PATCH] Ban sync*/async* from user facing code (#95050) --- dev/bots/analyze.dart | 33 ++ dev/bots/test.dart | 2 + ...eletable_chip_attributes.on_deleted.0.dart | 8 +- .../lib/widgets/async/stream_builder.0.dart | 1 + ...ti_child_render_object_widget_mixin.0.dart | 14 +- .../lib/src/animation/listener_helpers.dart | 16 +- packages/flutter/lib/src/cupertino/app.dart | 10 +- .../flutter/lib/src/cupertino/dialog.dart | 6 +- .../lib/src/foundation/assertions.dart | 8 +- .../lib/src/foundation/basic_types.dart | 6 +- .../lib/src/foundation/change_notifier.dart | 8 +- .../lib/src/foundation/diagnostics.dart | 20 +- .../flutter/lib/src/foundation/licenses.dart | 17 +- .../flutter/lib/src/foundation/print.dart | 12 +- .../flutter/lib/src/gestures/binding.dart | 14 +- .../flutter/lib/src/gestures/converter.dart | 373 +++++++++--------- .../lib/src/gestures/pointer_router.dart | 10 +- .../src/gestures/pointer_signal_resolver.dart | 6 +- .../flutter/lib/src/gestures/recognizer.dart | 8 +- packages/flutter/lib/src/material/app.dart | 12 +- packages/flutter/lib/src/material/chip.dart | 27 +- .../lib/src/material/input_decorator.dart | 48 +-- .../flutter/lib/src/material/list_tile.dart | 56 +-- .../lib/src/painting/_network_image_io.dart | 10 +- .../lib/src/painting/_network_image_web.dart | 10 +- .../lib/src/painting/decoration_image.dart | 11 +- .../lib/src/painting/image_provider.dart | 34 +- .../rendering/debug_overflow_indicator.dart | 16 +- packages/flutter/lib/src/rendering/layer.dart | 5 +- .../flutter/lib/src/rendering/object.dart | 31 +- .../flutter/lib/src/rendering/sliver.dart | 6 +- packages/flutter/lib/src/rendering/table.dart | 2 + .../flutter/lib/src/rendering/viewport.dart | 33 +- .../flutter/lib/src/scheduler/binding.dart | 63 +-- .../flutter/lib/src/services/binding.dart | 3 + .../lib/src/services/hardware_keyboard.dart | 12 +- .../lib/src/services/raw_keyboard.dart | 6 +- packages/flutter/lib/src/widgets/actions.dart | 8 +- packages/flutter/lib/src/widgets/app.dart | 50 +-- .../flutter/lib/src/widgets/drag_target.dart | 6 +- .../lib/src/widgets/focus_manager.dart | 8 +- .../flutter/lib/src/widgets/framework.dart | 77 ++-- .../lib/src/widgets/layout_builder.dart | 14 +- .../flutter/lib/src/widgets/navigator.dart | 8 +- .../lib/src/widgets/nested_scroll_view.dart | 4 +- packages/flutter/lib/src/widgets/router.dart | 8 +- packages/flutter/lib/src/widgets/routes.dart | 8 +- .../widgets/scroll_notification_observer.dart | 8 +- .../flutter/lib/src/widgets/shortcuts.dart | 4 +- .../lib/src/widgets/widget_inspector.dart | 23 +- .../flutter/test/material/list_tile_test.dart | 5 + .../lib/src/commands/update_packages.dart | 31 +- packages/flutter_tools/lib/src/device.dart | 13 +- packages/flutter_tools/lib/src/doctor.dart | 2 +- .../lib/src/runner/flutter_command.dart | 2 +- 55 files changed, 656 insertions(+), 580 deletions(-) diff --git a/dev/bots/analyze.dart b/dev/bots/analyze.dart index 794d5b10b5..5e77fe721c 100644 --- a/dev/bots/analyze.dart +++ b/dev/bots/analyze.dart @@ -24,6 +24,7 @@ import 'utils.dart'; final String flutterRoot = path.dirname(path.dirname(path.dirname(path.fromUri(Platform.script)))); final String flutter = path.join(flutterRoot, 'bin', Platform.isWindows ? 'flutter.bat' : 'flutter'); final String flutterPackages = path.join(flutterRoot, 'packages'); +final String flutterExamples = path.join(flutterRoot, 'examples'); final String dart = path.join(flutterRoot, 'bin', 'cache', 'dart-sdk', 'bin', Platform.isWindows ? 'dart.exe' : 'dart'); final String pub = path.join(flutterRoot, 'bin', 'cache', 'dart-sdk', 'bin', Platform.isWindows ? 'pub.bat' : 'pub'); final String pubCache = path.join(flutterRoot, '.pub-cache'); @@ -51,6 +52,10 @@ Future run(List arguments) async { exitWithError(['The analyze.dart script must be run with --enable-asserts.']); } + print('$clock No sync*/async*'); + await verifyNoSyncAsyncStar(flutterPackages); + await verifyNoSyncAsyncStar(flutterExamples, minimumMatches: 200); + print('$clock No runtimeType in toString...'); await verifyNoRuntimeTypeInToString(flutterRoot); @@ -153,6 +158,34 @@ Future run(List arguments) async { // TESTS +Future verifyNoSyncAsyncStar(String workingDirectory, {int minimumMatches = 2000 }) async { + final RegExp syncPattern = RegExp(r'\s*?a?sync\*\s*?{'); + const String ignorePattern = 'no_sync_async_star'; + final RegExp commentPattern = RegExp(r'^\s*?///?'); + final List errors = []; + await for (final File file in _allFiles(workingDirectory, 'dart', minimumMatches: minimumMatches)) { + if (file.path.contains('test')) { + continue; + } + final List lines = file.readAsLinesSync(); + for (int index = 0; index < lines.length; index += 1) { + final String line = lines[index]; + if (line.startsWith(commentPattern)) { + continue; + } + if (line.contains(syncPattern) && !line.contains(ignorePattern) && (index == 0 || !lines[index - 1].contains(ignorePattern))) { + errors.add('${file.path}:$index: sync*/async* without an ignore (no_sync_async_star).'); + } + } + } + if (errors.isNotEmpty) { + exitWithError([ + '${bold}Do not use sync*/async* methods. See https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#avoid-syncasync for details.$reset', + ...errors, + ]); + } +} + final RegExp _findGoldenTestPattern = RegExp(r'matchesGoldenFile\('); final RegExp _findGoldenDefinitionPattern = RegExp(r'matchesGoldenFile\(Object'); final RegExp _leadingComment = RegExp(r'\/\/'); diff --git a/dev/bots/test.dart b/dev/bots/test.dart index 8fe1c6cf32..e2268e59aa 100644 --- a/dev/bots/test.dart +++ b/dev/bots/test.dart @@ -430,6 +430,8 @@ Future runForbiddenFromReleaseTests() async { '--snapshot', path.join(tempDirectory.path, 'snapshot.arm64-v8a.json'), '--package-config', path.join(flutterRoot, 'examples', 'hello_world', '.dart_tool', 'package_config.json'), '--forbidden-type', 'package:flutter/src/widgets/widget_inspector.dart::WidgetInspectorService', + '--forbidden-type', 'package:flutter/src/widgets/framework.dart::DebugCreator', + '--forbidden-type', 'package:flutter/src/foundation/print.dart::debugPrint', ]; await runCommand( dart, diff --git a/examples/api/lib/material/chip/deletable_chip_attributes.on_deleted.0.dart b/examples/api/lib/material/chip/deletable_chip_attributes.on_deleted.0.dart index 67a6f6fb4c..757042694f 100644 --- a/examples/api/lib/material/chip/deletable_chip_attributes.on_deleted.0.dart +++ b/examples/api/lib/material/chip/deletable_chip_attributes.on_deleted.0.dart @@ -48,9 +48,9 @@ class CastListState extends State { const Actor('James Madison', 'JM'), ]; - Iterable get actorWidgets sync* { - for (final Actor actor in _cast) { - yield Padding( + Iterable get actorWidgets { + return _cast.map((Actor actor) { + return Padding( padding: const EdgeInsets.all(4.0), child: Chip( avatar: CircleAvatar(child: Text(actor.initials)), @@ -64,7 +64,7 @@ class CastListState extends State { }, ), ); - } + }); } @override diff --git a/examples/api/lib/widgets/async/stream_builder.0.dart b/examples/api/lib/widgets/async/stream_builder.0.dart index 2ec59351fa..3d4e087aef 100644 --- a/examples/api/lib/widgets/async/stream_builder.0.dart +++ b/examples/api/lib/widgets/async/stream_builder.0.dart @@ -30,6 +30,7 @@ class MyStatefulWidget extends StatefulWidget { } class _MyStatefulWidgetState extends State { + // ignore: no_sync_async_star final Stream _bids = (() async* { await Future.delayed(const Duration(seconds: 1)); yield 1; diff --git a/examples/api/lib/widgets/slotted_render_object_widget/slotted_multi_child_render_object_widget_mixin.0.dart b/examples/api/lib/widgets/slotted_render_object_widget/slotted_multi_child_render_object_widget_mixin.0.dart index 217544b3b6..7e89dc9282 100644 --- a/examples/api/lib/widgets/slotted_render_object_widget/slotted_multi_child_render_object_widget_mixin.0.dart +++ b/examples/api/lib/widgets/slotted_render_object_widget/slotted_multi_child_render_object_widget_mixin.0.dart @@ -94,13 +94,13 @@ class RenderDiagonal extends RenderBox with SlottedContainerRenderObjectMixin get children sync* { - if (_topLeft != null) { - yield _topLeft!; - } - if (_bottomRight != null) { - yield _bottomRight!; - } + Iterable get children { + return [ + if (_topLeft != null) + _topLeft!, + if (_bottomRight != null) + _bottomRight!, + ]; } // LAYOUT diff --git a/packages/flutter/lib/src/animation/listener_helpers.dart b/packages/flutter/lib/src/animation/listener_helpers.dart index 850d976f5d..ebf2943bd9 100644 --- a/packages/flutter/lib/src/animation/listener_helpers.dart +++ b/packages/flutter/lib/src/animation/listener_helpers.dart @@ -141,13 +141,13 @@ mixin AnimationLocalListenersMixin { for (final VoidCallback listener in localListeners) { InformationCollector? collector; assert(() { - collector = () sync* { - yield DiagnosticsProperty( + collector = () => [ + DiagnosticsProperty( 'The $runtimeType notifying listeners was', this, style: DiagnosticsTreeStyle.errorProperty, - ); - }; + ), + ]; return true; }()); try { @@ -234,13 +234,13 @@ mixin AnimationLocalStatusListenersMixin { } catch (exception, stack) { InformationCollector? collector; assert(() { - collector = () sync* { - yield DiagnosticsProperty( + collector = () => [ + DiagnosticsProperty( 'The $runtimeType notifying status listeners was', this, style: DiagnosticsTreeStyle.errorProperty, - ); - }; + ), + ]; return true; }()); FlutterError.reportError(FlutterErrorDetails( diff --git a/packages/flutter/lib/src/cupertino/app.dart b/packages/flutter/lib/src/cupertino/app.dart index b305e75c54..5c9210a4d9 100644 --- a/packages/flutter/lib/src/cupertino/app.dart +++ b/packages/flutter/lib/src/cupertino/app.dart @@ -487,10 +487,12 @@ class _CupertinoAppState extends State { // of a particular LocalizationsDelegate.type is loaded so the // localizationsDelegate parameter can be used to override // _CupertinoLocalizationsDelegate. - Iterable> get _localizationsDelegates sync* { - if (widget.localizationsDelegates != null) - yield* widget.localizationsDelegates!; - yield DefaultCupertinoLocalizations.delegate; + Iterable> get _localizationsDelegates { + return >[ + if (widget.localizationsDelegates != null) + ...widget.localizationsDelegates!, + DefaultCupertinoLocalizations.delegate, + ]; } Widget _inspectorSelectButtonBuilder(BuildContext context, VoidCallback onPressed) { diff --git a/packages/flutter/lib/src/cupertino/dialog.dart b/packages/flutter/lib/src/cupertino/dialog.dart index 32076a4c5f..22380fe495 100644 --- a/packages/flutter/lib/src/cupertino/dialog.dart +++ b/packages/flutter/lib/src/cupertino/dialog.dart @@ -2050,16 +2050,18 @@ class _RenderCupertinoDialogActions extends RenderBox markNeedsPaint(); } - Iterable get _pressedButtons sync* { + Iterable get _pressedButtons { + final List boxes = []; RenderBox? currentChild = firstChild; while (currentChild != null) { assert(currentChild.parentData is _ActionButtonParentData); final _ActionButtonParentData parentData = currentChild.parentData! as _ActionButtonParentData; if (parentData.isPressed) { - yield currentChild; + boxes.add(currentChild); } currentChild = childAfter(currentChild); } + return boxes; } bool get _isButtonPressed { diff --git a/packages/flutter/lib/src/foundation/assertions.dart b/packages/flutter/lib/src/foundation/assertions.dart index 84a503aa8f..a6c8c41782 100644 --- a/packages/flutter/lib/src/foundation/assertions.dart +++ b/packages/flutter/lib/src/foundation/assertions.dart @@ -542,10 +542,10 @@ class FlutterErrorDetails with Diagnosticable { /// FlutterError.reportError(FlutterErrorDetails( /// exception: error, /// stack: stack, - /// informationCollector: () sync* { - /// yield ErrorDescription('This happened while climbing the space elevator.'); - /// yield ErrorHint('The process ID is: $pid'); - /// }, + /// informationCollector: () => [ + /// ErrorDescription('This happened while climbing the space elevator.'), + /// ErrorHint('The process ID is: $pid'), + /// ], /// )); /// } /// } diff --git a/packages/flutter/lib/src/foundation/basic_types.dart b/packages/flutter/lib/src/foundation/basic_types.dart index 8ae2c80c71..09dfbf948b 100644 --- a/packages/flutter/lib/src/foundation/basic_types.dart +++ b/packages/flutter/lib/src/foundation/basic_types.dart @@ -113,7 +113,7 @@ class CachingIterable extends IterableBase { /// once. If you have an [Iterable], you can pass its [iterator] /// field as the argument to this constructor. /// - /// You can use a `sync*` function with this as follows: + /// You can this with an existing `sync*` function as follows: /// /// ```dart /// Iterable range(int start, int end) sync* { @@ -125,6 +125,10 @@ class CachingIterable extends IterableBase { /// print(i.length); // walks the list /// print(i.length); // efficient /// ``` + /// + /// Beware that this will eagerly evaluate the `range` iterable, and because + /// of that it would be better to just implement `range` as something that + /// returns a `List` to begin with if possible. CachingIterable(this._prefillIterator); final Iterator _prefillIterator; diff --git a/packages/flutter/lib/src/foundation/change_notifier.dart b/packages/flutter/lib/src/foundation/change_notifier.dart index 095054ba95..a910f03e71 100644 --- a/packages/flutter/lib/src/foundation/change_notifier.dart +++ b/packages/flutter/lib/src/foundation/change_notifier.dart @@ -312,13 +312,13 @@ class ChangeNotifier implements Listenable { stack: stack, library: 'foundation library', context: ErrorDescription('while dispatching notifications for $runtimeType'), - informationCollector: () sync* { - yield DiagnosticsProperty( + informationCollector: () => [ + DiagnosticsProperty( 'The $runtimeType sending notification was', this, style: DiagnosticsTreeStyle.errorProperty, - ); - }, + ), + ], )); } } diff --git a/packages/flutter/lib/src/foundation/diagnostics.dart b/packages/flutter/lib/src/foundation/diagnostics.dart index 73008ee3a8..c18f627d69 100644 --- a/packages/flutter/lib/src/foundation/diagnostics.dart +++ b/packages/flutter/lib/src/foundation/diagnostics.dart @@ -870,12 +870,12 @@ class _PrefixedStringBuilder { /// /// This method wraps a sequence of text where only some spans of text can be /// used as wrap boundaries. - static Iterable _wordWrapLine(String message, List wrapRanges, int width, { int startOffset = 0, int otherLineOffset = 0}) sync* { + static Iterable _wordWrapLine(String message, List wrapRanges, int width, { int startOffset = 0, int otherLineOffset = 0}) { if (message.length + startOffset < width) { // Nothing to do. The line doesn't wrap. - yield message; - return; + return [message]; } + final List wrappedLine = []; int startForLengthCalculations = -startOffset; bool addPrefix = false; int index = 0; @@ -920,10 +920,10 @@ class _PrefixedStringBuilder { lastWordEnd = index; } final String line = message.substring(start, lastWordEnd); - yield line; + wrappedLine.add(line); addPrefix = true; if (lastWordEnd >= message.length) - return; + return wrappedLine; // just yielded a line if (lastWordEnd == index) { // we broke at current position @@ -2590,12 +2590,10 @@ class FlagsSummary extends DiagnosticsProperty> { // // For a null value, it is omitted unless `includeEmpty` is true and // [ifEntryNull] contains a corresponding description. - Iterable _formattedValues() sync* { - for (final MapEntry entry in value.entries) { - if (entry.value != null) { - yield entry.key; - } - } + Iterable _formattedValues() { + return value.entries + .where((MapEntry entry) => entry.value != null) + .map((MapEntry entry) => entry.key); } } diff --git a/packages/flutter/lib/src/foundation/licenses.dart b/packages/flutter/lib/src/foundation/licenses.dart index 7983ba3add..6b794f754d 100644 --- a/packages/flutter/lib/src/foundation/licenses.dart +++ b/packages/flutter/lib/src/foundation/licenses.dart @@ -139,7 +139,7 @@ class LicenseEntryWithLineBreaks extends LicenseEntry { final String text; @override - Iterable get paragraphs sync* { + Iterable get paragraphs { int lineStart = 0; int currentPosition = 0; int lastLineIndent = 0; @@ -147,6 +147,7 @@ class LicenseEntryWithLineBreaks extends LicenseEntry { int? currentParagraphIndentation; _LicenseEntryWithLineBreaksParserState state = _LicenseEntryWithLineBreaksParserState.beforeParagraph; final List lines = []; + final List result = []; void addLine() { assert(lineStart < currentPosition); @@ -182,7 +183,7 @@ class LicenseEntryWithLineBreaks extends LicenseEntry { case '\n': case '\f': if (lines.isNotEmpty) { - yield getParagraph(); + result.add(getParagraph()); } if (text[currentPosition] == '\r' && currentPosition < text.length - 1 && text[currentPosition + 1] == '\n') { @@ -206,7 +207,7 @@ class LicenseEntryWithLineBreaks extends LicenseEntry { startParagraph: default: if (lines.isNotEmpty && currentLineIndent > lastLineIndent) { - yield getParagraph(); + result.add(getParagraph()); currentParagraphIndentation = null; } // The following is a wild heuristic for guessing the indentation level. @@ -231,7 +232,7 @@ class LicenseEntryWithLineBreaks extends LicenseEntry { break; case '\f': addLine(); - yield getParagraph(); + result.add(getParagraph()); lastLineIndent = 0; currentLineIndent = 0; currentParagraphIndentation = null; @@ -248,14 +249,15 @@ class LicenseEntryWithLineBreaks extends LicenseEntry { switch (state) { case _LicenseEntryWithLineBreaksParserState.beforeParagraph: if (lines.isNotEmpty) { - yield getParagraph(); + result.add(getParagraph()); } break; case _LicenseEntryWithLineBreaksParserState.inParagraph: addLine(); - yield getParagraph(); + result.add(getParagraph()); break; } + return result; } } @@ -307,6 +309,9 @@ class LicenseRegistry { /// Returns the licenses that have been registered. /// /// Generating the list of licenses is expensive. + // TODO(dnfield): Refactor the license logic. + // https://github.com/flutter/flutter/issues/95043 + // flutter_ignore: no_sync_async_star static Stream get licenses async* { if (_collectors == null) return; diff --git a/packages/flutter/lib/src/foundation/print.dart b/packages/flutter/lib/src/foundation/print.dart index b12f4902cc..f9d3f89cb4 100644 --- a/packages/flutter/lib/src/foundation/print.dart +++ b/packages/flutter/lib/src/foundation/print.dart @@ -113,11 +113,11 @@ enum _WordWrapParseMode { inSpace, inWord, atBreak } /// and so forth. It is only intended for formatting error messages. /// /// The default [debugPrint] implementation uses this for its line wrapping. -Iterable debugWordWrap(String message, int width, { String wrapIndent = '' }) sync* { +Iterable debugWordWrap(String message, int width, { String wrapIndent = '' }) { if (message.length < width || message.trimLeft()[0] == '#') { - yield message; - return; + return [message]; } + final List wrapped = []; final Match prefixMatch = _indentPattern.matchAsPrefix(message)!; final String prefix = wrapIndent + ' ' * prefixMatch.group(0)!.length; int start = 0; @@ -149,13 +149,13 @@ Iterable debugWordWrap(String message, int width, { String wrapIndent = lastWordEnd = index; } if (addPrefix) { - yield prefix + message.substring(start, lastWordEnd); + wrapped.add(prefix + message.substring(start, lastWordEnd)); } else { - yield message.substring(start, lastWordEnd); + wrapped.add(message.substring(start, lastWordEnd)); addPrefix = true; } if (lastWordEnd >= message.length) - return; + return wrapped; // just yielded a line if (lastWordEnd == index) { // we broke at current position diff --git a/packages/flutter/lib/src/gestures/binding.dart b/packages/flutter/lib/src/gestures/binding.dart index 241eb10538..38fc388fdb 100644 --- a/packages/flutter/lib/src/gestures/binding.dart +++ b/packages/flutter/lib/src/gestures/binding.dart @@ -407,9 +407,9 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H library: 'gesture library', context: ErrorDescription('while dispatching a non-hit-tested pointer event'), event: event, - informationCollector: () sync* { - yield DiagnosticsProperty('Event', event, style: DiagnosticsTreeStyle.errorProperty); - }, + informationCollector: () => [ + DiagnosticsProperty('Event', event, style: DiagnosticsTreeStyle.errorProperty), + ], )); } return; @@ -425,10 +425,10 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H context: ErrorDescription('while dispatching a pointer event'), event: event, hitTestEntry: entry, - informationCollector: () sync* { - yield DiagnosticsProperty('Event', event, style: DiagnosticsTreeStyle.errorProperty); - yield DiagnosticsProperty('Target', entry.target, style: DiagnosticsTreeStyle.errorProperty); - }, + informationCollector: () => [ + DiagnosticsProperty('Event', event, style: DiagnosticsTreeStyle.errorProperty), + DiagnosticsProperty('Target', entry.target, style: DiagnosticsTreeStyle.errorProperty), + ], )); } } diff --git a/packages/flutter/lib/src/gestures/converter.dart b/packages/flutter/lib/src/gestures/converter.dart index 5eb673d714..561c75320e 100644 --- a/packages/flutter/lib/src/gestures/converter.dart +++ b/packages/flutter/lib/src/gestures/converter.dart @@ -44,197 +44,188 @@ class PointerEventConverter { /// [dart:ui.FlutterView.devicePixelRatio]) is used to convert the incoming data /// from physical coordinates to logical pixels. See the discussion at /// [PointerEvent] for more details on the [PointerEvent] coordinate space. - static Iterable expand(Iterable data, double devicePixelRatio) sync* { - for (final ui.PointerData datum in data) { - final Offset position = Offset(datum.physicalX, datum.physicalY) / devicePixelRatio; - assert(position != null); - final Offset delta = Offset(datum.physicalDeltaX, datum.physicalDeltaY) / devicePixelRatio; - final double radiusMinor = _toLogicalPixels(datum.radiusMinor, devicePixelRatio); - final double radiusMajor = _toLogicalPixels(datum.radiusMajor, devicePixelRatio); - final double radiusMin = _toLogicalPixels(datum.radiusMin, devicePixelRatio); - final double radiusMax = _toLogicalPixels(datum.radiusMax, devicePixelRatio); - final Duration timeStamp = datum.timeStamp; - final PointerDeviceKind kind = datum.kind; - assert(datum.change != null); - if (datum.signalKind == null || datum.signalKind == ui.PointerSignalKind.none) { - switch (datum.change) { - case ui.PointerChange.add: - yield PointerAddedEvent( - timeStamp: timeStamp, - kind: kind, - device: datum.device, - position: position, - obscured: datum.obscured, - pressureMin: datum.pressureMin, - pressureMax: datum.pressureMax, - distance: datum.distance, - distanceMax: datum.distanceMax, - radiusMin: radiusMin, - radiusMax: radiusMax, - orientation: datum.orientation, - tilt: datum.tilt, - embedderId: datum.embedderId, - ); - break; - case ui.PointerChange.hover: - yield PointerHoverEvent( - timeStamp: timeStamp, - kind: kind, - device: datum.device, - position: position, - delta: delta, - buttons: datum.buttons, - obscured: datum.obscured, - pressureMin: datum.pressureMin, - pressureMax: datum.pressureMax, - distance: datum.distance, - distanceMax: datum.distanceMax, - size: datum.size, - radiusMajor: radiusMajor, - radiusMinor: radiusMinor, - radiusMin: radiusMin, - radiusMax: radiusMax, - orientation: datum.orientation, - tilt: datum.tilt, - synthesized: datum.synthesized, - embedderId: datum.embedderId, - ); - break; - case ui.PointerChange.down: - yield PointerDownEvent( - timeStamp: timeStamp, - pointer: datum.pointerIdentifier, - kind: kind, - device: datum.device, - position: position, - buttons: _synthesiseDownButtons(datum.buttons, kind), - obscured: datum.obscured, - pressure: datum.pressure, - pressureMin: datum.pressureMin, - pressureMax: datum.pressureMax, - distanceMax: datum.distanceMax, - size: datum.size, - radiusMajor: radiusMajor, - radiusMinor: radiusMinor, - radiusMin: radiusMin, - radiusMax: radiusMax, - orientation: datum.orientation, - tilt: datum.tilt, - embedderId: datum.embedderId, - ); - break; - case ui.PointerChange.move: - yield PointerMoveEvent( - timeStamp: timeStamp, - pointer: datum.pointerIdentifier, - kind: kind, - device: datum.device, - position: position, - delta: delta, - buttons: _synthesiseDownButtons(datum.buttons, kind), - obscured: datum.obscured, - pressure: datum.pressure, - pressureMin: datum.pressureMin, - pressureMax: datum.pressureMax, - distanceMax: datum.distanceMax, - size: datum.size, - radiusMajor: radiusMajor, - radiusMinor: radiusMinor, - radiusMin: radiusMin, - radiusMax: radiusMax, - orientation: datum.orientation, - tilt: datum.tilt, - platformData: datum.platformData, - synthesized: datum.synthesized, - embedderId: datum.embedderId, - ); - break; - case ui.PointerChange.up: - yield PointerUpEvent( - timeStamp: timeStamp, - pointer: datum.pointerIdentifier, - kind: kind, - device: datum.device, - position: position, - buttons: datum.buttons, - obscured: datum.obscured, - pressure: datum.pressure, - pressureMin: datum.pressureMin, - pressureMax: datum.pressureMax, - distance: datum.distance, - distanceMax: datum.distanceMax, - size: datum.size, - radiusMajor: radiusMajor, - radiusMinor: radiusMinor, - radiusMin: radiusMin, - radiusMax: radiusMax, - orientation: datum.orientation, - tilt: datum.tilt, - embedderId: datum.embedderId, - ); - break; - case ui.PointerChange.cancel: - yield PointerCancelEvent( - timeStamp: timeStamp, - pointer: datum.pointerIdentifier, - kind: kind, - device: datum.device, - position: position, - buttons: datum.buttons, - obscured: datum.obscured, - pressureMin: datum.pressureMin, - pressureMax: datum.pressureMax, - distance: datum.distance, - distanceMax: datum.distanceMax, - size: datum.size, - radiusMajor: radiusMajor, - radiusMinor: radiusMinor, - radiusMin: radiusMin, - radiusMax: radiusMax, - orientation: datum.orientation, - tilt: datum.tilt, - embedderId: datum.embedderId, - ); - break; - case ui.PointerChange.remove: - yield PointerRemovedEvent( - timeStamp: timeStamp, - kind: kind, - device: datum.device, - position: position, - obscured: datum.obscured, - pressureMin: datum.pressureMin, - pressureMax: datum.pressureMax, - distanceMax: datum.distanceMax, - radiusMin: radiusMin, - radiusMax: radiusMax, - embedderId: datum.embedderId, - ); - break; - } - } else { - switch (datum.signalKind!) { - case ui.PointerSignalKind.scroll: - final Offset scrollDelta = - Offset(datum.scrollDeltaX, datum.scrollDeltaY) / devicePixelRatio; - yield PointerScrollEvent( - timeStamp: timeStamp, - kind: kind, - device: datum.device, - position: position, - scrollDelta: scrollDelta, - embedderId: datum.embedderId, - ); - break; - case ui.PointerSignalKind.none: - assert(false); // This branch should already have 'none' filtered out. - break; - case ui.PointerSignalKind.unknown: - // Ignore unknown signals. - break; - } - } - } + static Iterable expand(Iterable data, double devicePixelRatio) { + return data + .where((ui.PointerData datum) => datum.signalKind != ui.PointerSignalKind.unknown) + .map((ui.PointerData datum) { + final Offset position = Offset(datum.physicalX, datum.physicalY) / devicePixelRatio; + assert(position != null); + final Offset delta = Offset(datum.physicalDeltaX, datum.physicalDeltaY) / devicePixelRatio; + final double radiusMinor = _toLogicalPixels(datum.radiusMinor, devicePixelRatio); + final double radiusMajor = _toLogicalPixels(datum.radiusMajor, devicePixelRatio); + final double radiusMin = _toLogicalPixels(datum.radiusMin, devicePixelRatio); + final double radiusMax = _toLogicalPixels(datum.radiusMax, devicePixelRatio); + final Duration timeStamp = datum.timeStamp; + final PointerDeviceKind kind = datum.kind; + assert(datum.change != null); + switch (datum.signalKind ?? ui.PointerSignalKind.none) { + case ui.PointerSignalKind.none: + switch (datum.change) { + case ui.PointerChange.add: + return PointerAddedEvent( + timeStamp: timeStamp, + kind: kind, + device: datum.device, + position: position, + obscured: datum.obscured, + pressureMin: datum.pressureMin, + pressureMax: datum.pressureMax, + distance: datum.distance, + distanceMax: datum.distanceMax, + radiusMin: radiusMin, + radiusMax: radiusMax, + orientation: datum.orientation, + tilt: datum.tilt, + embedderId: datum.embedderId, + ); + case ui.PointerChange.hover: + return PointerHoverEvent( + timeStamp: timeStamp, + kind: kind, + device: datum.device, + position: position, + delta: delta, + buttons: datum.buttons, + obscured: datum.obscured, + pressureMin: datum.pressureMin, + pressureMax: datum.pressureMax, + distance: datum.distance, + distanceMax: datum.distanceMax, + size: datum.size, + radiusMajor: radiusMajor, + radiusMinor: radiusMinor, + radiusMin: radiusMin, + radiusMax: radiusMax, + orientation: datum.orientation, + tilt: datum.tilt, + synthesized: datum.synthesized, + embedderId: datum.embedderId, + ); + case ui.PointerChange.down: + return PointerDownEvent( + timeStamp: timeStamp, + pointer: datum.pointerIdentifier, + kind: kind, + device: datum.device, + position: position, + buttons: _synthesiseDownButtons(datum.buttons, kind), + obscured: datum.obscured, + pressure: datum.pressure, + pressureMin: datum.pressureMin, + pressureMax: datum.pressureMax, + distanceMax: datum.distanceMax, + size: datum.size, + radiusMajor: radiusMajor, + radiusMinor: radiusMinor, + radiusMin: radiusMin, + radiusMax: radiusMax, + orientation: datum.orientation, + tilt: datum.tilt, + embedderId: datum.embedderId, + ); + case ui.PointerChange.move: + return PointerMoveEvent( + timeStamp: timeStamp, + pointer: datum.pointerIdentifier, + kind: kind, + device: datum.device, + position: position, + delta: delta, + buttons: _synthesiseDownButtons(datum.buttons, kind), + obscured: datum.obscured, + pressure: datum.pressure, + pressureMin: datum.pressureMin, + pressureMax: datum.pressureMax, + distanceMax: datum.distanceMax, + size: datum.size, + radiusMajor: radiusMajor, + radiusMinor: radiusMinor, + radiusMin: radiusMin, + radiusMax: radiusMax, + orientation: datum.orientation, + tilt: datum.tilt, + platformData: datum.platformData, + synthesized: datum.synthesized, + embedderId: datum.embedderId, + ); + case ui.PointerChange.up: + return PointerUpEvent( + timeStamp: timeStamp, + pointer: datum.pointerIdentifier, + kind: kind, + device: datum.device, + position: position, + buttons: datum.buttons, + obscured: datum.obscured, + pressure: datum.pressure, + pressureMin: datum.pressureMin, + pressureMax: datum.pressureMax, + distance: datum.distance, + distanceMax: datum.distanceMax, + size: datum.size, + radiusMajor: radiusMajor, + radiusMinor: radiusMinor, + radiusMin: radiusMin, + radiusMax: radiusMax, + orientation: datum.orientation, + tilt: datum.tilt, + embedderId: datum.embedderId, + ); + case ui.PointerChange.cancel: + return PointerCancelEvent( + timeStamp: timeStamp, + pointer: datum.pointerIdentifier, + kind: kind, + device: datum.device, + position: position, + buttons: datum.buttons, + obscured: datum.obscured, + pressureMin: datum.pressureMin, + pressureMax: datum.pressureMax, + distance: datum.distance, + distanceMax: datum.distanceMax, + size: datum.size, + radiusMajor: radiusMajor, + radiusMinor: radiusMinor, + radiusMin: radiusMin, + radiusMax: radiusMax, + orientation: datum.orientation, + tilt: datum.tilt, + embedderId: datum.embedderId, + ); + case ui.PointerChange.remove: + return PointerRemovedEvent( + timeStamp: timeStamp, + kind: kind, + device: datum.device, + position: position, + obscured: datum.obscured, + pressureMin: datum.pressureMin, + pressureMax: datum.pressureMax, + distanceMax: datum.distanceMax, + radiusMin: radiusMin, + radiusMax: radiusMax, + embedderId: datum.embedderId, + ); + } + case ui.PointerSignalKind.scroll: + final Offset scrollDelta = + Offset(datum.scrollDeltaX, datum.scrollDeltaY) / devicePixelRatio; + return PointerScrollEvent( + timeStamp: timeStamp, + kind: kind, + device: datum.device, + position: position, + scrollDelta: scrollDelta, + embedderId: datum.embedderId, + ); + case ui.PointerSignalKind.unknown: + // This branch should already have 'unknown' filtered out, but + // we don't want to return anything or miss if someone adds a new + // enumeration to PointerSignalKind. + throw StateError('Unreachable'); + } + }); } static double _toLogicalPixels(double physicalPixels, double devicePixelRatio) => physicalPixels / devicePixelRatio; diff --git a/packages/flutter/lib/src/gestures/pointer_router.dart b/packages/flutter/lib/src/gestures/pointer_router.dart index 6912ad1e02..beb36df18a 100644 --- a/packages/flutter/lib/src/gestures/pointer_router.dart +++ b/packages/flutter/lib/src/gestures/pointer_router.dart @@ -95,11 +95,11 @@ class PointerRouter { } catch (exception, stack) { InformationCollector? collector; assert(() { - collector = () sync* { - yield DiagnosticsProperty('router', this, level: DiagnosticLevel.debug); - yield DiagnosticsProperty('route', route, level: DiagnosticLevel.debug); - yield DiagnosticsProperty('event', event, level: DiagnosticLevel.debug); - }; + collector = () => [ + DiagnosticsProperty('router', this, level: DiagnosticLevel.debug), + DiagnosticsProperty('route', route, level: DiagnosticLevel.debug), + DiagnosticsProperty('event', event, level: DiagnosticLevel.debug), + ]; return true; }()); FlutterError.reportError(FlutterErrorDetails( diff --git a/packages/flutter/lib/src/gestures/pointer_signal_resolver.dart b/packages/flutter/lib/src/gestures/pointer_signal_resolver.dart index 8d162c36e0..a79af56551 100644 --- a/packages/flutter/lib/src/gestures/pointer_signal_resolver.dart +++ b/packages/flutter/lib/src/gestures/pointer_signal_resolver.dart @@ -89,9 +89,9 @@ class PointerSignalResolver { } catch (exception, stack) { InformationCollector? collector; assert(() { - collector = () sync* { - yield DiagnosticsProperty('Event', event, style: DiagnosticsTreeStyle.errorProperty); - }; + collector = () => [ + DiagnosticsProperty('Event', event, style: DiagnosticsTreeStyle.errorProperty), + ]; return true; }()); FlutterError.reportError(FlutterErrorDetails( diff --git a/packages/flutter/lib/src/gestures/recognizer.dart b/packages/flutter/lib/src/gestures/recognizer.dart index 70c1392918..3c061867de 100644 --- a/packages/flutter/lib/src/gestures/recognizer.dart +++ b/packages/flutter/lib/src/gestures/recognizer.dart @@ -199,10 +199,10 @@ abstract class GestureRecognizer extends GestureArenaMember with DiagnosticableT } catch (exception, stack) { InformationCollector? collector; assert(() { - collector = () sync* { - yield StringProperty('Handler', name); - yield DiagnosticsProperty('Recognizer', this, style: DiagnosticsTreeStyle.errorProperty); - }; + collector = () => [ + StringProperty('Handler', name), + DiagnosticsProperty('Recognizer', this, style: DiagnosticsTreeStyle.errorProperty), + ]; return true; }()); FlutterError.reportError(FlutterErrorDetails( diff --git a/packages/flutter/lib/src/material/app.dart b/packages/flutter/lib/src/material/app.dart index e26a49a0cd..acc572928c 100644 --- a/packages/flutter/lib/src/material/app.dart +++ b/packages/flutter/lib/src/material/app.dart @@ -797,11 +797,13 @@ class _MaterialAppState extends State { // of a particular LocalizationsDelegate.type is loaded so the // localizationsDelegate parameter can be used to override // _MaterialLocalizationsDelegate. - Iterable> get _localizationsDelegates sync* { - if (widget.localizationsDelegates != null) - yield* widget.localizationsDelegates!; - yield DefaultMaterialLocalizations.delegate; - yield DefaultCupertinoLocalizations.delegate; + Iterable> get _localizationsDelegates { + return >[ + if (widget.localizationsDelegates != null) + ...widget.localizationsDelegates!, + DefaultMaterialLocalizations.delegate, + DefaultCupertinoLocalizations.delegate, + ]; } Widget _inspectorSelectButtonBuilder(BuildContext context, VoidCallback onPressed) { diff --git a/packages/flutter/lib/src/material/chip.dart b/packages/flutter/lib/src/material/chip.dart index f6db9dadcc..0a85fa313f 100644 --- a/packages/flutter/lib/src/material/chip.dart +++ b/packages/flutter/lib/src/material/chip.dart @@ -1076,9 +1076,9 @@ class ChoiceChip extends StatelessWidget /// ]; /// final List _filters = []; /// -/// Iterable get actorWidgets sync* { -/// for (final ActorFilterEntry actor in _cast) { -/// yield Padding( +/// Iterable get actorWidgets { +/// return _cast.map((ActorFilterEntry actor) { +/// return Padding( /// padding: const EdgeInsets.all(4.0), /// child: FilterChip( /// avatar: CircleAvatar(child: Text(actor.initials)), @@ -1097,7 +1097,7 @@ class ChoiceChip extends StatelessWidget /// }, /// ), /// ); -/// } +/// }); /// } /// /// @override @@ -2257,16 +2257,15 @@ class _RenderChip extends RenderBox with SlottedContainerRenderObjectMixin<_Chip // The returned list is ordered for hit testing. @override - Iterable get children sync* { - if (avatar != null) { - yield avatar!; - } - if (label != null) { - yield label!; - } - if (deleteIcon != null) { - yield deleteIcon!; - } + Iterable get children { + return [ + if (avatar != null) + avatar!, + if (label != null) + label!, + if (deleteIcon != null) + deleteIcon!, + ]; } bool get isDrawingCheckmark => theme.showCheckmark && !checkmarkAnimation.isDismissed; diff --git a/packages/flutter/lib/src/material/input_decorator.dart b/packages/flutter/lib/src/material/input_decorator.dart index aea7da52bb..01c64843d6 100644 --- a/packages/flutter/lib/src/material/input_decorator.dart +++ b/packages/flutter/lib/src/material/input_decorator.dart @@ -723,29 +723,31 @@ class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin // The returned list is ordered for hit testing. @override - Iterable get children sync* { - if (icon != null) - yield icon!; - if (input != null) - yield input!; - if (prefixIcon != null) - yield prefixIcon!; - if (suffixIcon != null) - yield suffixIcon!; - if (prefix != null) - yield prefix!; - if (suffix != null) - yield suffix!; - if (label != null) - yield label!; - if (hint != null) - yield hint!; - if (helperError != null) - yield helperError!; - if (counter != null) - yield counter!; - if (container != null) - yield container!; + Iterable get children { + return [ + if (icon != null) + icon!, + if (input != null) + input!, + if (prefixIcon != null) + prefixIcon!, + if (suffixIcon != null) + suffixIcon!, + if (prefix != null) + prefix!, + if (suffix != null) + suffix!, + if (label != null) + label!, + if (hint != null) + hint!, + if (helperError != null) + helperError!, + if (counter != null) + counter!, + if (container != null) + container!, + ]; } _Decoration get decoration => _decoration; diff --git a/packages/flutter/lib/src/material/list_tile.dart b/packages/flutter/lib/src/material/list_tile.dart index 87a3c11301..71032f987b 100644 --- a/packages/flutter/lib/src/material/list_tile.dart +++ b/packages/flutter/lib/src/material/list_tile.dart @@ -1041,33 +1041,31 @@ class ListTile extends StatelessWidget { /// See also: /// /// * [Divider], which you can use to obtain this effect manually. - static Iterable divideTiles({ BuildContext? context, required Iterable tiles, Color? color }) sync* { + static Iterable divideTiles({ BuildContext? context, required Iterable tiles, Color? color }) { assert(tiles != null); assert(color != null || context != null); + tiles = tiles.toList(); - final Iterator iterator = tiles.iterator; - final bool hasNext = iterator.moveNext(); + if (tiles.isEmpty || tiles.length == 1) { + return tiles; + } - if (!hasNext) - return; - - final Decoration decoration = BoxDecoration( - border: Border( - bottom: Divider.createBorderSide(context, color: color), - ), - ); - - Widget tile = iterator.current; - while (iterator.moveNext()) { - yield DecoratedBox( + Widget wrapTile(Widget tile) { + return DecoratedBox( position: DecorationPosition.foreground, - decoration: decoration, + decoration: BoxDecoration( + border: Border( + bottom: Divider.createBorderSide(context, color: color), + ), + ), child: tile, ); - tile = iterator.current; } - if (hasNext) - yield tile; + + return [ + ...tiles.take(tiles.length - 1).map(wrapTile), + tiles.last, + ]; } Color? _iconColor(ThemeData theme, ListTileThemeData tileTheme) { @@ -1389,15 +1387,17 @@ class _RenderListTile extends RenderBox with SlottedContainerRenderObjectMixin<_ // The returned list is ordered for hit testing. @override - Iterable get children sync* { - if (leading != null) - yield leading!; - if (title != null) - yield title!; - if (subtitle != null) - yield subtitle!; - if (trailing != null) - yield trailing!; + Iterable get children { + return [ + if (leading != null) + leading!, + if (title != null) + title!, + if (subtitle != null) + subtitle!, + if (trailing != null) + trailing!, + ]; } bool get isDense => _isDense; diff --git a/packages/flutter/lib/src/painting/_network_image_io.dart b/packages/flutter/lib/src/painting/_network_image_io.dart index 21d0c95c90..9542c017b5 100644 --- a/packages/flutter/lib/src/painting/_network_image_io.dart +++ b/packages/flutter/lib/src/painting/_network_image_io.dart @@ -51,12 +51,10 @@ class NetworkImage extends image_provider.ImageProvider[ - DiagnosticsProperty('Image provider', this), - DiagnosticsProperty('Image key', key), - ]; - }, + informationCollector: () => [ + DiagnosticsProperty('Image provider', this), + DiagnosticsProperty('Image key', key), + ], ); } diff --git a/packages/flutter/lib/src/painting/_network_image_web.dart b/packages/flutter/lib/src/painting/_network_image_web.dart index 61543599cb..a1133291e7 100644 --- a/packages/flutter/lib/src/painting/_network_image_web.dart +++ b/packages/flutter/lib/src/painting/_network_image_web.dart @@ -59,12 +59,10 @@ class NetworkImage InformationCollector? _imageStreamInformationCollector(image_provider.NetworkImage key) { InformationCollector? collector; assert(() { - collector = () { - return [ - DiagnosticsProperty('Image provider', this), - DiagnosticsProperty('Image key', key as NetworkImage), - ]; - }; + collector = () => [ + DiagnosticsProperty('Image provider', this), + DiagnosticsProperty('Image key', key as NetworkImage), + ]; return true; }()); return collector; diff --git a/packages/flutter/lib/src/painting/decoration_image.dart b/packages/flutter/lib/src/painting/decoration_image.dart index 95e2558038..7e3cbdeeec 100644 --- a/packages/flutter/lib/src/painting/decoration_image.dart +++ b/packages/flutter/lib/src/painting/decoration_image.dart @@ -657,7 +657,7 @@ void paintImage({ } } -Iterable _generateImageTileRects(Rect outputRect, Rect fundamentalRect, ImageRepeat repeat) sync* { +Iterable _generateImageTileRects(Rect outputRect, Rect fundamentalRect, ImageRepeat repeat) { int startX = 0; int startY = 0; int stopX = 0; @@ -675,10 +675,11 @@ Iterable _generateImageTileRects(Rect outputRect, Rect fundamentalRect, Im stopY = ((outputRect.bottom - fundamentalRect.bottom) / strideY).ceil(); } - for (int i = startX; i <= stopX; ++i) { - for (int j = startY; j <= stopY; ++j) - yield fundamentalRect.shift(Offset(i * strideX, j * strideY)); - } + return [ + for (int i = startX; i <= stopX; ++i) + for (int j = startY; j <= stopY; ++j) + fundamentalRect.shift(Offset(i * strideX, j * strideY)), + ]; } Rect _scaleRect(Rect rect, double scale) => Rect.fromLTRB(rect.left * scale, rect.top * scale, rect.right * scale, rect.bottom * scale); diff --git a/packages/flutter/lib/src/painting/image_provider.dart b/packages/flutter/lib/src/painting/image_provider.dart index f52d533b5d..d5ca57e9f4 100644 --- a/packages/flutter/lib/src/painting/image_provider.dart +++ b/packages/flutter/lib/src/painting/image_provider.dart @@ -336,11 +336,11 @@ abstract class ImageProvider { await null; // wait an event turn in case a listener has been added to the image stream. InformationCollector? collector; assert(() { - collector = () sync* { - yield DiagnosticsProperty('Image provider', this); - yield DiagnosticsProperty('Image configuration', configuration); - yield DiagnosticsProperty('Image key', key, defaultValue: null); - }; + collector = () => [ + DiagnosticsProperty('Image provider', this), + DiagnosticsProperty('Image configuration', configuration), + DiagnosticsProperty('Image key', key, defaultValue: null), + ]; return true; }()); if (stream.completer == null) { @@ -395,11 +395,11 @@ abstract class ImageProvider { } else { InformationCollector? collector; assert(() { - collector = () sync* { - yield DiagnosticsProperty('Image provider', this); - yield DiagnosticsProperty('Image configuration', configuration); - yield DiagnosticsProperty('Image key', key, defaultValue: null); - }; + collector = () => [ + DiagnosticsProperty('Image provider', this), + DiagnosticsProperty('Image configuration', configuration), + DiagnosticsProperty('Image key', key, defaultValue: null), + ]; return true; }()); FlutterError.reportError(FlutterErrorDetails( @@ -648,10 +648,10 @@ abstract class AssetBundleImageProvider extends ImageProvider('Image provider', this); - yield DiagnosticsProperty('Image key', key); - }; + collector = () => [ + DiagnosticsProperty('Image provider', this), + DiagnosticsProperty('Image key', key), + ]; return true; }()); return MultiFrameImageStreamCompleter( @@ -878,9 +878,9 @@ class FileImage extends ImageProvider { codec: _loadAsync(key, decode), scale: key.scale, debugLabel: key.file.path, - informationCollector: () sync* { - yield ErrorDescription('Path: ${file.path}'); - }, + informationCollector: () => [ + ErrorDescription('Path: ${file.path}'), + ], ); } diff --git a/packages/flutter/lib/src/rendering/debug_overflow_indicator.dart b/packages/flutter/lib/src/rendering/debug_overflow_indicator.dart index e6fbde2012..8f5a4edf50 100644 --- a/packages/flutter/lib/src/rendering/debug_overflow_indicator.dart +++ b/packages/flutter/lib/src/rendering/debug_overflow_indicator.dart @@ -241,16 +241,18 @@ mixin DebugOverflowIndicatorMixin on RenderObject { exception: FlutterError('A $runtimeType overflowed by $overflowText.'), library: 'rendering library', context: ErrorDescription('during layout'), - informationCollector: () sync* { - if (debugCreator != null) - yield DiagnosticsDebugCreator(debugCreator!); - yield* overflowHints!; - yield describeForError('The specific $runtimeType in question is'); + informationCollector: () => [ + // debugCreator should only be set in DebugMode, but we want the + // treeshaker to know that. + if (kDebugMode && debugCreator != null) + DiagnosticsDebugCreator(debugCreator!), + ...overflowHints!, + describeForError('The specific $runtimeType in question is'), // TODO(jacobr): this line is ascii art that it would be nice to // handle a little more generically in GUI debugging clients in the // future. - yield DiagnosticsNode.message('◢◤' * (FlutterError.wrapWidth ~/ 2), allowWrap: false); - }, + DiagnosticsNode.message('◢◤' * (FlutterError.wrapWidth ~/ 2), allowWrap: false), + ], ), ); } diff --git a/packages/flutter/lib/src/rendering/layer.dart b/packages/flutter/lib/src/rendering/layer.dart index 711303dc8e..d56f4aaa93 100644 --- a/packages/flutter/lib/src/rendering/layer.dart +++ b/packages/flutter/lib/src/rendering/layer.dart @@ -67,9 +67,8 @@ class AnnotationResult { /// tree. /// /// It is similar to [entries] but does not contain other information. - Iterable get annotations sync* { - for (final AnnotationEntry entry in _entries) - yield entry.annotation; + Iterable get annotations { + return _entries.map((AnnotationEntry entry) => entry.annotation); } } diff --git a/packages/flutter/lib/src/rendering/object.dart b/packages/flutter/lib/src/rendering/object.dart index 8f85457c70..851ad52fe7 100644 --- a/packages/flutter/lib/src/rendering/object.dart +++ b/packages/flutter/lib/src/rendering/object.dart @@ -1391,16 +1391,18 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im stack: stack, library: 'rendering library', context: ErrorDescription('during $method()'), - informationCollector: () sync* { - if (debugCreator != null) - yield DiagnosticsDebugCreator(debugCreator!); - yield describeForError('The following RenderObject was being processed when the exception was fired'); + informationCollector: () => [ + // debugCreator should always be null outside of debugMode, but we want + // the tree shaker to notice this. + if (kDebugMode && debugCreator != null) + DiagnosticsDebugCreator(debugCreator!), + describeForError('The following RenderObject was being processed when the exception was fired'), // TODO(jacobr): this error message has a code smell. Consider whether // displaying the truncated children is really useful for command line // users. Inspector users can see the full tree by clicking on the // render object so this may not be that useful. - yield describeForError('RenderObject', style: DiagnosticsTreeStyle.truncateChildren); - }, + describeForError('RenderObject', style: DiagnosticsTreeStyle.truncateChildren), + ], )); } @@ -1781,7 +1783,7 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im assert(constraints != null); assert(constraints.debugAssertIsValid( isAppliedConstraint: true, - informationCollector: () sync* { + informationCollector: () { final List stack = StackTrace.current.toString().split('\n'); int? targetFrame; final Pattern layoutFramePattern = RegExp(r'^#[0-9]+ +RenderObject.layout \('); @@ -1796,13 +1798,16 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im final Match? targetFrameMatch = targetFramePattern.matchAsPrefix(stack[targetFrame]); final String? problemFunction = (targetFrameMatch != null && targetFrameMatch.groupCount > 0) ? targetFrameMatch.group(1) : stack[targetFrame].trim(); // TODO(jacobr): this case is similar to displaying a single stack frame. - yield ErrorDescription( - "These invalid constraints were provided to $runtimeType's layout() " - 'function by the following function, which probably computed the ' - 'invalid constraints in question:\n' - ' $problemFunction', - ); + return [ + ErrorDescription( + "These invalid constraints were provided to $runtimeType's layout() " + 'function by the following function, which probably computed the ' + 'invalid constraints in question:\n' + ' $problemFunction', + ), + ]; } + return []; }, )); assert(!_debugDoingThisResize); diff --git a/packages/flutter/lib/src/rendering/sliver.dart b/packages/flutter/lib/src/rendering/sliver.dart index 32b72607f5..1fa9de85bf 100644 --- a/packages/flutter/lib/src/rendering/sliver.dart +++ b/packages/flutter/lib/src/rendering/sliver.dart @@ -1203,9 +1203,9 @@ abstract class RenderSliver extends RenderObject { @override void debugAssertDoesMeetConstraints() { assert(geometry!.debugAssertIsValid( - informationCollector: () sync* { - yield describeForError('The RenderSliver that returned the offending geometry was'); - }, + informationCollector: () => [ + describeForError('The RenderSliver that returned the offending geometry was'), + ], )); assert(() { if (geometry!.paintOrigin + geometry!.paintExtent > constraints.remainingPaintExtent) { diff --git a/packages/flutter/lib/src/rendering/table.dart b/packages/flutter/lib/src/rendering/table.dart index 97eefd00cc..e2db5d0583 100644 --- a/packages/flutter/lib/src/rendering/table.dart +++ b/packages/flutter/lib/src/rendering/table.dart @@ -795,6 +795,7 @@ class RenderTable extends RenderBox { /// column, in row order, starting from the first row. /// /// This is a lazily-evaluated iterable. + // flutter_ignore: no_sync_async_star Iterable column(int x) sync* { for (int y = 0; y < rows; y += 1) { final int xy = x + y * columns; @@ -808,6 +809,7 @@ class RenderTable extends RenderBox { /// row, in column order, starting with the first column. /// /// This is a lazily-evaluated iterable. + // flutter_ignore: no_sync_async_star Iterable row(int y) sync* { final int start = y * columns; final int end = (y + 1) * columns; diff --git a/packages/flutter/lib/src/rendering/viewport.dart b/packages/flutter/lib/src/rendering/viewport.dart index e4f0dffd4f..0eb5402495 100644 --- a/packages/flutter/lib/src/rendering/viewport.dart +++ b/packages/flutter/lib/src/rendering/viewport.dart @@ -1718,37 +1718,40 @@ class RenderViewport extends RenderViewportBase get childrenInPaintOrder sync* { + Iterable get childrenInPaintOrder { + final List children = []; if (firstChild == null) - return; + return children; RenderSliver? child = firstChild; while (child != center) { - yield child!; + children.add(child!); child = childAfter(child); } child = lastChild; while (true) { - yield child!; + children.add(child!); if (child == center) - return; + return children; child = childBefore(child); } } @override - Iterable get childrenInHitTestOrder sync* { + Iterable get childrenInHitTestOrder { + final List children = []; if (firstChild == null) - return; + return children; RenderSliver? child = center; while (child != null) { - yield child; + children.add(child); child = childAfter(child); } child = childBefore(center!); while (child != null) { - yield child; + children.add(child); child = childBefore(child); } + return children; } @override @@ -2030,20 +2033,24 @@ class RenderShrinkWrappingViewport extends RenderViewportBase 'child $index'; @override - Iterable get childrenInPaintOrder sync* { + Iterable get childrenInPaintOrder { + final List children = []; RenderSliver? child = lastChild; while (child != null) { - yield child; + children.add(child); child = childBefore(child); } + return children; } @override - Iterable get childrenInHitTestOrder sync* { + Iterable get childrenInHitTestOrder { + final List children = []; RenderSliver? child = firstChild; while (child != null) { - yield child; + children.add(child); child = childAfter(child); } + return children; } } diff --git a/packages/flutter/lib/src/scheduler/binding.dart b/packages/flutter/lib/src/scheduler/binding.dart index af06bdf9e2..3f9ad49f94 100644 --- a/packages/flutter/lib/src/scheduler/binding.dart +++ b/packages/flutter/lib/src/scheduler/binding.dart @@ -288,13 +288,13 @@ mixin SchedulerBinding on BindingBase { } catch (exception, stack) { InformationCollector? collector; assert(() { - collector = () sync* { - yield DiagnosticsProperty( + collector = () => [ + DiagnosticsProperty( 'The TimingsCallback that gets executed was', callback, style: DiagnosticsTreeStyle.errorProperty, - ); - }; + ), + ]; return true; }()); FlutterError.reportError(FlutterErrorDetails( @@ -467,13 +467,15 @@ mixin SchedulerBinding on BindingBase { stack: exceptionStack, library: 'scheduler library', context: ErrorDescription('during a task callback'), - informationCollector: (callbackStack == null) ? null : () sync* { - yield DiagnosticsStackTrace( - '\nThis exception was thrown in the context of a scheduler callback. ' - 'When the scheduler callback was _registered_ (as opposed to when the ' - 'exception was thrown), this was the stack', - callbackStack, - ); + informationCollector: (callbackStack == null) ? null : () { + return [ + DiagnosticsStackTrace( + '\nThis exception was thrown in the context of a scheduler callback. ' + 'When the scheduler callback was _registered_ (as opposed to when the ' + 'exception was thrown), this was the stack', + callbackStack, + ), + ]; }, )); } @@ -566,24 +568,21 @@ mixin SchedulerBinding on BindingBase { FlutterError.reportError(FlutterErrorDetails( exception: reason, library: 'scheduler library', - informationCollector: () sync* { - if (count == 1) { + informationCollector: () => [ + if (count == 1) // TODO(jacobr): I have added an extra line break in this case. - yield ErrorDescription( + ErrorDescription( 'There was one transient callback left. ' 'The stack trace for when it was registered is as follows:', - ); - } else { - yield ErrorDescription( + ) + else + ErrorDescription( 'There were $count transient callbacks left. ' 'The stack traces for when they were registered are as follows:', - ); - } - for (final int id in callbacks.keys) { - final _FrameCallbackEntry entry = callbacks[id]!; - yield DiagnosticsStackTrace('── callback $id ──', entry.debugStack, showSeparator: false); - } - }, + ), + for (final int id in callbacks.keys) + DiagnosticsStackTrace('── callback $id ──', callbacks[id]!.debugStack, showSeparator: false), + ], )); } return true; @@ -1149,13 +1148,15 @@ mixin SchedulerBinding on BindingBase { stack: exceptionStack, library: 'scheduler library', context: ErrorDescription('during a scheduler callback'), - informationCollector: (callbackStack == null) ? null : () sync* { - yield DiagnosticsStackTrace( - '\nThis exception was thrown in the context of a scheduler callback. ' - 'When the scheduler callback was _registered_ (as opposed to when the ' - 'exception was thrown), this was the stack', - callbackStack, - ); + informationCollector: (callbackStack == null) ? null : () { + return [ + DiagnosticsStackTrace( + '\nThis exception was thrown in the context of a scheduler callback. ' + 'When the scheduler callback was _registered_ (as opposed to when the ' + 'exception was thrown), this was the stack', + callbackStack, + ), + ]; }, )); } diff --git a/packages/flutter/lib/src/services/binding.dart b/packages/flutter/lib/src/services/binding.dart index afc201bbda..a56117a399 100644 --- a/packages/flutter/lib/src/services/binding.dart +++ b/packages/flutter/lib/src/services/binding.dart @@ -144,6 +144,9 @@ mixin ServicesBinding on BindingBase, SchedulerBinding { LicenseRegistry.addLicense(_addLicenses); } + // TODO(dnfield): Refactor the license logic. + // https://github.com/flutter/flutter/issues/95043 + // flutter_ignore: no_sync_async_star Stream _addLicenses() async* { // Using _something_ here to break // this into two parts is important because isolates take a while to copy diff --git a/packages/flutter/lib/src/services/hardware_keyboard.dart b/packages/flutter/lib/src/services/hardware_keyboard.dart index dca3daac4b..82efc5a24e 100644 --- a/packages/flutter/lib/src/services/hardware_keyboard.dart +++ b/packages/flutter/lib/src/services/hardware_keyboard.dart @@ -516,9 +516,9 @@ class HardwareKeyboard { } catch (exception, stack) { InformationCollector? collector; assert(() { - collector = () sync* { - yield DiagnosticsProperty('Event', event); - }; + collector = () => [ + DiagnosticsProperty('Event', event), + ]; return true; }()); FlutterError.reportError(FlutterErrorDetails( @@ -833,9 +833,9 @@ class KeyEventManager { } catch (exception, stack) { InformationCollector? collector; assert(() { - collector = () sync* { - yield DiagnosticsProperty('KeyMessage', message); - }; + collector = () => [ + DiagnosticsProperty('KeyMessage', message), + ]; return true; }()); FlutterError.reportError(FlutterErrorDetails( diff --git a/packages/flutter/lib/src/services/raw_keyboard.dart b/packages/flutter/lib/src/services/raw_keyboard.dart index 6132c695c4..515261d09f 100644 --- a/packages/flutter/lib/src/services/raw_keyboard.dart +++ b/packages/flutter/lib/src/services/raw_keyboard.dart @@ -680,9 +680,9 @@ class RawKeyboard { } catch (exception, stack) { InformationCollector? collector; assert(() { - collector = () sync* { - yield DiagnosticsProperty('Event', event); - }; + collector = () => [ + DiagnosticsProperty('Event', event), + ]; return true; }()); FlutterError.reportError(FlutterErrorDetails( diff --git a/packages/flutter/lib/src/widgets/actions.dart b/packages/flutter/lib/src/widgets/actions.dart index 103eb7dd29..42441068e3 100644 --- a/packages/flutter/lib/src/widgets/actions.dart +++ b/packages/flutter/lib/src/widgets/actions.dart @@ -345,13 +345,13 @@ abstract class Action with Diagnosticable { for (final ActionListenerCallback listener in localListeners) { InformationCollector? collector; assert(() { - collector = () sync* { - yield DiagnosticsProperty>( + collector = () => [ + DiagnosticsProperty>( 'The $runtimeType sending notification was', this, style: DiagnosticsTreeStyle.errorProperty, - ); - }; + ), + ]; return true; }()); try { diff --git a/packages/flutter/lib/src/widgets/app.dart b/packages/flutter/lib/src/widgets/app.dart index 868e1138e2..a076ba31c9 100644 --- a/packages/flutter/lib/src/widgets/app.dart +++ b/packages/flutter/lib/src/widgets/app.dart @@ -1472,10 +1472,12 @@ class _WidgetsAppState extends State with WidgetsBindingObserver { // of a particular LocalizationsDelegate.type is loaded so the // localizationsDelegate parameter can be used to override // WidgetsLocalizations.delegate. - Iterable> get _localizationsDelegates sync* { - if (widget.localizationsDelegates != null) - yield* widget.localizationsDelegates!; - yield DefaultWidgetsLocalizations.delegate; + Iterable> get _localizationsDelegates { + return >[ + if (widget.localizationsDelegates != null) + ...widget.localizationsDelegates!, + DefaultWidgetsLocalizations.delegate, + ]; } // BUILDER @@ -1496,33 +1498,33 @@ class _WidgetsAppState extends State with WidgetsBindingObserver { FlutterError.reportError(FlutterErrorDetails( exception: "Warning: This application's locale, $appLocale, is not supported by all of its localization delegates.", library: 'widgets', - informationCollector: () sync* { - for (final Type unsupportedType in unsupportedTypes) { - yield ErrorDescription( + informationCollector: () => [ + for (final Type unsupportedType in unsupportedTypes) + ErrorDescription( '• A $unsupportedType delegate that supports the $appLocale locale was not found.', - ); - } - yield ErrorSpacer(); - if (unsupportedTypes.length == 1 && unsupportedTypes.single.toString() == 'CupertinoLocalizations') { + ), + ErrorSpacer(), + if (unsupportedTypes.length == 1 && unsupportedTypes.single.toString() == 'CupertinoLocalizations') // We previously explicitly avoided checking for this class so it's not uncommon for applications // to have omitted importing the required delegate. - yield ErrorHint( - 'If the application is built using GlobalMaterialLocalizations.delegate, consider using ' - 'GlobalMaterialLocalizations.delegates (plural) instead, as that will automatically declare ' - 'the appropriate Cupertino localizations.' - ); - yield ErrorSpacer(); - } - yield ErrorHint( + ...[ + ErrorHint( + 'If the application is built using GlobalMaterialLocalizations.delegate, consider using ' + 'GlobalMaterialLocalizations.delegates (plural) instead, as that will automatically declare ' + 'the appropriate Cupertino localizations.' + ), + ErrorSpacer(), + ], + ErrorHint( 'The declared supported locales for this app are: ${widget.supportedLocales.join(", ")}' - ); - yield ErrorSpacer(); - yield ErrorDescription( + ), + ErrorSpacer(), + ErrorDescription( 'See https://flutter.dev/tutorials/internationalization/ for more ' "information about configuring an app's locale, supportedLocales, " 'and localizationsDelegates parameters.', - ); - }, + ), + ], )); return true; }()); diff --git a/packages/flutter/lib/src/widgets/drag_target.dart b/packages/flutter/lib/src/widgets/drag_target.dart index 58a249bfbf..7325c8f9ed 100644 --- a/packages/flutter/lib/src/widgets/drag_target.dart +++ b/packages/flutter/lib/src/widgets/drag_target.dart @@ -905,17 +905,19 @@ class _DragAvatar extends Drag { _activeTarget = newTarget; } - Iterable<_DragTargetState> _getDragTargets(Iterable path) sync* { + Iterable<_DragTargetState> _getDragTargets(Iterable path) { // Look for the RenderBoxes that corresponds to the hit target (the hit target // widgets build RenderMetaData boxes for us for this purpose). + final List<_DragTargetState> targets = <_DragTargetState>[]; for (final HitTestEntry entry in path) { final HitTestTarget target = entry.target; if (target is RenderMetaData) { final dynamic metaData = target.metaData; if (metaData is _DragTargetState && metaData.isExpectedDataType(data, T)) - yield metaData; + targets.add(metaData); } } + return targets; } void _leaveAllEntered() { diff --git a/packages/flutter/lib/src/widgets/focus_manager.dart b/packages/flutter/lib/src/widgets/focus_manager.dart index 834101d217..423737fb85 100644 --- a/packages/flutter/lib/src/widgets/focus_manager.dart +++ b/packages/flutter/lib/src/widgets/focus_manager.dart @@ -1520,13 +1520,13 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier { } catch (exception, stack) { InformationCollector? collector; assert(() { - collector = () sync* { - yield DiagnosticsProperty( + collector = () => [ + DiagnosticsProperty( 'The $runtimeType sending notification was', this, style: DiagnosticsTreeStyle.errorProperty, - ); - }; + ), + ]; return true; }()); FlutterError.reportError(FlutterErrorDetails( diff --git a/packages/flutter/lib/src/widgets/framework.dart b/packages/flutter/lib/src/widgets/framework.dart index 7b3699fc61..6bb84f2137 100644 --- a/packages/flutter/lib/src/widgets/framework.dart +++ b/packages/flutter/lib/src/widgets/framework.dart @@ -1455,35 +1455,34 @@ abstract class ParentDataWidget extends ProxyWidget { required ParentData? parentData, RenderObjectWidget? parentDataCreator, DiagnosticsNode? ownershipChain, - }) sync* { + }) { assert(T != dynamic); assert(T != ParentData); assert(debugTypicalAncestorWidgetClass != null); final String description = 'The ParentDataWidget $this wants to apply ParentData of type $T to a RenderObject'; - if (parentData == null) { - yield ErrorDescription( - '$description, which has not been set up to receive any ParentData.', - ); - } else { - yield ErrorDescription( - '$description, which has been set up to accept ParentData of incompatible type ${parentData.runtimeType}.', - ); - } - yield ErrorHint( - 'Usually, this means that the $runtimeType widget has the wrong ancestor RenderObjectWidget. ' - 'Typically, $runtimeType widgets are placed directly inside $debugTypicalAncestorWidgetClass widgets.', - ); - if (parentDataCreator != null) { - yield ErrorHint( - 'The offending $runtimeType is currently placed inside a ${parentDataCreator.runtimeType} widget.', - ); - } - if (ownershipChain != null) { - yield ErrorDescription( - 'The ownership chain for the RenderObject that received the incompatible parent data was:\n $ownershipChain', - ); - } + return [ + if (parentData == null) + ErrorDescription( + '$description, which has not been set up to receive any ParentData.', + ) + else + ErrorDescription( + '$description, which has been set up to accept ParentData of incompatible type ${parentData.runtimeType}.', + ), + ErrorHint( + 'Usually, this means that the $runtimeType widget has the wrong ancestor RenderObjectWidget. ' + 'Typically, $runtimeType widgets are placed directly inside $debugTypicalAncestorWidgetClass widgets.', + ), + if (parentDataCreator != null) + ErrorHint( + 'The offending $runtimeType is currently placed inside a ${parentDataCreator.runtimeType} widget.', + ), + if (ownershipChain != null) + ErrorDescription( + 'The ownership chain for the RenderObject that received the incompatible parent data was:\n $ownershipChain', + ), + ]; } /// Write the data from this widget into the given render object's parent data. @@ -2630,14 +2629,14 @@ class BuildOwner { ErrorDescription('while rebuilding dirty elements'), e, stack, - informationCollector: () sync* { - if (index < _dirtyElements.length) { - yield DiagnosticsDebugCreator(DebugCreator(element)); - yield element.describeElement('The element being rebuilt at the time was index $index of $dirtyCount'); - } else { - yield ErrorHint('The element being rebuilt at the time was index $index of $dirtyCount, but _dirtyElements only had ${_dirtyElements.length} entries. This suggests some confusion in the framework internals.'); - } - }, + informationCollector: () => [ + if (kDebugMode && index < _dirtyElements.length) + DiagnosticsDebugCreator(DebugCreator(element)), + if (index < _dirtyElements.length) + element.describeElement('The element being rebuilt at the time was index $index of $dirtyCount') + else + ErrorHint('The element being rebuilt at the time was index $index of $dirtyCount, but _dirtyElements only had ${_dirtyElements.length} entries. This suggests some confusion in the framework internals.'), + ], ); } if (!kReleaseMode && debugProfileBuildsEnabled) @@ -4721,9 +4720,10 @@ abstract class ComponentElement extends Element { ErrorDescription('building $this'), e, stack, - informationCollector: () sync* { - yield DiagnosticsDebugCreator(DebugCreator(this)); - }, + informationCollector: () => [ + if (kDebugMode) + DiagnosticsDebugCreator(DebugCreator(this)), + ], ), ); } finally { @@ -4741,9 +4741,10 @@ abstract class ComponentElement extends Element { ErrorDescription('building $this'), e, stack, - informationCollector: () sync* { - yield DiagnosticsDebugCreator(DebugCreator(this)); - }, + informationCollector: () => [ + if (kDebugMode) + DiagnosticsDebugCreator(DebugCreator(this)), + ], ), ); _child = updateChild(null, built, slot); diff --git a/packages/flutter/lib/src/widgets/layout_builder.dart b/packages/flutter/lib/src/widgets/layout_builder.dart index 41720af68e..5ad7102138 100644 --- a/packages/flutter/lib/src/widgets/layout_builder.dart +++ b/packages/flutter/lib/src/widgets/layout_builder.dart @@ -127,9 +127,10 @@ class _LayoutBuilderElement extends RenderOb ErrorDescription('building $widget'), e, stack, - informationCollector: () sync* { - yield DiagnosticsDebugCreator(DebugCreator(this)); - }, + informationCollector: () => [ + if (kDebugMode) + DiagnosticsDebugCreator(DebugCreator(this)), + ], ), ); } @@ -142,9 +143,10 @@ class _LayoutBuilderElement extends RenderOb ErrorDescription('building $widget'), e, stack, - informationCollector: () sync* { - yield DiagnosticsDebugCreator(DebugCreator(this)); - }, + informationCollector: () => [ + if (kDebugMode) + DiagnosticsDebugCreator(DebugCreator(this)), + ], ), ); _child = updateChild(null, built, slot); diff --git a/packages/flutter/lib/src/widgets/navigator.dart b/packages/flutter/lib/src/widgets/navigator.dart index ce01b0abcb..65eb30883c 100644 --- a/packages/flutter/lib/src/widgets/navigator.dart +++ b/packages/flutter/lib/src/widgets/navigator.dart @@ -3515,9 +3515,11 @@ class NavigatorState extends State with TickerProviderStateMixin, Res /// The overlay this navigator uses for its visual presentation. OverlayState? get overlay => _overlayKey.currentState; - Iterable get _allRouteOverlayEntries sync* { - for (final _RouteEntry entry in _history) - yield* entry.route.overlayEntries; + Iterable get _allRouteOverlayEntries { + return [ + for (final _RouteEntry entry in _history) + ...entry.route.overlayEntries, + ]; } String? _lastAnnouncedRouteName; diff --git a/packages/flutter/lib/src/widgets/nested_scroll_view.dart b/packages/flutter/lib/src/widgets/nested_scroll_view.dart index f951b6c47c..9098084886 100644 --- a/packages/flutter/lib/src/widgets/nested_scroll_view.dart +++ b/packages/flutter/lib/src/widgets/nested_scroll_view.dart @@ -1133,9 +1133,9 @@ class _NestedScrollController extends ScrollController { ); } - Iterable<_NestedScrollPosition> get nestedPositions sync* { + Iterable<_NestedScrollPosition> get nestedPositions { // TODO(vegorov): use instance method version of castFrom when it is available. - yield* Iterable.castFrom(positions); + return Iterable.castFrom(positions); } } diff --git a/packages/flutter/lib/src/widgets/router.dart b/packages/flutter/lib/src/widgets/router.dart index 8faf464f57..bca9f5769e 100644 --- a/packages/flutter/lib/src/widgets/router.dart +++ b/packages/flutter/lib/src/widgets/router.dart @@ -809,13 +809,13 @@ class _CallbackHookProvider { stack: stack, library: 'widget library', context: ErrorDescription('while invoking the callback for $runtimeType'), - informationCollector: () sync* { - yield DiagnosticsProperty<_CallbackHookProvider>( + informationCollector: () => [ + DiagnosticsProperty<_CallbackHookProvider>( 'The $runtimeType that invoked the callback was', this, style: DiagnosticsTreeStyle.errorProperty, - ); - }, + ), + ], )); return defaultValue; } diff --git a/packages/flutter/lib/src/widgets/routes.dart b/packages/flutter/lib/src/widgets/routes.dart index 1b4c35bbd5..3f37bf2517 100644 --- a/packages/flutter/lib/src/widgets/routes.dart +++ b/packages/flutter/lib/src/widgets/routes.dart @@ -1634,9 +1634,11 @@ abstract class ModalRoute extends TransitionRoute with LocalHistoryRoute createOverlayEntries() sync* { - yield _modalBarrier = OverlayEntry(builder: _buildModalBarrier); - yield _modalScope = OverlayEntry(builder: _buildModalScope, maintainState: maintainState); + Iterable createOverlayEntries() { + return [ + _modalBarrier = OverlayEntry(builder: _buildModalBarrier), + _modalScope = OverlayEntry(builder: _buildModalScope, maintainState: maintainState), + ]; } @override diff --git a/packages/flutter/lib/src/widgets/scroll_notification_observer.dart b/packages/flutter/lib/src/widgets/scroll_notification_observer.dart index 8c8a12ee26..7829cbd59e 100644 --- a/packages/flutter/lib/src/widgets/scroll_notification_observer.dart +++ b/packages/flutter/lib/src/widgets/scroll_notification_observer.dart @@ -139,13 +139,13 @@ class ScrollNotificationObserverState extends State stack: stack, library: 'widget library', context: ErrorDescription('while dispatching notifications for $runtimeType'), - informationCollector: () sync* { - yield DiagnosticsProperty( + informationCollector: () => [ + DiagnosticsProperty( 'The $runtimeType sending notification was', this, style: DiagnosticsTreeStyle.errorProperty, - ); - }, + ), + ], )); } } diff --git a/packages/flutter/lib/src/widgets/shortcuts.dart b/packages/flutter/lib/src/widgets/shortcuts.dart index 25ce53353c..7dcedeb90b 100644 --- a/packages/flutter/lib/src/widgets/shortcuts.dart +++ b/packages/flutter/lib/src/widgets/shortcuts.dart @@ -483,8 +483,8 @@ class SingleActivator with Diagnosticable implements ShortcutActivator { final bool meta; @override - Iterable get triggers sync* { - yield trigger; + Iterable get triggers { + return [trigger]; } @override diff --git a/packages/flutter/lib/src/widgets/widget_inspector.dart b/packages/flutter/lib/src/widgets/widget_inspector.dart index 3ca2436537..9201f63c7d 100644 --- a/packages/flutter/lib/src/widgets/widget_inspector.dart +++ b/packages/flutter/lib/src/widgets/widget_inspector.dart @@ -2916,9 +2916,9 @@ bool _isDebugCreator(DiagnosticsNode node) => node is DiagnosticsDebugCreator; /// in [WidgetsBinding.initInstances]. /// /// This is meant to be called only in debug mode. In other modes, it yields an empty list. -Iterable debugTransformDebugCreator(Iterable properties) sync* { +Iterable debugTransformDebugCreator(Iterable properties) { if (!kDebugMode) { - return; + return []; } final List pending = []; ErrorSummary? errorSummary; @@ -2929,20 +2929,22 @@ Iterable debugTransformDebugCreator(Iterable p } } bool foundStackTrace = false; + final List result = []; for (final DiagnosticsNode node in properties) { if (!foundStackTrace && node is DiagnosticsStackTrace) foundStackTrace = true; if (_isDebugCreator(node)) { - yield* _parseDiagnosticsNode(node, errorSummary); + result.addAll(_parseDiagnosticsNode(node, errorSummary)); } else { if (foundStackTrace) { pending.add(node); } else { - yield node; + result.add(node); } } } - yield* pending; + result.addAll(pending); + return result; } /// Transform the input [DiagnosticsNode]. @@ -2951,23 +2953,24 @@ Iterable debugTransformDebugCreator(Iterable p Iterable _parseDiagnosticsNode( DiagnosticsNode node, ErrorSummary? errorSummary, -) sync* { +) { assert(_isDebugCreator(node)); try { final DebugCreator debugCreator = node.value! as DebugCreator; final Element element = debugCreator.element; - yield* _describeRelevantUserCode(element, errorSummary); + return _describeRelevantUserCode(element, errorSummary); } catch (error, stack) { scheduleMicrotask(() { FlutterError.reportError(FlutterErrorDetails( exception: error, stack: stack, library: 'widget inspector', - informationCollector: () sync* { - yield DiagnosticsNode.message('This exception was caught while trying to describe the user-relevant code of another error.'); - } + informationCollector: () => [ + DiagnosticsNode.message('This exception was caught while trying to describe the user-relevant code of another error.'), + ], )); }); + return []; } } diff --git a/packages/flutter/test/material/list_tile_test.dart b/packages/flutter/test/material/list_tile_test.dart index 0248a4b59a..f9a26b5456 100644 --- a/packages/flutter/test/material/list_tile_test.dart +++ b/packages/flutter/test/material/list_tile_test.dart @@ -379,6 +379,11 @@ void main() { expect(output, isEmpty); }); + testWidgets('ListTile.divideTiles with single item list', (WidgetTester tester) async { + final Iterable output = ListTile.divideTiles(tiles: const [SizedBox()], color: Colors.grey); + expect(output.single, isA()); + }); + testWidgets('ListTile.divideTiles only runs the generator once', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/pull/78879 int callCount = 0; diff --git a/packages/flutter_tools/lib/src/commands/update_packages.dart b/packages/flutter_tools/lib/src/commands/update_packages.dart index a3b8fc1832..c293a11403 100644 --- a/packages/flutter_tools/lib/src/commands/update_packages.dart +++ b/packages/flutter_tools/lib/src/commands/update_packages.dart @@ -740,26 +740,22 @@ class PubspecYaml { } /// This returns all the explicit dependencies that this pubspec.yaml lists under dependencies. - Iterable get dependencies sync* { + Iterable get dependencies { // It works by iterating over the parsed data from _parse above, collecting // all the dependencies that were found, ignoring any that are flagged as as // overridden by subsequent entries in the same file and any that have the // magic comment flagging them as auto-generated transitive dependencies // that we added in a previous run. - for (final PubspecLine data in inputData) { - if (data is PubspecDependency && data.kind != DependencyKind.overridden && !data.isTransitive && !data.isDevDependency) { - yield data; - } - } + return inputData + .whereType() + .where((PubspecDependency data) => data.kind != DependencyKind.overridden && !data.isTransitive && !data.isDevDependency); } /// This returns all regular dependencies and all dev dependencies. - Iterable get allDependencies sync* { - for (final PubspecLine data in inputData) { - if (data is PubspecDependency && data.kind != DependencyKind.overridden && !data.isTransitive) { - yield data; - } - } + Iterable get allDependencies { + return inputData + .whereType() + .where((PubspecDependency data) => data.kind != DependencyKind.overridden && !data.isTransitive); } /// Take a dependency graph with explicit version numbers, and apply them to @@ -1412,23 +1408,26 @@ class PubDependencyTree { String package, { @required Set seen, @required Set exclude, - }) sync* { + List/*?*/ result, + }) { assert(seen != null); assert(exclude != null); + result ??= []; if (!_dependencyTree.containsKey(package)) { // We have no transitive dependencies extracted for flutter_sdk packages // because they were omitted from pubspec.yaml used for 'pub upgrade' run. - return; + return result; } for (final String dependency in _dependencyTree[package]) { if (!seen.contains(dependency)) { if (!exclude.contains(dependency)) { - yield dependency; + result.add(dependency); } seen.add(dependency); - yield* getTransitiveDependenciesFor(dependency, seen: seen, exclude: exclude); + getTransitiveDependenciesFor(dependency, seen: seen, exclude: exclude, result: result); } } + return result; } /// The version that a particular package ended up with. diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 5b691193a2..53bcf189b4 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -635,9 +635,9 @@ abstract class Device { @override String toString() => name; - static Stream descriptions(List devices) async* { + static Future> descriptions(List devices) async { if (devices.isEmpty) { - return; + return const []; } // Extract device information @@ -665,13 +665,14 @@ abstract class Device { } // Join columns into lines of text - for (final List row in table) { - yield indices.map((int i) => row[i].padRight(widths[i])).followedBy([row.last]).join(' • '); - } + return [ + for (final List row in table) + indices.map((int i) => row[i].padRight(widths[i])).followedBy([row.last]).join(' • '), + ]; } static Future printDevices(List devices, Logger logger) async { - await descriptions(devices).forEach(logger.printStatus); + (await descriptions(devices)).forEach(logger.printStatus); } static List devicesPlatformTypes(List devices) { diff --git a/packages/flutter_tools/lib/src/doctor.dart b/packages/flutter_tools/lib/src/doctor.dart index d47a55c7c6..342bf44af7 100644 --- a/packages/flutter_tools/lib/src/doctor.dart +++ b/packages/flutter_tools/lib/src/doctor.dart @@ -520,7 +520,7 @@ class DeviceValidator extends DoctorValidator { final List devices = await _deviceManager.getAllConnectedDevices(); List installedMessages = []; if (devices.isNotEmpty) { - installedMessages = await Device.descriptions(devices) + installedMessages = (await Device.descriptions(devices)) .map((String msg) => ValidationMessage(msg)).toList(); } diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart index e69ac9902d..04033e5cf2 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart @@ -1372,7 +1372,7 @@ abstract class FlutterCommand extends Command { final StringBuffer result = StringBuffer(); result.writeln(userMessages.flutterFoundButUnsupportedDevices); result.writeAll( - await Device.descriptions(unsupportedDevices) + (await Device.descriptions(unsupportedDevices)) .map((String desc) => desc) .toList(), '\n',