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 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<void> run(List<String> arguments) async {
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...');
await verifyNoRuntimeTypeInToString(flutterRoot);
@ -153,6 +158,34 @@ Future<void> run(List<String> arguments) async {
// 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 _findGoldenDefinitionPattern = RegExp(r'matchesGoldenFile\(Object');
final RegExp _leadingComment = RegExp(r'\/\/');

View file

@ -430,6 +430,8 @@ Future<void> 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,

View file

@ -48,9 +48,9 @@ class CastListState extends State<CastList> {
const Actor('James Madison', 'JM'),
];
Iterable<Widget> get actorWidgets sync* {
for (final Actor actor in _cast) {
yield Padding(
Iterable<Widget> 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<CastList> {
},
),
);
}
});
}
@override

View file

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

View file

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

View file

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

View file

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

View file

@ -2050,16 +2050,18 @@ class _RenderCupertinoDialogActions extends RenderBox
markNeedsPaint();
}
Iterable<RenderBox> get _pressedButtons sync* {
Iterable<RenderBox> get _pressedButtons {
final List<RenderBox> boxes = <RenderBox>[];
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 {

View file

@ -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: () => <DiagnosticsNode>[
/// ErrorDescription('This happened while climbing the space elevator.'),
/// 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]
/// 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<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); // 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<E> _prefillIterator;

View file

@ -312,13 +312,13 @@ class ChangeNotifier implements Listenable {
stack: stack,
library: 'foundation library',
context: ErrorDescription('while dispatching notifications for $runtimeType'),
informationCollector: () sync* {
yield DiagnosticsProperty<ChangeNotifier>(
informationCollector: () => <DiagnosticsNode>[
DiagnosticsProperty<ChangeNotifier>(
'The $runtimeType sending notification was',
this,
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
/// 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) {
// Nothing to do. The line doesn't wrap.
yield message;
return;
return <String>[message];
}
final List<String> wrappedLine = <String>[];
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<T> extends DiagnosticsProperty<Map<String, T?>> {
//
// For a null value, it is omitted unless `includeEmpty` is true and
// [ifEntryNull] contains a corresponding description.
Iterable<String> _formattedValues() sync* {
for (final MapEntry<String, T?> entry in value.entries) {
if (entry.value != null) {
yield entry.key;
}
}
Iterable<String> _formattedValues() {
return value.entries
.where((MapEntry<String, T?> entry) => entry.value != null)
.map((MapEntry<String, T?> entry) => entry.key);
}
}

View file

@ -139,7 +139,7 @@ class LicenseEntryWithLineBreaks extends LicenseEntry {
final String text;
@override
Iterable<LicenseParagraph> get paragraphs sync* {
Iterable<LicenseParagraph> 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<String> lines = <String>[];
final List<LicenseParagraph> result = <LicenseParagraph>[];
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<LicenseEntry> get licenses async* {
if (_collectors == null)
return;

View file

@ -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<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] == '#') {
yield message;
return;
return <String>[message];
}
final List<String> wrapped = <String>[];
final Match prefixMatch = _indentPattern.matchAsPrefix(message)!;
final String prefix = wrapIndent + ' ' * prefixMatch.group(0)!.length;
int start = 0;
@ -149,13 +149,13 @@ Iterable<String> 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

View file

@ -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<PointerEvent>('Event', event, style: DiagnosticsTreeStyle.errorProperty);
},
informationCollector: () => <DiagnosticsNode>[
DiagnosticsProperty<PointerEvent>('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<PointerEvent>('Event', event, style: DiagnosticsTreeStyle.errorProperty);
yield DiagnosticsProperty<HitTestTarget>('Target', entry.target, style: DiagnosticsTreeStyle.errorProperty);
},
informationCollector: () => <DiagnosticsNode>[
DiagnosticsProperty<PointerEvent>('Event', event, 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
/// from physical coordinates to logical pixels. See the discussion at
/// [PointerEvent] for more details on the [PointerEvent] coordinate space.
static Iterable<PointerEvent> expand(Iterable<ui.PointerData> 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<PointerEvent> expand(Iterable<ui.PointerData> 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;

View file

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

View file

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

View file

@ -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<GestureRecognizer>('Recognizer', this, style: DiagnosticsTreeStyle.errorProperty);
};
collector = () => <DiagnosticsNode>[
StringProperty('Handler', name),
DiagnosticsProperty<GestureRecognizer>('Recognizer', this, style: DiagnosticsTreeStyle.errorProperty),
];
return true;
}());
FlutterError.reportError(FlutterErrorDetails(

View file

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

View file

@ -1076,9 +1076,9 @@ class ChoiceChip extends StatelessWidget
/// ];
/// final List<String> _filters = <String>[];
///
/// Iterable<Widget> get actorWidgets sync* {
/// for (final ActorFilterEntry actor in _cast) {
/// yield Padding(
/// Iterable<Widget> 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<RenderBox> get children sync* {
if (avatar != null) {
yield avatar!;
}
if (label != null) {
yield label!;
}
if (deleteIcon != null) {
yield deleteIcon!;
}
Iterable<RenderBox> get children {
return <RenderBox>[
if (avatar != null)
avatar!,
if (label != null)
label!,
if (deleteIcon != null)
deleteIcon!,
];
}
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.
@override
Iterable<RenderBox> 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<RenderBox> get children {
return <RenderBox>[
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;

View file

@ -1041,33 +1041,31 @@ class ListTile extends StatelessWidget {
/// See also:
///
/// * [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(color != null || context != null);
tiles = tiles.toList();
final Iterator<Widget> 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 <Widget>[
...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<RenderBox> get children sync* {
if (leading != null)
yield leading!;
if (title != null)
yield title!;
if (subtitle != null)
yield subtitle!;
if (trailing != null)
yield trailing!;
Iterable<RenderBox> get children {
return <RenderBox>[
if (leading != null)
leading!,
if (title != null)
title!,
if (subtitle != null)
subtitle!,
if (trailing != null)
trailing!,
];
}
bool get isDense => _isDense;

View file

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

View file

@ -59,12 +59,10 @@ class NetworkImage
InformationCollector? _imageStreamInformationCollector(image_provider.NetworkImage key) {
InformationCollector? collector;
assert(() {
collector = () {
return <DiagnosticsNode>[
DiagnosticsProperty<image_provider.ImageProvider>('Image provider', this),
DiagnosticsProperty<NetworkImage>('Image key', key as NetworkImage),
];
};
collector = () => <DiagnosticsNode>[
DiagnosticsProperty<image_provider.ImageProvider>('Image provider', this),
DiagnosticsProperty<NetworkImage>('Image key', key as NetworkImage),
];
return true;
}());
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 startY = 0;
int stopX = 0;
@ -675,10 +675,11 @@ Iterable<Rect> _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 <Rect>[
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);

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.
InformationCollector? collector;
assert(() {
collector = () sync* {
yield DiagnosticsProperty<ImageProvider>('Image provider', this);
yield DiagnosticsProperty<ImageConfiguration>('Image configuration', configuration);
yield DiagnosticsProperty<T>('Image key', key, defaultValue: null);
};
collector = () => <DiagnosticsNode>[
DiagnosticsProperty<ImageProvider>('Image provider', this),
DiagnosticsProperty<ImageConfiguration>('Image configuration', configuration),
DiagnosticsProperty<T>('Image key', key, defaultValue: null),
];
return true;
}());
if (stream.completer == null) {
@ -395,11 +395,11 @@ abstract class ImageProvider<T extends Object> {
} else {
InformationCollector? collector;
assert(() {
collector = () sync* {
yield DiagnosticsProperty<ImageProvider>('Image provider', this);
yield DiagnosticsProperty<ImageConfiguration>('Image configuration', configuration);
yield DiagnosticsProperty<T>('Image key', key, defaultValue: null);
};
collector = () => <DiagnosticsNode>[
DiagnosticsProperty<ImageProvider>('Image provider', this),
DiagnosticsProperty<ImageConfiguration>('Image configuration', configuration),
DiagnosticsProperty<T>('Image key', key, defaultValue: null),
];
return true;
}());
FlutterError.reportError(FlutterErrorDetails(
@ -648,10 +648,10 @@ abstract class AssetBundleImageProvider extends ImageProvider<AssetBundleImageKe
ImageStreamCompleter load(AssetBundleImageKey key, DecoderCallback decode) {
InformationCollector? collector;
assert(() {
collector = () sync* {
yield DiagnosticsProperty<ImageProvider>('Image provider', this);
yield DiagnosticsProperty<AssetBundleImageKey>('Image key', key);
};
collector = () => <DiagnosticsNode>[
DiagnosticsProperty<ImageProvider>('Image provider', this),
DiagnosticsProperty<AssetBundleImageKey>('Image key', key),
];
return true;
}());
return MultiFrameImageStreamCompleter(
@ -878,9 +878,9 @@ class FileImage extends ImageProvider<FileImage> {
codec: _loadAsync(key, decode),
scale: key.scale,
debugLabel: key.file.path,
informationCollector: () sync* {
yield ErrorDescription('Path: ${file.path}');
},
informationCollector: () => <DiagnosticsNode>[
ErrorDescription('Path: ${file.path}'),
],
);
}

View file

@ -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: () => <DiagnosticsNode>[
// 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),
],
),
);
}

View file

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

View file

@ -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: () => <DiagnosticsNode>[
// 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<String> 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 <DiagnosticsNode>[
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 <DiagnosticsNode>[];
},
));
assert(!_debugDoingThisResize);

View file

@ -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: () => <DiagnosticsNode>[
describeForError('The RenderSliver that returned the offending geometry was'),
],
));
assert(() {
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.
///
/// This is a lazily-evaluated iterable.
// flutter_ignore: no_sync_async_star
Iterable<RenderBox> 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<RenderBox> row(int y) sync* {
final int start = y * columns;
final int end = (y + 1) * columns;

View file

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

View file

@ -288,13 +288,13 @@ mixin SchedulerBinding on BindingBase {
} catch (exception, stack) {
InformationCollector? collector;
assert(() {
collector = () sync* {
yield DiagnosticsProperty<TimingsCallback>(
collector = () => <DiagnosticsNode>[
DiagnosticsProperty<TimingsCallback>(
'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 <DiagnosticsNode>[
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: () => <DiagnosticsNode>[
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 <DiagnosticsNode>[
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,
),
];
},
));
}

View file

@ -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<LicenseEntry> _addLicenses() async* {
// Using _something_ here to break
// this into two parts is important because isolates take a while to copy

View file

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

View file

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

View file

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

View file

@ -1472,10 +1472,12 @@ class _WidgetsAppState extends State<WidgetsApp> with WidgetsBindingObserver {
// of a particular LocalizationsDelegate.type is loaded so the
// localizationsDelegate parameter can be used to override
// WidgetsLocalizations.delegate.
Iterable<LocalizationsDelegate<dynamic>> get _localizationsDelegates sync* {
if (widget.localizationsDelegates != null)
yield* widget.localizationsDelegates!;
yield DefaultWidgetsLocalizations.delegate;
Iterable<LocalizationsDelegate<dynamic>> get _localizationsDelegates {
return <LocalizationsDelegate<dynamic>>[
if (widget.localizationsDelegates != null)
...widget.localizationsDelegates!,
DefaultWidgetsLocalizations.delegate,
];
}
// BUILDER
@ -1496,33 +1498,33 @@ class _WidgetsAppState extends State<WidgetsApp> 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: () => <DiagnosticsNode>[
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(
...<DiagnosticsNode>[
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;
}());

View file

@ -905,17 +905,19 @@ class _DragAvatar<T extends Object> extends Drag {
_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
// widgets build RenderMetaData boxes for us for this purpose).
final List<_DragTargetState<Object>> targets = <_DragTargetState<Object>>[];
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() {

View file

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

View file

@ -1455,35 +1455,34 @@ abstract class ParentDataWidget<T extends ParentData> 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 <DiagnosticsNode>[
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: () => <DiagnosticsNode>[
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: () => <DiagnosticsNode>[
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: () => <DiagnosticsNode>[
if (kDebugMode)
DiagnosticsDebugCreator(DebugCreator(this)),
],
),
);
_child = updateChild(null, built, slot);

View file

@ -127,9 +127,10 @@ class _LayoutBuilderElement<ConstraintType extends Constraints> extends RenderOb
ErrorDescription('building $widget'),
e,
stack,
informationCollector: () sync* {
yield DiagnosticsDebugCreator(DebugCreator(this));
},
informationCollector: () => <DiagnosticsNode>[
if (kDebugMode)
DiagnosticsDebugCreator(DebugCreator(this)),
],
),
);
}
@ -142,9 +143,10 @@ class _LayoutBuilderElement<ConstraintType extends Constraints> extends RenderOb
ErrorDescription('building $widget'),
e,
stack,
informationCollector: () sync* {
yield DiagnosticsDebugCreator(DebugCreator(this));
},
informationCollector: () => <DiagnosticsNode>[
if (kDebugMode)
DiagnosticsDebugCreator(DebugCreator(this)),
],
),
);
_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.
OverlayState? get overlay => _overlayKey.currentState;
Iterable<OverlayEntry> get _allRouteOverlayEntries sync* {
for (final _RouteEntry entry in _history)
yield* entry.route.overlayEntries;
Iterable<OverlayEntry> get _allRouteOverlayEntries {
return <OverlayEntry>[
for (final _RouteEntry entry in _history)
...entry.route.overlayEntries,
];
}
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.
yield* Iterable.castFrom<ScrollPosition, _NestedScrollPosition>(positions);
return Iterable.castFrom<ScrollPosition, _NestedScrollPosition>(positions);
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -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<DiagnosticsNode> debugTransformDebugCreator(Iterable<DiagnosticsNode> properties) sync* {
Iterable<DiagnosticsNode> debugTransformDebugCreator(Iterable<DiagnosticsNode> properties) {
if (!kDebugMode) {
return;
return <DiagnosticsNode>[];
}
final List<DiagnosticsNode> pending = <DiagnosticsNode>[];
ErrorSummary? errorSummary;
@ -2929,20 +2929,22 @@ Iterable<DiagnosticsNode> debugTransformDebugCreator(Iterable<DiagnosticsNode> p
}
}
bool foundStackTrace = false;
final List<DiagnosticsNode> result = <DiagnosticsNode>[];
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<DiagnosticsNode> debugTransformDebugCreator(Iterable<DiagnosticsNode> p
Iterable<DiagnosticsNode> _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>[
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);
});
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 {
// Regression test for https://github.com/flutter/flutter/pull/78879
int callCount = 0;

View file

@ -740,26 +740,22 @@ class PubspecYaml {
}
/// 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
// 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<PubspecDependency>()
.where((PubspecDependency data) => data.kind != DependencyKind.overridden && !data.isTransitive && !data.isDevDependency);
}
/// This returns all regular dependencies and all dev dependencies.
Iterable<PubspecDependency> get allDependencies sync* {
for (final PubspecLine data in inputData) {
if (data is PubspecDependency && data.kind != DependencyKind.overridden && !data.isTransitive) {
yield data;
}
}
Iterable<PubspecDependency> get allDependencies {
return inputData
.whereType<PubspecDependency>()
.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<String> seen,
@required Set<String> exclude,
}) sync* {
List<String>/*?*/ result,
}) {
assert(seen != null);
assert(exclude != null);
result ??= <String>[];
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.

View file

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

View file

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

View file

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