Add @widgetFactory
annotation (#117455)
* Add `@widgetFactory` annotation * Simplify and fix example * Specify `TargetKind`s for `widgetFactory` * Explain why `library_private_types_in_public_api` is ignored. * Trigger CI
This commit is contained in:
parent
c6b636fa51
commit
2b7d709fdc
@ -23,6 +23,7 @@ import 'dart:ui' as ui
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:meta/meta_meta.dart';
|
||||
|
||||
import 'app.dart';
|
||||
import 'basic.dart';
|
||||
@ -3684,3 +3685,68 @@ class InspectorSerializationDelegate implements DiagnosticsSerializationDelegate
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Target(<TargetKind>{TargetKind.method})
|
||||
class _WidgetFactory {
|
||||
const _WidgetFactory();
|
||||
}
|
||||
|
||||
/// Annotation which marks a function as a widget factory for the purpose of
|
||||
/// widget creation tracking.
|
||||
///
|
||||
/// When widget creation tracking is enabled, the framework tracks the source
|
||||
/// code location of the constructor call for each widget instance. This
|
||||
/// information is used by the DevTools to provide an improved developer
|
||||
/// experience. For example, it allows the Flutter inspector to present the
|
||||
/// widget tree in a manner similar to how the UI was defined in your source
|
||||
/// code.
|
||||
///
|
||||
/// [Widget] constructors are automatically instrumented to track the source
|
||||
/// code location of constructor calls. However, there are cases where
|
||||
/// a function acts as a sort of a constructor for a widget and a call to such
|
||||
/// a function should be considered as the creation location for the returned
|
||||
/// widget instance.
|
||||
///
|
||||
/// Annotating a function with this annotation marks the function as a widget
|
||||
/// factory. The framework will then instrument that function in the same way
|
||||
/// as it does for [Widget] constructors.
|
||||
///
|
||||
/// Note that the function **must not** have optional positional parameters for
|
||||
/// tracking to work correctly.
|
||||
///
|
||||
/// Currently this annotation is only supported on extension methods.
|
||||
///
|
||||
/// {@tool snippet}
|
||||
///
|
||||
/// This example shows how to use the [widgetFactory] annotation to mark an
|
||||
/// extension method as a widget factory:
|
||||
///
|
||||
/// ```dart
|
||||
/// extension PaddingModifier on Widget {
|
||||
/// @widgetFactory
|
||||
/// Widget padding(EdgeInsetsGeometry padding) {
|
||||
/// return Padding(padding: padding, child: this);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// When using the above extension method, the framework will track the
|
||||
/// creation location of the [Padding] widget instance as the source code
|
||||
/// location where the `padding` extension method was called:
|
||||
///
|
||||
/// ```dart
|
||||
/// // continuing from previous example...
|
||||
/// const Text('Hello World!')
|
||||
/// .padding(const EdgeInsets.all(8));
|
||||
/// ```
|
||||
///
|
||||
/// {@end-tool}
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * the documentation for [Track widget creation](https://docs.flutter.dev/development/tools/devtools/inspector#track-widget-creation).
|
||||
// The below ignore is needed because the static type of the annotation is used
|
||||
// by the CFE kernel transformer that implements the instrumentation to
|
||||
// recognize the annotation.
|
||||
// ignore: library_private_types_in_public_api
|
||||
const _WidgetFactory widgetFactory = _WidgetFactory();
|
||||
|
@ -238,6 +238,13 @@ int getChildLayerCount(OffsetLayer layer) {
|
||||
return count;
|
||||
}
|
||||
|
||||
extension TextFromString on String {
|
||||
@widgetFactory
|
||||
Widget text() {
|
||||
return Text(this);
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
_TestWidgetInspectorService.runTests();
|
||||
}
|
||||
@ -944,19 +951,20 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService {
|
||||
|
||||
testWidgets('WidgetInspectorService creationLocation', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const Directionality(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
Text('a'),
|
||||
Text('b', textDirection: TextDirection.ltr),
|
||||
Text('c', textDirection: TextDirection.ltr),
|
||||
const Text('a'),
|
||||
const Text('b', textDirection: TextDirection.ltr),
|
||||
'c'.text(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
final Element elementA = find.text('a').evaluate().first;
|
||||
final Element elementB = find.text('b').evaluate().first;
|
||||
final Element elementC = find.text('c').evaluate().first;
|
||||
|
||||
service.disposeAllGroups();
|
||||
service.resetPubRootDirectories();
|
||||
@ -979,14 +987,28 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService {
|
||||
final int columnB = creationLocationB['column']! as int;
|
||||
final String? nameB = creationLocationB['name'] as String?;
|
||||
expect(nameB, equals('Text'));
|
||||
|
||||
service.setSelection(elementC, 'my-group');
|
||||
final Map<String, Object?> jsonC = json.decode(service.getSelectedWidget(null, 'my-group')) as Map<String, Object?>;
|
||||
final Map<String, Object?> creationLocationC = jsonC['creationLocation']! as Map<String, Object?>;
|
||||
expect(creationLocationC, isNotNull);
|
||||
final String fileC = creationLocationC['file']! as String;
|
||||
final int lineC = creationLocationC['line']! as int;
|
||||
final int columnC = creationLocationC['column']! as int;
|
||||
final String? nameC = creationLocationC['name'] as String?;
|
||||
expect(nameC, equals('TextFromString|text'));
|
||||
|
||||
expect(fileA, endsWith('widget_inspector_test.dart'));
|
||||
expect(fileA, equals(fileB));
|
||||
expect(fileA, equals(fileC));
|
||||
// We don't hardcode the actual lines the widgets are created on as that
|
||||
// would make this test fragile.
|
||||
expect(lineA + 1, equals(lineB));
|
||||
expect(lineB + 1, equals(lineC));
|
||||
// Column numbers are more stable than line numbers.
|
||||
expect(columnA, equals(15));
|
||||
expect(columnA, equals(21));
|
||||
expect(columnA, equals(columnB));
|
||||
expect(columnC, equals(19));
|
||||
}, skip: !WidgetInspectorService.instance.isWidgetCreationTracked()); // [intended] Test requires --track-widget-creation flag.
|
||||
|
||||
testWidgets('WidgetInspectorService setSelection notifiers for an Element',
|
||||
|
Loading…
x
Reference in New Issue
Block a user