mirror of
https://github.com/flutter/flutter
synced 2024-08-27 12:00:58 +00:00
Ban sync*/async* from user facing code (#95050)
This commit is contained in:
parent
895beb04bb
commit
ab0a335973
|
@ -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'\/\/');
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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'),
|
||||
/// ],
|
||||
/// ));
|
||||
/// }
|
||||
/// }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
],
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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}'),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
];
|
||||
},
|
||||
));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}());
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>[];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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',
|
||||
|
|
Loading…
Reference in a new issue