Ban sync*/async* from user facing code (#95050)

This commit is contained in:
Dan Field 2021-12-12 13:05:03 -08:00 committed by GitHub
parent 895beb04bb
commit ab0a335973
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 656 additions and 580 deletions

View File

@ -24,6 +24,7 @@ import 'utils.dart';
final String flutterRoot = path.dirname(path.dirname(path.dirname(path.fromUri(Platform.script)))); 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 flutter = path.join(flutterRoot, 'bin', Platform.isWindows ? 'flutter.bat' : 'flutter');
final String flutterPackages = path.join(flutterRoot, 'packages'); 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 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 pub = path.join(flutterRoot, 'bin', 'cache', 'dart-sdk', 'bin', Platform.isWindows ? 'pub.bat' : 'pub');
final String pubCache = path.join(flutterRoot, '.pub-cache'); final String pubCache = path.join(flutterRoot, '.pub-cache');
@ -51,6 +52,10 @@ Future<void> run(List<String> arguments) async {
exitWithError(<String>['The analyze.dart script must be run with --enable-asserts.']); exitWithError(<String>['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...'); print('$clock No runtimeType in toString...');
await verifyNoRuntimeTypeInToString(flutterRoot); await verifyNoRuntimeTypeInToString(flutterRoot);
@ -153,6 +158,34 @@ Future<void> run(List<String> arguments) async {
// TESTS // TESTS
Future<void> 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<String> errors = <String>[];
await for (final File file in _allFiles(workingDirectory, 'dart', minimumMatches: minimumMatches)) {
if (file.path.contains('test')) {
continue;
}
final List<String> 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(<String>[
'${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 _findGoldenTestPattern = RegExp(r'matchesGoldenFile\(');
final RegExp _findGoldenDefinitionPattern = RegExp(r'matchesGoldenFile\(Object'); final RegExp _findGoldenDefinitionPattern = RegExp(r'matchesGoldenFile\(Object');
final RegExp _leadingComment = RegExp(r'\/\/'); final RegExp _leadingComment = RegExp(r'\/\/');

View File

@ -430,6 +430,8 @@ Future<void> runForbiddenFromReleaseTests() async {
'--snapshot', path.join(tempDirectory.path, 'snapshot.arm64-v8a.json'), '--snapshot', path.join(tempDirectory.path, 'snapshot.arm64-v8a.json'),
'--package-config', path.join(flutterRoot, 'examples', 'hello_world', '.dart_tool', 'package_config.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/widget_inspector.dart::WidgetInspectorService',
'--forbidden-type', 'package:flutter/src/widgets/framework.dart::DebugCreator',
'--forbidden-type', 'package:flutter/src/foundation/print.dart::debugPrint',
]; ];
await runCommand( await runCommand(
dart, dart,

View File

@ -48,9 +48,9 @@ class CastListState extends State<CastList> {
const Actor('James Madison', 'JM'), const Actor('James Madison', 'JM'),
]; ];
Iterable<Widget> get actorWidgets sync* { Iterable<Widget> get actorWidgets {
for (final Actor actor in _cast) { return _cast.map((Actor actor) {
yield Padding( return Padding(
padding: const EdgeInsets.all(4.0), padding: const EdgeInsets.all(4.0),
child: Chip( child: Chip(
avatar: CircleAvatar(child: Text(actor.initials)), avatar: CircleAvatar(child: Text(actor.initials)),
@ -64,7 +64,7 @@ class CastListState extends State<CastList> {
}, },
), ),
); );
} });
} }
@override @override

View File

@ -30,6 +30,7 @@ class MyStatefulWidget extends StatefulWidget {
} }
class _MyStatefulWidgetState extends State<MyStatefulWidget> { class _MyStatefulWidgetState extends State<MyStatefulWidget> {
// ignore: no_sync_async_star
final Stream<int> _bids = (() async* { final Stream<int> _bids = (() async* {
await Future<void>.delayed(const Duration(seconds: 1)); await Future<void>.delayed(const Duration(seconds: 1));
yield 1; yield 1;

View File

@ -94,13 +94,13 @@ class RenderDiagonal extends RenderBox with SlottedContainerRenderObjectMixin<Di
// Returns children in hit test order. // Returns children in hit test order.
@override @override
Iterable<RenderBox> get children sync* { Iterable<RenderBox> get children {
if (_topLeft != null) { return <RenderBox>[
yield _topLeft!; if (_topLeft != null)
} _topLeft!,
if (_bottomRight != null) { if (_bottomRight != null)
yield _bottomRight!; _bottomRight!,
} ];
} }
// LAYOUT // LAYOUT

View File

@ -141,13 +141,13 @@ mixin AnimationLocalListenersMixin {
for (final VoidCallback listener in localListeners) { for (final VoidCallback listener in localListeners) {
InformationCollector? collector; InformationCollector? collector;
assert(() { assert(() {
collector = () sync* { collector = () => <DiagnosticsNode>[
yield DiagnosticsProperty<AnimationLocalListenersMixin>( DiagnosticsProperty<AnimationLocalListenersMixin>(
'The $runtimeType notifying listeners was', 'The $runtimeType notifying listeners was',
this, this,
style: DiagnosticsTreeStyle.errorProperty, style: DiagnosticsTreeStyle.errorProperty,
); ),
}; ];
return true; return true;
}()); }());
try { try {
@ -234,13 +234,13 @@ mixin AnimationLocalStatusListenersMixin {
} catch (exception, stack) { } catch (exception, stack) {
InformationCollector? collector; InformationCollector? collector;
assert(() { assert(() {
collector = () sync* { collector = () => <DiagnosticsNode>[
yield DiagnosticsProperty<AnimationLocalStatusListenersMixin>( DiagnosticsProperty<AnimationLocalStatusListenersMixin>(
'The $runtimeType notifying status listeners was', 'The $runtimeType notifying status listeners was',
this, this,
style: DiagnosticsTreeStyle.errorProperty, style: DiagnosticsTreeStyle.errorProperty,
); ),
}; ];
return true; return true;
}()); }());
FlutterError.reportError(FlutterErrorDetails( FlutterError.reportError(FlutterErrorDetails(

View File

@ -487,10 +487,12 @@ class _CupertinoAppState extends State<CupertinoApp> {
// of a particular LocalizationsDelegate.type is loaded so the // of a particular LocalizationsDelegate.type is loaded so the
// localizationsDelegate parameter can be used to override // localizationsDelegate parameter can be used to override
// _CupertinoLocalizationsDelegate. // _CupertinoLocalizationsDelegate.
Iterable<LocalizationsDelegate<dynamic>> get _localizationsDelegates sync* { Iterable<LocalizationsDelegate<dynamic>> get _localizationsDelegates {
if (widget.localizationsDelegates != null) return <LocalizationsDelegate<dynamic>>[
yield* widget.localizationsDelegates!; if (widget.localizationsDelegates != null)
yield DefaultCupertinoLocalizations.delegate; ...widget.localizationsDelegates!,
DefaultCupertinoLocalizations.delegate,
];
} }
Widget _inspectorSelectButtonBuilder(BuildContext context, VoidCallback onPressed) { Widget _inspectorSelectButtonBuilder(BuildContext context, VoidCallback onPressed) {

View File

@ -2050,16 +2050,18 @@ class _RenderCupertinoDialogActions extends RenderBox
markNeedsPaint(); markNeedsPaint();
} }
Iterable<RenderBox> get _pressedButtons sync* { Iterable<RenderBox> get _pressedButtons {
final List<RenderBox> boxes = <RenderBox>[];
RenderBox? currentChild = firstChild; RenderBox? currentChild = firstChild;
while (currentChild != null) { while (currentChild != null) {
assert(currentChild.parentData is _ActionButtonParentData); assert(currentChild.parentData is _ActionButtonParentData);
final _ActionButtonParentData parentData = currentChild.parentData! as _ActionButtonParentData; final _ActionButtonParentData parentData = currentChild.parentData! as _ActionButtonParentData;
if (parentData.isPressed) { if (parentData.isPressed) {
yield currentChild; boxes.add(currentChild);
} }
currentChild = childAfter(currentChild); currentChild = childAfter(currentChild);
} }
return boxes;
} }
bool get _isButtonPressed { bool get _isButtonPressed {

View File

@ -542,10 +542,10 @@ class FlutterErrorDetails with Diagnosticable {
/// FlutterError.reportError(FlutterErrorDetails( /// FlutterError.reportError(FlutterErrorDetails(
/// exception: error, /// exception: error,
/// stack: stack, /// stack: stack,
/// informationCollector: () sync* { /// informationCollector: () => <DiagnosticsNode>[
/// yield ErrorDescription('This happened while climbing the space elevator.'); /// ErrorDescription('This happened while climbing the space elevator.'),
/// yield ErrorHint('The process ID is: $pid'); /// ErrorHint('The process ID is: $pid'),
/// }, /// ],
/// )); /// ));
/// } /// }
/// } /// }

View File

@ -113,7 +113,7 @@ class CachingIterable<E> extends IterableBase<E> {
/// once. If you have an [Iterable], you can pass its [iterator] /// once. If you have an [Iterable], you can pass its [iterator]
/// field as the argument to this constructor. /// 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 /// ```dart
/// Iterable<int> range(int start, int end) sync* { /// Iterable<int> range(int start, int end) sync* {
@ -125,6 +125,10 @@ class CachingIterable<E> extends IterableBase<E> {
/// print(i.length); // walks the list /// print(i.length); // walks the list
/// print(i.length); // efficient /// 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); CachingIterable(this._prefillIterator);
final Iterator<E> _prefillIterator; final Iterator<E> _prefillIterator;

View File

@ -312,13 +312,13 @@ class ChangeNotifier implements Listenable {
stack: stack, stack: stack,
library: 'foundation library', library: 'foundation library',
context: ErrorDescription('while dispatching notifications for $runtimeType'), context: ErrorDescription('while dispatching notifications for $runtimeType'),
informationCollector: () sync* { informationCollector: () => <DiagnosticsNode>[
yield DiagnosticsProperty<ChangeNotifier>( DiagnosticsProperty<ChangeNotifier>(
'The $runtimeType sending notification was', 'The $runtimeType sending notification was',
this, this,
style: DiagnosticsTreeStyle.errorProperty, style: DiagnosticsTreeStyle.errorProperty,
); ),
}, ],
)); ));
} }
} }

View File

@ -870,12 +870,12 @@ class _PrefixedStringBuilder {
/// ///
/// This method wraps a sequence of text where only some spans of text can be /// This method wraps a sequence of text where only some spans of text can be
/// used as wrap boundaries. /// used as wrap boundaries.
static Iterable<String> _wordWrapLine(String message, List<int> wrapRanges, int width, { int startOffset = 0, int otherLineOffset = 0}) sync* { static Iterable<String> _wordWrapLine(String message, List<int> wrapRanges, int width, { int startOffset = 0, int otherLineOffset = 0}) {
if (message.length + startOffset < width) { if (message.length + startOffset < width) {
// Nothing to do. The line doesn't wrap. // Nothing to do. The line doesn't wrap.
yield message; return <String>[message];
return;
} }
final List<String> wrappedLine = <String>[];
int startForLengthCalculations = -startOffset; int startForLengthCalculations = -startOffset;
bool addPrefix = false; bool addPrefix = false;
int index = 0; int index = 0;
@ -920,10 +920,10 @@ class _PrefixedStringBuilder {
lastWordEnd = index; lastWordEnd = index;
} }
final String line = message.substring(start, lastWordEnd); final String line = message.substring(start, lastWordEnd);
yield line; wrappedLine.add(line);
addPrefix = true; addPrefix = true;
if (lastWordEnd >= message.length) if (lastWordEnd >= message.length)
return; return wrappedLine;
// just yielded a line // just yielded a line
if (lastWordEnd == index) { if (lastWordEnd == index) {
// we broke at current position // we broke at current position
@ -2590,12 +2590,10 @@ class FlagsSummary<T> extends DiagnosticsProperty<Map<String, T?>> {
// //
// For a null value, it is omitted unless `includeEmpty` is true and // For a null value, it is omitted unless `includeEmpty` is true and
// [ifEntryNull] contains a corresponding description. // [ifEntryNull] contains a corresponding description.
Iterable<String> _formattedValues() sync* { Iterable<String> _formattedValues() {
for (final MapEntry<String, T?> entry in value.entries) { return value.entries
if (entry.value != null) { .where((MapEntry<String, T?> entry) => entry.value != null)
yield entry.key; .map((MapEntry<String, T?> entry) => entry.key);
}
}
} }
} }

View File

@ -139,7 +139,7 @@ class LicenseEntryWithLineBreaks extends LicenseEntry {
final String text; final String text;
@override @override
Iterable<LicenseParagraph> get paragraphs sync* { Iterable<LicenseParagraph> get paragraphs {
int lineStart = 0; int lineStart = 0;
int currentPosition = 0; int currentPosition = 0;
int lastLineIndent = 0; int lastLineIndent = 0;
@ -147,6 +147,7 @@ class LicenseEntryWithLineBreaks extends LicenseEntry {
int? currentParagraphIndentation; int? currentParagraphIndentation;
_LicenseEntryWithLineBreaksParserState state = _LicenseEntryWithLineBreaksParserState.beforeParagraph; _LicenseEntryWithLineBreaksParserState state = _LicenseEntryWithLineBreaksParserState.beforeParagraph;
final List<String> lines = <String>[]; final List<String> lines = <String>[];
final List<LicenseParagraph> result = <LicenseParagraph>[];
void addLine() { void addLine() {
assert(lineStart < currentPosition); assert(lineStart < currentPosition);
@ -182,7 +183,7 @@ class LicenseEntryWithLineBreaks extends LicenseEntry {
case '\n': case '\n':
case '\f': case '\f':
if (lines.isNotEmpty) { if (lines.isNotEmpty) {
yield getParagraph(); result.add(getParagraph());
} }
if (text[currentPosition] == '\r' && currentPosition < text.length - 1 if (text[currentPosition] == '\r' && currentPosition < text.length - 1
&& text[currentPosition + 1] == '\n') { && text[currentPosition + 1] == '\n') {
@ -206,7 +207,7 @@ class LicenseEntryWithLineBreaks extends LicenseEntry {
startParagraph: startParagraph:
default: default:
if (lines.isNotEmpty && currentLineIndent > lastLineIndent) { if (lines.isNotEmpty && currentLineIndent > lastLineIndent) {
yield getParagraph(); result.add(getParagraph());
currentParagraphIndentation = null; currentParagraphIndentation = null;
} }
// The following is a wild heuristic for guessing the indentation level. // The following is a wild heuristic for guessing the indentation level.
@ -231,7 +232,7 @@ class LicenseEntryWithLineBreaks extends LicenseEntry {
break; break;
case '\f': case '\f':
addLine(); addLine();
yield getParagraph(); result.add(getParagraph());
lastLineIndent = 0; lastLineIndent = 0;
currentLineIndent = 0; currentLineIndent = 0;
currentParagraphIndentation = null; currentParagraphIndentation = null;
@ -248,14 +249,15 @@ class LicenseEntryWithLineBreaks extends LicenseEntry {
switch (state) { switch (state) {
case _LicenseEntryWithLineBreaksParserState.beforeParagraph: case _LicenseEntryWithLineBreaksParserState.beforeParagraph:
if (lines.isNotEmpty) { if (lines.isNotEmpty) {
yield getParagraph(); result.add(getParagraph());
} }
break; break;
case _LicenseEntryWithLineBreaksParserState.inParagraph: case _LicenseEntryWithLineBreaksParserState.inParagraph:
addLine(); addLine();
yield getParagraph(); result.add(getParagraph());
break; break;
} }
return result;
} }
} }
@ -307,6 +309,9 @@ class LicenseRegistry {
/// Returns the licenses that have been registered. /// Returns the licenses that have been registered.
/// ///
/// Generating the list of licenses is expensive. /// 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<LicenseEntry> get licenses async* { static Stream<LicenseEntry> get licenses async* {
if (_collectors == null) if (_collectors == null)
return; return;

View File

@ -113,11 +113,11 @@ enum _WordWrapParseMode { inSpace, inWord, atBreak }
/// and so forth. It is only intended for formatting error messages. /// and so forth. It is only intended for formatting error messages.
/// ///
/// The default [debugPrint] implementation uses this for its line wrapping. /// The default [debugPrint] implementation uses this for its line wrapping.
Iterable<String> debugWordWrap(String message, int width, { String wrapIndent = '' }) sync* { Iterable<String> debugWordWrap(String message, int width, { String wrapIndent = '' }) {
if (message.length < width || message.trimLeft()[0] == '#') { if (message.length < width || message.trimLeft()[0] == '#') {
yield message; return <String>[message];
return;
} }
final List<String> wrapped = <String>[];
final Match prefixMatch = _indentPattern.matchAsPrefix(message)!; final Match prefixMatch = _indentPattern.matchAsPrefix(message)!;
final String prefix = wrapIndent + ' ' * prefixMatch.group(0)!.length; final String prefix = wrapIndent + ' ' * prefixMatch.group(0)!.length;
int start = 0; int start = 0;
@ -149,13 +149,13 @@ Iterable<String> debugWordWrap(String message, int width, { String wrapIndent =
lastWordEnd = index; lastWordEnd = index;
} }
if (addPrefix) { if (addPrefix) {
yield prefix + message.substring(start, lastWordEnd); wrapped.add(prefix + message.substring(start, lastWordEnd));
} else { } else {
yield message.substring(start, lastWordEnd); wrapped.add(message.substring(start, lastWordEnd));
addPrefix = true; addPrefix = true;
} }
if (lastWordEnd >= message.length) if (lastWordEnd >= message.length)
return; return wrapped;
// just yielded a line // just yielded a line
if (lastWordEnd == index) { if (lastWordEnd == index) {
// we broke at current position // we broke at current position

View File

@ -407,9 +407,9 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H
library: 'gesture library', library: 'gesture library',
context: ErrorDescription('while dispatching a non-hit-tested pointer event'), context: ErrorDescription('while dispatching a non-hit-tested pointer event'),
event: event, event: event,
informationCollector: () sync* { informationCollector: () => <DiagnosticsNode>[
yield DiagnosticsProperty<PointerEvent>('Event', event, style: DiagnosticsTreeStyle.errorProperty); DiagnosticsProperty<PointerEvent>('Event', event, style: DiagnosticsTreeStyle.errorProperty),
}, ],
)); ));
} }
return; return;
@ -425,10 +425,10 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H
context: ErrorDescription('while dispatching a pointer event'), context: ErrorDescription('while dispatching a pointer event'),
event: event, event: event,
hitTestEntry: entry, hitTestEntry: entry,
informationCollector: () sync* { informationCollector: () => <DiagnosticsNode>[
yield DiagnosticsProperty<PointerEvent>('Event', event, style: DiagnosticsTreeStyle.errorProperty); DiagnosticsProperty<PointerEvent>('Event', event, style: DiagnosticsTreeStyle.errorProperty),
yield DiagnosticsProperty<HitTestTarget>('Target', entry.target, style: DiagnosticsTreeStyle.errorProperty); DiagnosticsProperty<HitTestTarget>('Target', entry.target, style: DiagnosticsTreeStyle.errorProperty),
}, ],
)); ));
} }
} }

View File

@ -44,197 +44,188 @@ class PointerEventConverter {
/// [dart:ui.FlutterView.devicePixelRatio]) is used to convert the incoming data /// [dart:ui.FlutterView.devicePixelRatio]) is used to convert the incoming data
/// from physical coordinates to logical pixels. See the discussion at /// from physical coordinates to logical pixels. See the discussion at
/// [PointerEvent] for more details on the [PointerEvent] coordinate space. /// [PointerEvent] for more details on the [PointerEvent] coordinate space.
static Iterable<PointerEvent> expand(Iterable<ui.PointerData> data, double devicePixelRatio) sync* { static Iterable<PointerEvent> expand(Iterable<ui.PointerData> data, double devicePixelRatio) {
for (final ui.PointerData datum in data) { return data
final Offset position = Offset(datum.physicalX, datum.physicalY) / devicePixelRatio; .where((ui.PointerData datum) => datum.signalKind != ui.PointerSignalKind.unknown)
assert(position != null); .map((ui.PointerData datum) {
final Offset delta = Offset(datum.physicalDeltaX, datum.physicalDeltaY) / devicePixelRatio; final Offset position = Offset(datum.physicalX, datum.physicalY) / devicePixelRatio;
final double radiusMinor = _toLogicalPixels(datum.radiusMinor, devicePixelRatio); assert(position != null);
final double radiusMajor = _toLogicalPixels(datum.radiusMajor, devicePixelRatio); final Offset delta = Offset(datum.physicalDeltaX, datum.physicalDeltaY) / devicePixelRatio;
final double radiusMin = _toLogicalPixels(datum.radiusMin, devicePixelRatio); final double radiusMinor = _toLogicalPixels(datum.radiusMinor, devicePixelRatio);
final double radiusMax = _toLogicalPixels(datum.radiusMax, devicePixelRatio); final double radiusMajor = _toLogicalPixels(datum.radiusMajor, devicePixelRatio);
final Duration timeStamp = datum.timeStamp; final double radiusMin = _toLogicalPixels(datum.radiusMin, devicePixelRatio);
final PointerDeviceKind kind = datum.kind; final double radiusMax = _toLogicalPixels(datum.radiusMax, devicePixelRatio);
assert(datum.change != null); final Duration timeStamp = datum.timeStamp;
if (datum.signalKind == null || datum.signalKind == ui.PointerSignalKind.none) { final PointerDeviceKind kind = datum.kind;
switch (datum.change) { assert(datum.change != null);
case ui.PointerChange.add: switch (datum.signalKind ?? ui.PointerSignalKind.none) {
yield PointerAddedEvent( case ui.PointerSignalKind.none:
timeStamp: timeStamp, switch (datum.change) {
kind: kind, case ui.PointerChange.add:
device: datum.device, return PointerAddedEvent(
position: position, timeStamp: timeStamp,
obscured: datum.obscured, kind: kind,
pressureMin: datum.pressureMin, device: datum.device,
pressureMax: datum.pressureMax, position: position,
distance: datum.distance, obscured: datum.obscured,
distanceMax: datum.distanceMax, pressureMin: datum.pressureMin,
radiusMin: radiusMin, pressureMax: datum.pressureMax,
radiusMax: radiusMax, distance: datum.distance,
orientation: datum.orientation, distanceMax: datum.distanceMax,
tilt: datum.tilt, radiusMin: radiusMin,
embedderId: datum.embedderId, radiusMax: radiusMax,
); orientation: datum.orientation,
break; tilt: datum.tilt,
case ui.PointerChange.hover: embedderId: datum.embedderId,
yield PointerHoverEvent( );
timeStamp: timeStamp, case ui.PointerChange.hover:
kind: kind, return PointerHoverEvent(
device: datum.device, timeStamp: timeStamp,
position: position, kind: kind,
delta: delta, device: datum.device,
buttons: datum.buttons, position: position,
obscured: datum.obscured, delta: delta,
pressureMin: datum.pressureMin, buttons: datum.buttons,
pressureMax: datum.pressureMax, obscured: datum.obscured,
distance: datum.distance, pressureMin: datum.pressureMin,
distanceMax: datum.distanceMax, pressureMax: datum.pressureMax,
size: datum.size, distance: datum.distance,
radiusMajor: radiusMajor, distanceMax: datum.distanceMax,
radiusMinor: radiusMinor, size: datum.size,
radiusMin: radiusMin, radiusMajor: radiusMajor,
radiusMax: radiusMax, radiusMinor: radiusMinor,
orientation: datum.orientation, radiusMin: radiusMin,
tilt: datum.tilt, radiusMax: radiusMax,
synthesized: datum.synthesized, orientation: datum.orientation,
embedderId: datum.embedderId, tilt: datum.tilt,
); synthesized: datum.synthesized,
break; embedderId: datum.embedderId,
case ui.PointerChange.down: );
yield PointerDownEvent( case ui.PointerChange.down:
timeStamp: timeStamp, return PointerDownEvent(
pointer: datum.pointerIdentifier, timeStamp: timeStamp,
kind: kind, pointer: datum.pointerIdentifier,
device: datum.device, kind: kind,
position: position, device: datum.device,
buttons: _synthesiseDownButtons(datum.buttons, kind), position: position,
obscured: datum.obscured, buttons: _synthesiseDownButtons(datum.buttons, kind),
pressure: datum.pressure, obscured: datum.obscured,
pressureMin: datum.pressureMin, pressure: datum.pressure,
pressureMax: datum.pressureMax, pressureMin: datum.pressureMin,
distanceMax: datum.distanceMax, pressureMax: datum.pressureMax,
size: datum.size, distanceMax: datum.distanceMax,
radiusMajor: radiusMajor, size: datum.size,
radiusMinor: radiusMinor, radiusMajor: radiusMajor,
radiusMin: radiusMin, radiusMinor: radiusMinor,
radiusMax: radiusMax, radiusMin: radiusMin,
orientation: datum.orientation, radiusMax: radiusMax,
tilt: datum.tilt, orientation: datum.orientation,
embedderId: datum.embedderId, tilt: datum.tilt,
); embedderId: datum.embedderId,
break; );
case ui.PointerChange.move: case ui.PointerChange.move:
yield PointerMoveEvent( return PointerMoveEvent(
timeStamp: timeStamp, timeStamp: timeStamp,
pointer: datum.pointerIdentifier, pointer: datum.pointerIdentifier,
kind: kind, kind: kind,
device: datum.device, device: datum.device,
position: position, position: position,
delta: delta, delta: delta,
buttons: _synthesiseDownButtons(datum.buttons, kind), buttons: _synthesiseDownButtons(datum.buttons, kind),
obscured: datum.obscured, obscured: datum.obscured,
pressure: datum.pressure, pressure: datum.pressure,
pressureMin: datum.pressureMin, pressureMin: datum.pressureMin,
pressureMax: datum.pressureMax, pressureMax: datum.pressureMax,
distanceMax: datum.distanceMax, distanceMax: datum.distanceMax,
size: datum.size, size: datum.size,
radiusMajor: radiusMajor, radiusMajor: radiusMajor,
radiusMinor: radiusMinor, radiusMinor: radiusMinor,
radiusMin: radiusMin, radiusMin: radiusMin,
radiusMax: radiusMax, radiusMax: radiusMax,
orientation: datum.orientation, orientation: datum.orientation,
tilt: datum.tilt, tilt: datum.tilt,
platformData: datum.platformData, platformData: datum.platformData,
synthesized: datum.synthesized, synthesized: datum.synthesized,
embedderId: datum.embedderId, embedderId: datum.embedderId,
); );
break; case ui.PointerChange.up:
case ui.PointerChange.up: return PointerUpEvent(
yield PointerUpEvent( timeStamp: timeStamp,
timeStamp: timeStamp, pointer: datum.pointerIdentifier,
pointer: datum.pointerIdentifier, kind: kind,
kind: kind, device: datum.device,
device: datum.device, position: position,
position: position, buttons: datum.buttons,
buttons: datum.buttons, obscured: datum.obscured,
obscured: datum.obscured, pressure: datum.pressure,
pressure: datum.pressure, pressureMin: datum.pressureMin,
pressureMin: datum.pressureMin, pressureMax: datum.pressureMax,
pressureMax: datum.pressureMax, distance: datum.distance,
distance: datum.distance, distanceMax: datum.distanceMax,
distanceMax: datum.distanceMax, size: datum.size,
size: datum.size, radiusMajor: radiusMajor,
radiusMajor: radiusMajor, radiusMinor: radiusMinor,
radiusMinor: radiusMinor, radiusMin: radiusMin,
radiusMin: radiusMin, radiusMax: radiusMax,
radiusMax: radiusMax, orientation: datum.orientation,
orientation: datum.orientation, tilt: datum.tilt,
tilt: datum.tilt, embedderId: datum.embedderId,
embedderId: datum.embedderId, );
); case ui.PointerChange.cancel:
break; return PointerCancelEvent(
case ui.PointerChange.cancel: timeStamp: timeStamp,
yield PointerCancelEvent( pointer: datum.pointerIdentifier,
timeStamp: timeStamp, kind: kind,
pointer: datum.pointerIdentifier, device: datum.device,
kind: kind, position: position,
device: datum.device, buttons: datum.buttons,
position: position, obscured: datum.obscured,
buttons: datum.buttons, pressureMin: datum.pressureMin,
obscured: datum.obscured, pressureMax: datum.pressureMax,
pressureMin: datum.pressureMin, distance: datum.distance,
pressureMax: datum.pressureMax, distanceMax: datum.distanceMax,
distance: datum.distance, size: datum.size,
distanceMax: datum.distanceMax, radiusMajor: radiusMajor,
size: datum.size, radiusMinor: radiusMinor,
radiusMajor: radiusMajor, radiusMin: radiusMin,
radiusMinor: radiusMinor, radiusMax: radiusMax,
radiusMin: radiusMin, orientation: datum.orientation,
radiusMax: radiusMax, tilt: datum.tilt,
orientation: datum.orientation, embedderId: datum.embedderId,
tilt: datum.tilt, );
embedderId: datum.embedderId, case ui.PointerChange.remove:
); return PointerRemovedEvent(
break; timeStamp: timeStamp,
case ui.PointerChange.remove: kind: kind,
yield PointerRemovedEvent( device: datum.device,
timeStamp: timeStamp, position: position,
kind: kind, obscured: datum.obscured,
device: datum.device, pressureMin: datum.pressureMin,
position: position, pressureMax: datum.pressureMax,
obscured: datum.obscured, distanceMax: datum.distanceMax,
pressureMin: datum.pressureMin, radiusMin: radiusMin,
pressureMax: datum.pressureMax, radiusMax: radiusMax,
distanceMax: datum.distanceMax, embedderId: datum.embedderId,
radiusMin: radiusMin, );
radiusMax: radiusMax, }
embedderId: datum.embedderId, case ui.PointerSignalKind.scroll:
); final Offset scrollDelta =
break; Offset(datum.scrollDeltaX, datum.scrollDeltaY) / devicePixelRatio;
} return PointerScrollEvent(
} else { timeStamp: timeStamp,
switch (datum.signalKind!) { kind: kind,
case ui.PointerSignalKind.scroll: device: datum.device,
final Offset scrollDelta = position: position,
Offset(datum.scrollDeltaX, datum.scrollDeltaY) / devicePixelRatio; scrollDelta: scrollDelta,
yield PointerScrollEvent( embedderId: datum.embedderId,
timeStamp: timeStamp, );
kind: kind, case ui.PointerSignalKind.unknown:
device: datum.device, // This branch should already have 'unknown' filtered out, but
position: position, // we don't want to return anything or miss if someone adds a new
scrollDelta: scrollDelta, // enumeration to PointerSignalKind.
embedderId: datum.embedderId, throw StateError('Unreachable');
); }
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 double _toLogicalPixels(double physicalPixels, double devicePixelRatio) => physicalPixels / devicePixelRatio; static double _toLogicalPixels(double physicalPixels, double devicePixelRatio) => physicalPixels / devicePixelRatio;

View File

@ -95,11 +95,11 @@ class PointerRouter {
} catch (exception, stack) { } catch (exception, stack) {
InformationCollector? collector; InformationCollector? collector;
assert(() { assert(() {
collector = () sync* { collector = () => <DiagnosticsNode>[
yield DiagnosticsProperty<PointerRouter>('router', this, level: DiagnosticLevel.debug); DiagnosticsProperty<PointerRouter>('router', this, level: DiagnosticLevel.debug),
yield DiagnosticsProperty<PointerRoute>('route', route, level: DiagnosticLevel.debug); DiagnosticsProperty<PointerRoute>('route', route, level: DiagnosticLevel.debug),
yield DiagnosticsProperty<PointerEvent>('event', event, level: DiagnosticLevel.debug); DiagnosticsProperty<PointerEvent>('event', event, level: DiagnosticLevel.debug),
}; ];
return true; return true;
}()); }());
FlutterError.reportError(FlutterErrorDetails( FlutterError.reportError(FlutterErrorDetails(

View File

@ -89,9 +89,9 @@ class PointerSignalResolver {
} catch (exception, stack) { } catch (exception, stack) {
InformationCollector? collector; InformationCollector? collector;
assert(() { assert(() {
collector = () sync* { collector = () => <DiagnosticsNode>[
yield DiagnosticsProperty<PointerSignalEvent>('Event', event, style: DiagnosticsTreeStyle.errorProperty); DiagnosticsProperty<PointerSignalEvent>('Event', event, style: DiagnosticsTreeStyle.errorProperty),
}; ];
return true; return true;
}()); }());
FlutterError.reportError(FlutterErrorDetails( FlutterError.reportError(FlutterErrorDetails(

View File

@ -199,10 +199,10 @@ abstract class GestureRecognizer extends GestureArenaMember with DiagnosticableT
} catch (exception, stack) { } catch (exception, stack) {
InformationCollector? collector; InformationCollector? collector;
assert(() { assert(() {
collector = () sync* { collector = () => <DiagnosticsNode>[
yield StringProperty('Handler', name); StringProperty('Handler', name),
yield DiagnosticsProperty<GestureRecognizer>('Recognizer', this, style: DiagnosticsTreeStyle.errorProperty); DiagnosticsProperty<GestureRecognizer>('Recognizer', this, style: DiagnosticsTreeStyle.errorProperty),
}; ];
return true; return true;
}()); }());
FlutterError.reportError(FlutterErrorDetails( FlutterError.reportError(FlutterErrorDetails(

View File

@ -797,11 +797,13 @@ class _MaterialAppState extends State<MaterialApp> {
// of a particular LocalizationsDelegate.type is loaded so the // of a particular LocalizationsDelegate.type is loaded so the
// localizationsDelegate parameter can be used to override // localizationsDelegate parameter can be used to override
// _MaterialLocalizationsDelegate. // _MaterialLocalizationsDelegate.
Iterable<LocalizationsDelegate<dynamic>> get _localizationsDelegates sync* { Iterable<LocalizationsDelegate<dynamic>> get _localizationsDelegates {
if (widget.localizationsDelegates != null) return <LocalizationsDelegate<dynamic>>[
yield* widget.localizationsDelegates!; if (widget.localizationsDelegates != null)
yield DefaultMaterialLocalizations.delegate; ...widget.localizationsDelegates!,
yield DefaultCupertinoLocalizations.delegate; DefaultMaterialLocalizations.delegate,
DefaultCupertinoLocalizations.delegate,
];
} }
Widget _inspectorSelectButtonBuilder(BuildContext context, VoidCallback onPressed) { Widget _inspectorSelectButtonBuilder(BuildContext context, VoidCallback onPressed) {

View File

@ -1076,9 +1076,9 @@ class ChoiceChip extends StatelessWidget
/// ]; /// ];
/// final List<String> _filters = <String>[]; /// final List<String> _filters = <String>[];
/// ///
/// Iterable<Widget> get actorWidgets sync* { /// Iterable<Widget> get actorWidgets {
/// for (final ActorFilterEntry actor in _cast) { /// return _cast.map((ActorFilterEntry actor) {
/// yield Padding( /// return Padding(
/// padding: const EdgeInsets.all(4.0), /// padding: const EdgeInsets.all(4.0),
/// child: FilterChip( /// child: FilterChip(
/// avatar: CircleAvatar(child: Text(actor.initials)), /// avatar: CircleAvatar(child: Text(actor.initials)),
@ -1097,7 +1097,7 @@ class ChoiceChip extends StatelessWidget
/// }, /// },
/// ), /// ),
/// ); /// );
/// } /// });
/// } /// }
/// ///
/// @override /// @override
@ -2257,16 +2257,15 @@ class _RenderChip extends RenderBox with SlottedContainerRenderObjectMixin<_Chip
// The returned list is ordered for hit testing. // The returned list is ordered for hit testing.
@override @override
Iterable<RenderBox> get children sync* { Iterable<RenderBox> get children {
if (avatar != null) { return <RenderBox>[
yield avatar!; if (avatar != null)
} avatar!,
if (label != null) { if (label != null)
yield label!; label!,
} if (deleteIcon != null)
if (deleteIcon != null) { deleteIcon!,
yield deleteIcon!; ];
}
} }
bool get isDrawingCheckmark => theme.showCheckmark && !checkmarkAnimation.isDismissed; bool get isDrawingCheckmark => theme.showCheckmark && !checkmarkAnimation.isDismissed;

View File

@ -723,29 +723,31 @@ class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin
// The returned list is ordered for hit testing. // The returned list is ordered for hit testing.
@override @override
Iterable<RenderBox> get children sync* { Iterable<RenderBox> get children {
if (icon != null) return <RenderBox>[
yield icon!; if (icon != null)
if (input != null) icon!,
yield input!; if (input != null)
if (prefixIcon != null) input!,
yield prefixIcon!; if (prefixIcon != null)
if (suffixIcon != null) prefixIcon!,
yield suffixIcon!; if (suffixIcon != null)
if (prefix != null) suffixIcon!,
yield prefix!; if (prefix != null)
if (suffix != null) prefix!,
yield suffix!; if (suffix != null)
if (label != null) suffix!,
yield label!; if (label != null)
if (hint != null) label!,
yield hint!; if (hint != null)
if (helperError != null) hint!,
yield helperError!; if (helperError != null)
if (counter != null) helperError!,
yield counter!; if (counter != null)
if (container != null) counter!,
yield container!; if (container != null)
container!,
];
} }
_Decoration get decoration => _decoration; _Decoration get decoration => _decoration;

View File

@ -1041,33 +1041,31 @@ class ListTile extends StatelessWidget {
/// See also: /// See also:
/// ///
/// * [Divider], which you can use to obtain this effect manually. /// * [Divider], which you can use to obtain this effect manually.
static Iterable<Widget> divideTiles({ BuildContext? context, required Iterable<Widget> tiles, Color? color }) sync* { static Iterable<Widget> divideTiles({ BuildContext? context, required Iterable<Widget> tiles, Color? color }) {
assert(tiles != null); assert(tiles != null);
assert(color != null || context != null); assert(color != null || context != null);
tiles = tiles.toList();
final Iterator<Widget> iterator = tiles.iterator; if (tiles.isEmpty || tiles.length == 1) {
final bool hasNext = iterator.moveNext(); return tiles;
}
if (!hasNext) Widget wrapTile(Widget tile) {
return; return DecoratedBox(
final Decoration decoration = BoxDecoration(
border: Border(
bottom: Divider.createBorderSide(context, color: color),
),
);
Widget tile = iterator.current;
while (iterator.moveNext()) {
yield DecoratedBox(
position: DecorationPosition.foreground, position: DecorationPosition.foreground,
decoration: decoration, decoration: BoxDecoration(
border: Border(
bottom: Divider.createBorderSide(context, color: color),
),
),
child: tile, child: tile,
); );
tile = iterator.current;
} }
if (hasNext)
yield tile; return <Widget>[
...tiles.take(tiles.length - 1).map(wrapTile),
tiles.last,
];
} }
Color? _iconColor(ThemeData theme, ListTileThemeData tileTheme) { Color? _iconColor(ThemeData theme, ListTileThemeData tileTheme) {
@ -1389,15 +1387,17 @@ class _RenderListTile extends RenderBox with SlottedContainerRenderObjectMixin<_
// The returned list is ordered for hit testing. // The returned list is ordered for hit testing.
@override @override
Iterable<RenderBox> get children sync* { Iterable<RenderBox> get children {
if (leading != null) return <RenderBox>[
yield leading!; if (leading != null)
if (title != null) leading!,
yield title!; if (title != null)
if (subtitle != null) title!,
yield subtitle!; if (subtitle != null)
if (trailing != null) subtitle!,
yield trailing!; if (trailing != null)
trailing!,
];
} }
bool get isDense => _isDense; bool get isDense => _isDense;

View File

@ -51,12 +51,10 @@ class NetworkImage extends image_provider.ImageProvider<image_provider.NetworkIm
chunkEvents: chunkEvents.stream, chunkEvents: chunkEvents.stream,
scale: key.scale, scale: key.scale,
debugLabel: key.url, debugLabel: key.url,
informationCollector: () { informationCollector: () => <DiagnosticsNode>[
return <DiagnosticsNode>[ DiagnosticsProperty<image_provider.ImageProvider>('Image provider', this),
DiagnosticsProperty<image_provider.ImageProvider>('Image provider', this), DiagnosticsProperty<image_provider.NetworkImage>('Image key', key),
DiagnosticsProperty<image_provider.NetworkImage>('Image key', key), ],
];
},
); );
} }

View File

@ -59,12 +59,10 @@ class NetworkImage
InformationCollector? _imageStreamInformationCollector(image_provider.NetworkImage key) { InformationCollector? _imageStreamInformationCollector(image_provider.NetworkImage key) {
InformationCollector? collector; InformationCollector? collector;
assert(() { assert(() {
collector = () { collector = () => <DiagnosticsNode>[
return <DiagnosticsNode>[ DiagnosticsProperty<image_provider.ImageProvider>('Image provider', this),
DiagnosticsProperty<image_provider.ImageProvider>('Image provider', this), DiagnosticsProperty<NetworkImage>('Image key', key as NetworkImage),
DiagnosticsProperty<NetworkImage>('Image key', key as NetworkImage), ];
];
};
return true; return true;
}()); }());
return collector; return collector;

View File

@ -657,7 +657,7 @@ void paintImage({
} }
} }
Iterable<Rect> _generateImageTileRects(Rect outputRect, Rect fundamentalRect, ImageRepeat repeat) sync* { Iterable<Rect> _generateImageTileRects(Rect outputRect, Rect fundamentalRect, ImageRepeat repeat) {
int startX = 0; int startX = 0;
int startY = 0; int startY = 0;
int stopX = 0; int stopX = 0;
@ -675,10 +675,11 @@ Iterable<Rect> _generateImageTileRects(Rect outputRect, Rect fundamentalRect, Im
stopY = ((outputRect.bottom - fundamentalRect.bottom) / strideY).ceil(); stopY = ((outputRect.bottom - fundamentalRect.bottom) / strideY).ceil();
} }
for (int i = startX; i <= stopX; ++i) { return <Rect>[
for (int j = startY; j <= stopY; ++j) for (int i = startX; i <= stopX; ++i)
yield fundamentalRect.shift(Offset(i * strideX, j * strideY)); 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); Rect _scaleRect(Rect rect, double scale) => Rect.fromLTRB(rect.left * scale, rect.top * scale, rect.right * scale, rect.bottom * scale);

View File

@ -336,11 +336,11 @@ abstract class ImageProvider<T extends Object> {
await null; // wait an event turn in case a listener has been added to the image stream. await null; // wait an event turn in case a listener has been added to the image stream.
InformationCollector? collector; InformationCollector? collector;
assert(() { assert(() {
collector = () sync* { collector = () => <DiagnosticsNode>[
yield DiagnosticsProperty<ImageProvider>('Image provider', this); DiagnosticsProperty<ImageProvider>('Image provider', this),
yield DiagnosticsProperty<ImageConfiguration>('Image configuration', configuration); DiagnosticsProperty<ImageConfiguration>('Image configuration', configuration),
yield DiagnosticsProperty<T>('Image key', key, defaultValue: null); DiagnosticsProperty<T>('Image key', key, defaultValue: null),
}; ];
return true; return true;
}()); }());
if (stream.completer == null) { if (stream.completer == null) {
@ -395,11 +395,11 @@ abstract class ImageProvider<T extends Object> {
} else { } else {
InformationCollector? collector; InformationCollector? collector;
assert(() { assert(() {
collector = () sync* { collector = () => <DiagnosticsNode>[
yield DiagnosticsProperty<ImageProvider>('Image provider', this); DiagnosticsProperty<ImageProvider>('Image provider', this),
yield DiagnosticsProperty<ImageConfiguration>('Image configuration', configuration); DiagnosticsProperty<ImageConfiguration>('Image configuration', configuration),
yield DiagnosticsProperty<T>('Image key', key, defaultValue: null); DiagnosticsProperty<T>('Image key', key, defaultValue: null),
}; ];
return true; return true;
}()); }());
FlutterError.reportError(FlutterErrorDetails( FlutterError.reportError(FlutterErrorDetails(
@ -648,10 +648,10 @@ abstract class AssetBundleImageProvider extends ImageProvider<AssetBundleImageKe
ImageStreamCompleter load(AssetBundleImageKey key, DecoderCallback decode) { ImageStreamCompleter load(AssetBundleImageKey key, DecoderCallback decode) {
InformationCollector? collector; InformationCollector? collector;
assert(() { assert(() {
collector = () sync* { collector = () => <DiagnosticsNode>[
yield DiagnosticsProperty<ImageProvider>('Image provider', this); DiagnosticsProperty<ImageProvider>('Image provider', this),
yield DiagnosticsProperty<AssetBundleImageKey>('Image key', key); DiagnosticsProperty<AssetBundleImageKey>('Image key', key),
}; ];
return true; return true;
}()); }());
return MultiFrameImageStreamCompleter( return MultiFrameImageStreamCompleter(
@ -878,9 +878,9 @@ class FileImage extends ImageProvider<FileImage> {
codec: _loadAsync(key, decode), codec: _loadAsync(key, decode),
scale: key.scale, scale: key.scale,
debugLabel: key.file.path, debugLabel: key.file.path,
informationCollector: () sync* { informationCollector: () => <DiagnosticsNode>[
yield ErrorDescription('Path: ${file.path}'); ErrorDescription('Path: ${file.path}'),
}, ],
); );
} }

View File

@ -241,16 +241,18 @@ mixin DebugOverflowIndicatorMixin on RenderObject {
exception: FlutterError('A $runtimeType overflowed by $overflowText.'), exception: FlutterError('A $runtimeType overflowed by $overflowText.'),
library: 'rendering library', library: 'rendering library',
context: ErrorDescription('during layout'), context: ErrorDescription('during layout'),
informationCollector: () sync* { informationCollector: () => <DiagnosticsNode>[
if (debugCreator != null) // debugCreator should only be set in DebugMode, but we want the
yield DiagnosticsDebugCreator(debugCreator!); // treeshaker to know that.
yield* overflowHints!; if (kDebugMode && debugCreator != null)
yield describeForError('The specific $runtimeType in question is'); DiagnosticsDebugCreator(debugCreator!),
...overflowHints!,
describeForError('The specific $runtimeType in question is'),
// TODO(jacobr): this line is ascii art that it would be nice to // TODO(jacobr): this line is ascii art that it would be nice to
// handle a little more generically in GUI debugging clients in the // handle a little more generically in GUI debugging clients in the
// future. // future.
yield DiagnosticsNode.message('◢◤' * (FlutterError.wrapWidth ~/ 2), allowWrap: false); DiagnosticsNode.message('◢◤' * (FlutterError.wrapWidth ~/ 2), allowWrap: false),
}, ],
), ),
); );
} }

View File

@ -67,9 +67,8 @@ class AnnotationResult<T> {
/// tree. /// tree.
/// ///
/// It is similar to [entries] but does not contain other information. /// It is similar to [entries] but does not contain other information.
Iterable<T> get annotations sync* { Iterable<T> get annotations {
for (final AnnotationEntry<T> entry in _entries) return _entries.map((AnnotationEntry<T> entry) => entry.annotation);
yield entry.annotation;
} }
} }

View File

@ -1391,16 +1391,18 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
stack: stack, stack: stack,
library: 'rendering library', library: 'rendering library',
context: ErrorDescription('during $method()'), context: ErrorDescription('during $method()'),
informationCollector: () sync* { informationCollector: () => <DiagnosticsNode>[
if (debugCreator != null) // debugCreator should always be null outside of debugMode, but we want
yield DiagnosticsDebugCreator(debugCreator!); // the tree shaker to notice this.
yield describeForError('The following RenderObject was being processed when the exception was fired'); 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 // TODO(jacobr): this error message has a code smell. Consider whether
// displaying the truncated children is really useful for command line // displaying the truncated children is really useful for command line
// users. Inspector users can see the full tree by clicking on the // users. Inspector users can see the full tree by clicking on the
// render object so this may not be that useful. // 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 != null);
assert(constraints.debugAssertIsValid( assert(constraints.debugAssertIsValid(
isAppliedConstraint: true, isAppliedConstraint: true,
informationCollector: () sync* { informationCollector: () {
final List<String> stack = StackTrace.current.toString().split('\n'); final List<String> stack = StackTrace.current.toString().split('\n');
int? targetFrame; int? targetFrame;
final Pattern layoutFramePattern = RegExp(r'^#[0-9]+ +RenderObject.layout \('); 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 Match? targetFrameMatch = targetFramePattern.matchAsPrefix(stack[targetFrame]);
final String? problemFunction = (targetFrameMatch != null && targetFrameMatch.groupCount > 0) ? targetFrameMatch.group(1) : stack[targetFrame].trim(); 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. // TODO(jacobr): this case is similar to displaying a single stack frame.
yield ErrorDescription( return <DiagnosticsNode>[
"These invalid constraints were provided to $runtimeType's layout() " ErrorDescription(
'function by the following function, which probably computed the ' "These invalid constraints were provided to $runtimeType's layout() "
'invalid constraints in question:\n' 'function by the following function, which probably computed the '
' $problemFunction', 'invalid constraints in question:\n'
); ' $problemFunction',
),
];
} }
return <DiagnosticsNode>[];
}, },
)); ));
assert(!_debugDoingThisResize); assert(!_debugDoingThisResize);

View File

@ -1203,9 +1203,9 @@ abstract class RenderSliver extends RenderObject {
@override @override
void debugAssertDoesMeetConstraints() { void debugAssertDoesMeetConstraints() {
assert(geometry!.debugAssertIsValid( assert(geometry!.debugAssertIsValid(
informationCollector: () sync* { informationCollector: () => <DiagnosticsNode>[
yield describeForError('The RenderSliver that returned the offending geometry was'); describeForError('The RenderSliver that returned the offending geometry was'),
}, ],
)); ));
assert(() { assert(() {
if (geometry!.paintOrigin + geometry!.paintExtent > constraints.remainingPaintExtent) { if (geometry!.paintOrigin + geometry!.paintExtent > constraints.remainingPaintExtent) {

View File

@ -795,6 +795,7 @@ class RenderTable extends RenderBox {
/// column, in row order, starting from the first row. /// column, in row order, starting from the first row.
/// ///
/// This is a lazily-evaluated iterable. /// This is a lazily-evaluated iterable.
// flutter_ignore: no_sync_async_star
Iterable<RenderBox> column(int x) sync* { Iterable<RenderBox> column(int x) sync* {
for (int y = 0; y < rows; y += 1) { for (int y = 0; y < rows; y += 1) {
final int xy = x + y * columns; final int xy = x + y * columns;
@ -808,6 +809,7 @@ class RenderTable extends RenderBox {
/// row, in column order, starting with the first column. /// row, in column order, starting with the first column.
/// ///
/// This is a lazily-evaluated iterable. /// This is a lazily-evaluated iterable.
// flutter_ignore: no_sync_async_star
Iterable<RenderBox> row(int y) sync* { Iterable<RenderBox> row(int y) sync* {
final int start = y * columns; final int start = y * columns;
final int end = (y + 1) * columns; final int end = (y + 1) * columns;

View File

@ -1718,37 +1718,40 @@ class RenderViewport extends RenderViewportBase<SliverPhysicalContainerParentDat
} }
@override @override
Iterable<RenderSliver> get childrenInPaintOrder sync* { Iterable<RenderSliver> get childrenInPaintOrder {
final List<RenderSliver> children = <RenderSliver>[];
if (firstChild == null) if (firstChild == null)
return; return children;
RenderSliver? child = firstChild; RenderSliver? child = firstChild;
while (child != center) { while (child != center) {
yield child!; children.add(child!);
child = childAfter(child); child = childAfter(child);
} }
child = lastChild; child = lastChild;
while (true) { while (true) {
yield child!; children.add(child!);
if (child == center) if (child == center)
return; return children;
child = childBefore(child); child = childBefore(child);
} }
} }
@override @override
Iterable<RenderSliver> get childrenInHitTestOrder sync* { Iterable<RenderSliver> get childrenInHitTestOrder {
final List<RenderSliver> children = <RenderSliver>[];
if (firstChild == null) if (firstChild == null)
return; return children;
RenderSliver? child = center; RenderSliver? child = center;
while (child != null) { while (child != null) {
yield child; children.add(child);
child = childAfter(child); child = childAfter(child);
} }
child = childBefore(center!); child = childBefore(center!);
while (child != null) { while (child != null) {
yield child; children.add(child);
child = childBefore(child); child = childBefore(child);
} }
return children;
} }
@override @override
@ -2030,20 +2033,24 @@ class RenderShrinkWrappingViewport extends RenderViewportBase<SliverLogicalConta
String labelForChild(int index) => 'child $index'; String labelForChild(int index) => 'child $index';
@override @override
Iterable<RenderSliver> get childrenInPaintOrder sync* { Iterable<RenderSliver> get childrenInPaintOrder {
final List<RenderSliver> children = <RenderSliver>[];
RenderSliver? child = lastChild; RenderSliver? child = lastChild;
while (child != null) { while (child != null) {
yield child; children.add(child);
child = childBefore(child); child = childBefore(child);
} }
return children;
} }
@override @override
Iterable<RenderSliver> get childrenInHitTestOrder sync* { Iterable<RenderSliver> get childrenInHitTestOrder {
final List<RenderSliver> children = <RenderSliver>[];
RenderSliver? child = firstChild; RenderSliver? child = firstChild;
while (child != null) { while (child != null) {
yield child; children.add(child);
child = childAfter(child); child = childAfter(child);
} }
return children;
} }
} }

View File

@ -288,13 +288,13 @@ mixin SchedulerBinding on BindingBase {
} catch (exception, stack) { } catch (exception, stack) {
InformationCollector? collector; InformationCollector? collector;
assert(() { assert(() {
collector = () sync* { collector = () => <DiagnosticsNode>[
yield DiagnosticsProperty<TimingsCallback>( DiagnosticsProperty<TimingsCallback>(
'The TimingsCallback that gets executed was', 'The TimingsCallback that gets executed was',
callback, callback,
style: DiagnosticsTreeStyle.errorProperty, style: DiagnosticsTreeStyle.errorProperty,
); ),
}; ];
return true; return true;
}()); }());
FlutterError.reportError(FlutterErrorDetails( FlutterError.reportError(FlutterErrorDetails(
@ -467,13 +467,15 @@ mixin SchedulerBinding on BindingBase {
stack: exceptionStack, stack: exceptionStack,
library: 'scheduler library', library: 'scheduler library',
context: ErrorDescription('during a task callback'), context: ErrorDescription('during a task callback'),
informationCollector: (callbackStack == null) ? null : () sync* { informationCollector: (callbackStack == null) ? null : () {
yield DiagnosticsStackTrace( return <DiagnosticsNode>[
'\nThis exception was thrown in the context of a scheduler callback. ' DiagnosticsStackTrace(
'When the scheduler callback was _registered_ (as opposed to when the ' '\nThis exception was thrown in the context of a scheduler callback. '
'exception was thrown), this was the stack', 'When the scheduler callback was _registered_ (as opposed to when the '
callbackStack, 'exception was thrown), this was the stack',
); callbackStack,
),
];
}, },
)); ));
} }
@ -566,24 +568,21 @@ mixin SchedulerBinding on BindingBase {
FlutterError.reportError(FlutterErrorDetails( FlutterError.reportError(FlutterErrorDetails(
exception: reason, exception: reason,
library: 'scheduler library', library: 'scheduler library',
informationCollector: () sync* { informationCollector: () => <DiagnosticsNode>[
if (count == 1) { if (count == 1)
// TODO(jacobr): I have added an extra line break in this case. // TODO(jacobr): I have added an extra line break in this case.
yield ErrorDescription( ErrorDescription(
'There was one transient callback left. ' 'There was one transient callback left. '
'The stack trace for when it was registered is as follows:', 'The stack trace for when it was registered is as follows:',
); )
} else { else
yield ErrorDescription( ErrorDescription(
'There were $count transient callbacks left. ' 'There were $count transient callbacks left. '
'The stack traces for when they were registered are as follows:', 'The stack traces for when they were registered are as follows:',
); ),
} for (final int id in callbacks.keys)
for (final int id in callbacks.keys) { DiagnosticsStackTrace('── callback $id ──', callbacks[id]!.debugStack, showSeparator: false),
final _FrameCallbackEntry entry = callbacks[id]!; ],
yield DiagnosticsStackTrace('── callback $id ──', entry.debugStack, showSeparator: false);
}
},
)); ));
} }
return true; return true;
@ -1149,13 +1148,15 @@ mixin SchedulerBinding on BindingBase {
stack: exceptionStack, stack: exceptionStack,
library: 'scheduler library', library: 'scheduler library',
context: ErrorDescription('during a scheduler callback'), context: ErrorDescription('during a scheduler callback'),
informationCollector: (callbackStack == null) ? null : () sync* { informationCollector: (callbackStack == null) ? null : () {
yield DiagnosticsStackTrace( return <DiagnosticsNode>[
'\nThis exception was thrown in the context of a scheduler callback. ' DiagnosticsStackTrace(
'When the scheduler callback was _registered_ (as opposed to when the ' '\nThis exception was thrown in the context of a scheduler callback. '
'exception was thrown), this was the stack', 'When the scheduler callback was _registered_ (as opposed to when the '
callbackStack, 'exception was thrown), this was the stack',
); callbackStack,
),
];
}, },
)); ));
} }

View File

@ -144,6 +144,9 @@ mixin ServicesBinding on BindingBase, SchedulerBinding {
LicenseRegistry.addLicense(_addLicenses); LicenseRegistry.addLicense(_addLicenses);
} }
// TODO(dnfield): Refactor the license logic.
// https://github.com/flutter/flutter/issues/95043
// flutter_ignore: no_sync_async_star
Stream<LicenseEntry> _addLicenses() async* { Stream<LicenseEntry> _addLicenses() async* {
// Using _something_ here to break // Using _something_ here to break
// this into two parts is important because isolates take a while to copy // this into two parts is important because isolates take a while to copy

View File

@ -516,9 +516,9 @@ class HardwareKeyboard {
} catch (exception, stack) { } catch (exception, stack) {
InformationCollector? collector; InformationCollector? collector;
assert(() { assert(() {
collector = () sync* { collector = () => <DiagnosticsNode>[
yield DiagnosticsProperty<KeyEvent>('Event', event); DiagnosticsProperty<KeyEvent>('Event', event),
}; ];
return true; return true;
}()); }());
FlutterError.reportError(FlutterErrorDetails( FlutterError.reportError(FlutterErrorDetails(
@ -833,9 +833,9 @@ class KeyEventManager {
} catch (exception, stack) { } catch (exception, stack) {
InformationCollector? collector; InformationCollector? collector;
assert(() { assert(() {
collector = () sync* { collector = () => <DiagnosticsNode>[
yield DiagnosticsProperty<KeyMessage>('KeyMessage', message); DiagnosticsProperty<KeyMessage>('KeyMessage', message),
}; ];
return true; return true;
}()); }());
FlutterError.reportError(FlutterErrorDetails( FlutterError.reportError(FlutterErrorDetails(

View File

@ -680,9 +680,9 @@ class RawKeyboard {
} catch (exception, stack) { } catch (exception, stack) {
InformationCollector? collector; InformationCollector? collector;
assert(() { assert(() {
collector = () sync* { collector = () => <DiagnosticsNode>[
yield DiagnosticsProperty<RawKeyEvent>('Event', event); DiagnosticsProperty<RawKeyEvent>('Event', event),
}; ];
return true; return true;
}()); }());
FlutterError.reportError(FlutterErrorDetails( FlutterError.reportError(FlutterErrorDetails(

View File

@ -345,13 +345,13 @@ abstract class Action<T extends Intent> with Diagnosticable {
for (final ActionListenerCallback listener in localListeners) { for (final ActionListenerCallback listener in localListeners) {
InformationCollector? collector; InformationCollector? collector;
assert(() { assert(() {
collector = () sync* { collector = () => <DiagnosticsNode>[
yield DiagnosticsProperty<Action<T>>( DiagnosticsProperty<Action<T>>(
'The $runtimeType sending notification was', 'The $runtimeType sending notification was',
this, this,
style: DiagnosticsTreeStyle.errorProperty, style: DiagnosticsTreeStyle.errorProperty,
); ),
}; ];
return true; return true;
}()); }());
try { try {

View File

@ -1472,10 +1472,12 @@ class _WidgetsAppState extends State<WidgetsApp> with WidgetsBindingObserver {
// of a particular LocalizationsDelegate.type is loaded so the // of a particular LocalizationsDelegate.type is loaded so the
// localizationsDelegate parameter can be used to override // localizationsDelegate parameter can be used to override
// WidgetsLocalizations.delegate. // WidgetsLocalizations.delegate.
Iterable<LocalizationsDelegate<dynamic>> get _localizationsDelegates sync* { Iterable<LocalizationsDelegate<dynamic>> get _localizationsDelegates {
if (widget.localizationsDelegates != null) return <LocalizationsDelegate<dynamic>>[
yield* widget.localizationsDelegates!; if (widget.localizationsDelegates != null)
yield DefaultWidgetsLocalizations.delegate; ...widget.localizationsDelegates!,
DefaultWidgetsLocalizations.delegate,
];
} }
// BUILDER // BUILDER
@ -1496,33 +1498,33 @@ class _WidgetsAppState extends State<WidgetsApp> with WidgetsBindingObserver {
FlutterError.reportError(FlutterErrorDetails( FlutterError.reportError(FlutterErrorDetails(
exception: "Warning: This application's locale, $appLocale, is not supported by all of its localization delegates.", exception: "Warning: This application's locale, $appLocale, is not supported by all of its localization delegates.",
library: 'widgets', library: 'widgets',
informationCollector: () sync* { informationCollector: () => <DiagnosticsNode>[
for (final Type unsupportedType in unsupportedTypes) { for (final Type unsupportedType in unsupportedTypes)
yield ErrorDescription( ErrorDescription(
'• A $unsupportedType delegate that supports the $appLocale locale was not found.', '• A $unsupportedType delegate that supports the $appLocale locale was not found.',
); ),
} ErrorSpacer(),
yield ErrorSpacer(); if (unsupportedTypes.length == 1 && unsupportedTypes.single.toString() == 'CupertinoLocalizations')
if (unsupportedTypes.length == 1 && unsupportedTypes.single.toString() == 'CupertinoLocalizations') {
// We previously explicitly avoided checking for this class so it's not uncommon for applications // We previously explicitly avoided checking for this class so it's not uncommon for applications
// to have omitted importing the required delegate. // to have omitted importing the required delegate.
yield ErrorHint( ...<DiagnosticsNode>[
'If the application is built using GlobalMaterialLocalizations.delegate, consider using ' ErrorHint(
'GlobalMaterialLocalizations.delegates (plural) instead, as that will automatically declare ' 'If the application is built using GlobalMaterialLocalizations.delegate, consider using '
'the appropriate Cupertino localizations.' 'GlobalMaterialLocalizations.delegates (plural) instead, as that will automatically declare '
); 'the appropriate Cupertino localizations.'
yield ErrorSpacer(); ),
} ErrorSpacer(),
yield ErrorHint( ],
ErrorHint(
'The declared supported locales for this app are: ${widget.supportedLocales.join(", ")}' 'The declared supported locales for this app are: ${widget.supportedLocales.join(", ")}'
); ),
yield ErrorSpacer(); ErrorSpacer(),
yield ErrorDescription( ErrorDescription(
'See https://flutter.dev/tutorials/internationalization/ for more ' 'See https://flutter.dev/tutorials/internationalization/ for more '
"information about configuring an app's locale, supportedLocales, " "information about configuring an app's locale, supportedLocales, "
'and localizationsDelegates parameters.', 'and localizationsDelegates parameters.',
); ),
}, ],
)); ));
return true; return true;
}()); }());

View File

@ -905,17 +905,19 @@ class _DragAvatar<T extends Object> extends Drag {
_activeTarget = newTarget; _activeTarget = newTarget;
} }
Iterable<_DragTargetState<Object>> _getDragTargets(Iterable<HitTestEntry> path) sync* { Iterable<_DragTargetState<Object>> _getDragTargets(Iterable<HitTestEntry> path) {
// Look for the RenderBoxes that corresponds to the hit target (the hit target // Look for the RenderBoxes that corresponds to the hit target (the hit target
// widgets build RenderMetaData boxes for us for this purpose). // widgets build RenderMetaData boxes for us for this purpose).
final List<_DragTargetState<Object>> targets = <_DragTargetState<Object>>[];
for (final HitTestEntry entry in path) { for (final HitTestEntry entry in path) {
final HitTestTarget target = entry.target; final HitTestTarget target = entry.target;
if (target is RenderMetaData) { if (target is RenderMetaData) {
final dynamic metaData = target.metaData; final dynamic metaData = target.metaData;
if (metaData is _DragTargetState && metaData.isExpectedDataType(data, T)) if (metaData is _DragTargetState && metaData.isExpectedDataType(data, T))
yield metaData; targets.add(metaData);
} }
} }
return targets;
} }
void _leaveAllEntered() { void _leaveAllEntered() {

View File

@ -1520,13 +1520,13 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
} catch (exception, stack) { } catch (exception, stack) {
InformationCollector? collector; InformationCollector? collector;
assert(() { assert(() {
collector = () sync* { collector = () => <DiagnosticsNode>[
yield DiagnosticsProperty<FocusManager>( DiagnosticsProperty<FocusManager>(
'The $runtimeType sending notification was', 'The $runtimeType sending notification was',
this, this,
style: DiagnosticsTreeStyle.errorProperty, style: DiagnosticsTreeStyle.errorProperty,
); ),
}; ];
return true; return true;
}()); }());
FlutterError.reportError(FlutterErrorDetails( FlutterError.reportError(FlutterErrorDetails(

View File

@ -1455,35 +1455,34 @@ abstract class ParentDataWidget<T extends ParentData> extends ProxyWidget {
required ParentData? parentData, required ParentData? parentData,
RenderObjectWidget? parentDataCreator, RenderObjectWidget? parentDataCreator,
DiagnosticsNode? ownershipChain, DiagnosticsNode? ownershipChain,
}) sync* { }) {
assert(T != dynamic); assert(T != dynamic);
assert(T != ParentData); assert(T != ParentData);
assert(debugTypicalAncestorWidgetClass != null); assert(debugTypicalAncestorWidgetClass != null);
final String description = 'The ParentDataWidget $this wants to apply ParentData of type $T to a RenderObject'; final String description = 'The ParentDataWidget $this wants to apply ParentData of type $T to a RenderObject';
if (parentData == null) { return <DiagnosticsNode>[
yield ErrorDescription( if (parentData == null)
'$description, which has not been set up to receive any ParentData.', ErrorDescription(
); '$description, which has not been set up to receive any ParentData.',
} else { )
yield ErrorDescription( else
'$description, which has been set up to accept ParentData of incompatible type ${parentData.runtimeType}.', ErrorDescription(
); '$description, which has been set up to accept ParentData of incompatible type ${parentData.runtimeType}.',
} ),
yield ErrorHint( ErrorHint(
'Usually, this means that the $runtimeType widget has the wrong ancestor RenderObjectWidget. ' 'Usually, this means that the $runtimeType widget has the wrong ancestor RenderObjectWidget. '
'Typically, $runtimeType widgets are placed directly inside $debugTypicalAncestorWidgetClass widgets.', 'Typically, $runtimeType widgets are placed directly inside $debugTypicalAncestorWidgetClass widgets.',
); ),
if (parentDataCreator != null) { if (parentDataCreator != null)
yield ErrorHint( ErrorHint(
'The offending $runtimeType is currently placed inside a ${parentDataCreator.runtimeType} widget.', 'The offending $runtimeType is currently placed inside a ${parentDataCreator.runtimeType} widget.',
); ),
} if (ownershipChain != null)
if (ownershipChain != null) { ErrorDescription(
yield ErrorDescription( 'The ownership chain for the RenderObject that received the incompatible parent data was:\n $ownershipChain',
'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. /// 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'), ErrorDescription('while rebuilding dirty elements'),
e, e,
stack, stack,
informationCollector: () sync* { informationCollector: () => <DiagnosticsNode>[
if (index < _dirtyElements.length) { if (kDebugMode && index < _dirtyElements.length)
yield DiagnosticsDebugCreator(DebugCreator(element)); DiagnosticsDebugCreator(DebugCreator(element)),
yield element.describeElement('The element being rebuilt at the time was index $index of $dirtyCount'); if (index < _dirtyElements.length)
} else { element.describeElement('The element being rebuilt at the time was index $index of $dirtyCount')
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.'); 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) if (!kReleaseMode && debugProfileBuildsEnabled)
@ -4721,9 +4720,10 @@ abstract class ComponentElement extends Element {
ErrorDescription('building $this'), ErrorDescription('building $this'),
e, e,
stack, stack,
informationCollector: () sync* { informationCollector: () => <DiagnosticsNode>[
yield DiagnosticsDebugCreator(DebugCreator(this)); if (kDebugMode)
}, DiagnosticsDebugCreator(DebugCreator(this)),
],
), ),
); );
} finally { } finally {
@ -4741,9 +4741,10 @@ abstract class ComponentElement extends Element {
ErrorDescription('building $this'), ErrorDescription('building $this'),
e, e,
stack, stack,
informationCollector: () sync* { informationCollector: () => <DiagnosticsNode>[
yield DiagnosticsDebugCreator(DebugCreator(this)); if (kDebugMode)
}, DiagnosticsDebugCreator(DebugCreator(this)),
],
), ),
); );
_child = updateChild(null, built, slot); _child = updateChild(null, built, slot);

View File

@ -127,9 +127,10 @@ class _LayoutBuilderElement<ConstraintType extends Constraints> extends RenderOb
ErrorDescription('building $widget'), ErrorDescription('building $widget'),
e, e,
stack, stack,
informationCollector: () sync* { informationCollector: () => <DiagnosticsNode>[
yield DiagnosticsDebugCreator(DebugCreator(this)); if (kDebugMode)
}, DiagnosticsDebugCreator(DebugCreator(this)),
],
), ),
); );
} }
@ -142,9 +143,10 @@ class _LayoutBuilderElement<ConstraintType extends Constraints> extends RenderOb
ErrorDescription('building $widget'), ErrorDescription('building $widget'),
e, e,
stack, stack,
informationCollector: () sync* { informationCollector: () => <DiagnosticsNode>[
yield DiagnosticsDebugCreator(DebugCreator(this)); if (kDebugMode)
}, DiagnosticsDebugCreator(DebugCreator(this)),
],
), ),
); );
_child = updateChild(null, built, slot); _child = updateChild(null, built, slot);

View File

@ -3515,9 +3515,11 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin, Res
/// The overlay this navigator uses for its visual presentation. /// The overlay this navigator uses for its visual presentation.
OverlayState? get overlay => _overlayKey.currentState; OverlayState? get overlay => _overlayKey.currentState;
Iterable<OverlayEntry> get _allRouteOverlayEntries sync* { Iterable<OverlayEntry> get _allRouteOverlayEntries {
for (final _RouteEntry entry in _history) return <OverlayEntry>[
yield* entry.route.overlayEntries; for (final _RouteEntry entry in _history)
...entry.route.overlayEntries,
];
} }
String? _lastAnnouncedRouteName; String? _lastAnnouncedRouteName;

View File

@ -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. // TODO(vegorov): use instance method version of castFrom when it is available.
yield* Iterable.castFrom<ScrollPosition, _NestedScrollPosition>(positions); return Iterable.castFrom<ScrollPosition, _NestedScrollPosition>(positions);
} }
} }

View File

@ -809,13 +809,13 @@ class _CallbackHookProvider<T> {
stack: stack, stack: stack,
library: 'widget library', library: 'widget library',
context: ErrorDescription('while invoking the callback for $runtimeType'), context: ErrorDescription('while invoking the callback for $runtimeType'),
informationCollector: () sync* { informationCollector: () => <DiagnosticsNode>[
yield DiagnosticsProperty<_CallbackHookProvider<T>>( DiagnosticsProperty<_CallbackHookProvider<T>>(
'The $runtimeType that invoked the callback was', 'The $runtimeType that invoked the callback was',
this, this,
style: DiagnosticsTreeStyle.errorProperty, style: DiagnosticsTreeStyle.errorProperty,
); ),
}, ],
)); ));
return defaultValue; return defaultValue;
} }

View File

@ -1634,9 +1634,11 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
late OverlayEntry _modalScope; late OverlayEntry _modalScope;
@override @override
Iterable<OverlayEntry> createOverlayEntries() sync* { Iterable<OverlayEntry> createOverlayEntries() {
yield _modalBarrier = OverlayEntry(builder: _buildModalBarrier); return <OverlayEntry>[
yield _modalScope = OverlayEntry(builder: _buildModalScope, maintainState: maintainState); _modalBarrier = OverlayEntry(builder: _buildModalBarrier),
_modalScope = OverlayEntry(builder: _buildModalScope, maintainState: maintainState),
];
} }
@override @override

View File

@ -139,13 +139,13 @@ class ScrollNotificationObserverState extends State<ScrollNotificationObserver>
stack: stack, stack: stack,
library: 'widget library', library: 'widget library',
context: ErrorDescription('while dispatching notifications for $runtimeType'), context: ErrorDescription('while dispatching notifications for $runtimeType'),
informationCollector: () sync* { informationCollector: () => <DiagnosticsNode>[
yield DiagnosticsProperty<ScrollNotificationObserverState>( DiagnosticsProperty<ScrollNotificationObserverState>(
'The $runtimeType sending notification was', 'The $runtimeType sending notification was',
this, this,
style: DiagnosticsTreeStyle.errorProperty, style: DiagnosticsTreeStyle.errorProperty,
); ),
}, ],
)); ));
} }
} }

View File

@ -483,8 +483,8 @@ class SingleActivator with Diagnosticable implements ShortcutActivator {
final bool meta; final bool meta;
@override @override
Iterable<LogicalKeyboardKey> get triggers sync* { Iterable<LogicalKeyboardKey> get triggers {
yield trigger; return <LogicalKeyboardKey>[trigger];
} }
@override @override

View File

@ -2916,9 +2916,9 @@ bool _isDebugCreator(DiagnosticsNode node) => node is DiagnosticsDebugCreator;
/// in [WidgetsBinding.initInstances]. /// in [WidgetsBinding.initInstances].
/// ///
/// This is meant to be called only in debug mode. In other modes, it yields an empty list. /// This is meant to be called only in debug mode. In other modes, it yields an empty list.
Iterable<DiagnosticsNode> debugTransformDebugCreator(Iterable<DiagnosticsNode> properties) sync* { Iterable<DiagnosticsNode> debugTransformDebugCreator(Iterable<DiagnosticsNode> properties) {
if (!kDebugMode) { if (!kDebugMode) {
return; return <DiagnosticsNode>[];
} }
final List<DiagnosticsNode> pending = <DiagnosticsNode>[]; final List<DiagnosticsNode> pending = <DiagnosticsNode>[];
ErrorSummary? errorSummary; ErrorSummary? errorSummary;
@ -2929,20 +2929,22 @@ Iterable<DiagnosticsNode> debugTransformDebugCreator(Iterable<DiagnosticsNode> p
} }
} }
bool foundStackTrace = false; bool foundStackTrace = false;
final List<DiagnosticsNode> result = <DiagnosticsNode>[];
for (final DiagnosticsNode node in properties) { for (final DiagnosticsNode node in properties) {
if (!foundStackTrace && node is DiagnosticsStackTrace) if (!foundStackTrace && node is DiagnosticsStackTrace)
foundStackTrace = true; foundStackTrace = true;
if (_isDebugCreator(node)) { if (_isDebugCreator(node)) {
yield* _parseDiagnosticsNode(node, errorSummary); result.addAll(_parseDiagnosticsNode(node, errorSummary));
} else { } else {
if (foundStackTrace) { if (foundStackTrace) {
pending.add(node); pending.add(node);
} else { } else {
yield node; result.add(node);
} }
} }
} }
yield* pending; result.addAll(pending);
return result;
} }
/// Transform the input [DiagnosticsNode]. /// Transform the input [DiagnosticsNode].
@ -2951,23 +2953,24 @@ Iterable<DiagnosticsNode> debugTransformDebugCreator(Iterable<DiagnosticsNode> p
Iterable<DiagnosticsNode> _parseDiagnosticsNode( Iterable<DiagnosticsNode> _parseDiagnosticsNode(
DiagnosticsNode node, DiagnosticsNode node,
ErrorSummary? errorSummary, ErrorSummary? errorSummary,
) sync* { ) {
assert(_isDebugCreator(node)); assert(_isDebugCreator(node));
try { try {
final DebugCreator debugCreator = node.value! as DebugCreator; final DebugCreator debugCreator = node.value! as DebugCreator;
final Element element = debugCreator.element; final Element element = debugCreator.element;
yield* _describeRelevantUserCode(element, errorSummary); return _describeRelevantUserCode(element, errorSummary);
} catch (error, stack) { } catch (error, stack) {
scheduleMicrotask(() { scheduleMicrotask(() {
FlutterError.reportError(FlutterErrorDetails( FlutterError.reportError(FlutterErrorDetails(
exception: error, exception: error,
stack: stack, stack: stack,
library: 'widget inspector', library: 'widget inspector',
informationCollector: () sync* { informationCollector: () => <DiagnosticsNode>[
yield DiagnosticsNode.message('This exception was caught while trying to describe the user-relevant code of another error.'); DiagnosticsNode.message('This exception was caught while trying to describe the user-relevant code of another error.'),
} ],
)); ));
}); });
return <DiagnosticsNode>[];
} }
} }

View File

@ -379,6 +379,11 @@ void main() {
expect(output, isEmpty); expect(output, isEmpty);
}); });
testWidgets('ListTile.divideTiles with single item list', (WidgetTester tester) async {
final Iterable<Widget> output = ListTile.divideTiles(tiles: const <Widget>[SizedBox()], color: Colors.grey);
expect(output.single, isA<SizedBox>());
});
testWidgets('ListTile.divideTiles only runs the generator once', (WidgetTester tester) async { testWidgets('ListTile.divideTiles only runs the generator once', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/pull/78879 // Regression test for https://github.com/flutter/flutter/pull/78879
int callCount = 0; int callCount = 0;

View File

@ -740,26 +740,22 @@ class PubspecYaml {
} }
/// This returns all the explicit dependencies that this pubspec.yaml lists under dependencies. /// This returns all the explicit dependencies that this pubspec.yaml lists under dependencies.
Iterable<PubspecDependency> get dependencies sync* { Iterable<PubspecDependency> get dependencies {
// It works by iterating over the parsed data from _parse above, collecting // 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 // 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 // overridden by subsequent entries in the same file and any that have the
// magic comment flagging them as auto-generated transitive dependencies // magic comment flagging them as auto-generated transitive dependencies
// that we added in a previous run. // that we added in a previous run.
for (final PubspecLine data in inputData) { return inputData
if (data is PubspecDependency && data.kind != DependencyKind.overridden && !data.isTransitive && !data.isDevDependency) { .whereType<PubspecDependency>()
yield data; .where((PubspecDependency data) => data.kind != DependencyKind.overridden && !data.isTransitive && !data.isDevDependency);
}
}
} }
/// This returns all regular dependencies and all dev dependencies. /// This returns all regular dependencies and all dev dependencies.
Iterable<PubspecDependency> get allDependencies sync* { Iterable<PubspecDependency> get allDependencies {
for (final PubspecLine data in inputData) { return inputData
if (data is PubspecDependency && data.kind != DependencyKind.overridden && !data.isTransitive) { .whereType<PubspecDependency>()
yield data; .where((PubspecDependency data) => data.kind != DependencyKind.overridden && !data.isTransitive);
}
}
} }
/// Take a dependency graph with explicit version numbers, and apply them to /// Take a dependency graph with explicit version numbers, and apply them to
@ -1412,23 +1408,26 @@ class PubDependencyTree {
String package, { String package, {
@required Set<String> seen, @required Set<String> seen,
@required Set<String> exclude, @required Set<String> exclude,
}) sync* { List<String>/*?*/ result,
}) {
assert(seen != null); assert(seen != null);
assert(exclude != null); assert(exclude != null);
result ??= <String>[];
if (!_dependencyTree.containsKey(package)) { if (!_dependencyTree.containsKey(package)) {
// We have no transitive dependencies extracted for flutter_sdk packages // We have no transitive dependencies extracted for flutter_sdk packages
// because they were omitted from pubspec.yaml used for 'pub upgrade' run. // because they were omitted from pubspec.yaml used for 'pub upgrade' run.
return; return result;
} }
for (final String dependency in _dependencyTree[package]) { for (final String dependency in _dependencyTree[package]) {
if (!seen.contains(dependency)) { if (!seen.contains(dependency)) {
if (!exclude.contains(dependency)) { if (!exclude.contains(dependency)) {
yield dependency; result.add(dependency);
} }
seen.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. /// The version that a particular package ended up with.

View File

@ -635,9 +635,9 @@ abstract class Device {
@override @override
String toString() => name; String toString() => name;
static Stream<String> descriptions(List<Device> devices) async* { static Future<List<String>> descriptions(List<Device> devices) async {
if (devices.isEmpty) { if (devices.isEmpty) {
return; return const <String>[];
} }
// Extract device information // Extract device information
@ -665,13 +665,14 @@ abstract class Device {
} }
// Join columns into lines of text // Join columns into lines of text
for (final List<String> row in table) { return <String>[
yield indices.map<String>((int i) => row[i].padRight(widths[i])).followedBy(<String>[row.last]).join(''); for (final List<String> row in table)
} indices.map<String>((int i) => row[i].padRight(widths[i])).followedBy(<String>[row.last]).join(''),
];
} }
static Future<void> printDevices(List<Device> devices, Logger logger) async { static Future<void> printDevices(List<Device> devices, Logger logger) async {
await descriptions(devices).forEach(logger.printStatus); (await descriptions(devices)).forEach(logger.printStatus);
} }
static List<String> devicesPlatformTypes(List<Device> devices) { static List<String> devicesPlatformTypes(List<Device> devices) {

View File

@ -520,7 +520,7 @@ class DeviceValidator extends DoctorValidator {
final List<Device> devices = await _deviceManager.getAllConnectedDevices(); final List<Device> devices = await _deviceManager.getAllConnectedDevices();
List<ValidationMessage> installedMessages = <ValidationMessage>[]; List<ValidationMessage> installedMessages = <ValidationMessage>[];
if (devices.isNotEmpty) { if (devices.isNotEmpty) {
installedMessages = await Device.descriptions(devices) installedMessages = (await Device.descriptions(devices))
.map<ValidationMessage>((String msg) => ValidationMessage(msg)).toList(); .map<ValidationMessage>((String msg) => ValidationMessage(msg)).toList();
} }

View File

@ -1372,7 +1372,7 @@ abstract class FlutterCommand extends Command<void> {
final StringBuffer result = StringBuffer(); final StringBuffer result = StringBuffer();
result.writeln(userMessages.flutterFoundButUnsupportedDevices); result.writeln(userMessages.flutterFoundButUnsupportedDevices);
result.writeAll( result.writeAll(
await Device.descriptions(unsupportedDevices) (await Device.descriptions(unsupportedDevices))
.map((String desc) => desc) .map((String desc) => desc)
.toList(), .toList(),
'\n', '\n',