Relands "Add runWidget to bootstrap a widget tree without a default View" (#142344)
Reverts flutter/flutter#142339 In the original change one of the tests included the same view twice which resulted in a different failure than the expected one. The second commit contains the fix for this. I don't understand how this wasn't caught presubmit on CI.
This commit is contained in:
parent
a5ad088f7b
commit
671d8eaf71
@ -24,6 +24,10 @@ import 'widget_inspector.dart';
|
|||||||
|
|
||||||
export 'dart:ui' show AppLifecycleState, Locale;
|
export 'dart:ui' show AppLifecycleState, Locale;
|
||||||
|
|
||||||
|
// Examples can assume:
|
||||||
|
// late FlutterView myFlutterView;
|
||||||
|
// class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) => const Placeholder(); }
|
||||||
|
|
||||||
/// Interface for classes that register with the Widgets layer binding.
|
/// Interface for classes that register with the Widgets layer binding.
|
||||||
///
|
///
|
||||||
/// This can be used by any class, not just widgets. It provides an interface
|
/// This can be used by any class, not just widgets. It provides an interface
|
||||||
@ -1152,14 +1156,20 @@ mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureB
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inflate the given widget and attach it to the screen.
|
/// Inflate the given widget and attach it to the view.
|
||||||
|
///
|
||||||
|
// TODO(goderbauer): Update the paragraph below to include the Window widget once that exists.
|
||||||
|
/// The [runApp] method renders the provided `app` widget into the
|
||||||
|
/// [PlatformDispatcher.implicitView] by wrapping it in a [View] widget, which
|
||||||
|
/// will bootstrap the render tree for the app. Apps that want to control which
|
||||||
|
/// [FlutterView] they render into can use [runWidget] instead.
|
||||||
///
|
///
|
||||||
/// The widget is given constraints during layout that force it to fill the
|
/// The widget is given constraints during layout that force it to fill the
|
||||||
/// entire screen. If you wish to align your widget to one side of the screen
|
/// entire view. If you wish to align your widget to one side of the view
|
||||||
/// (e.g., the top), consider using the [Align] widget. If you wish to center
|
/// (e.g., the top), consider using the [Align] widget. If you wish to center
|
||||||
/// your widget, you can also use the [Center] widget.
|
/// your widget, you can also use the [Center] widget.
|
||||||
///
|
///
|
||||||
/// Calling [runApp] again will detach the previous root widget from the screen
|
/// Calling [runApp] again will detach the previous root widget from the view
|
||||||
/// and attach the given widget in its place. The new widget tree is compared
|
/// and attach the given widget in its place. The new widget tree is compared
|
||||||
/// against the previous widget tree and any differences are applied to the
|
/// against the previous widget tree and any differences are applied to the
|
||||||
/// underlying render tree, similar to what happens when a [StatefulWidget]
|
/// underlying render tree, similar to what happens when a [StatefulWidget]
|
||||||
@ -1167,6 +1177,7 @@ mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureB
|
|||||||
///
|
///
|
||||||
/// Initializes the binding using [WidgetsFlutterBinding] if necessary.
|
/// Initializes the binding using [WidgetsFlutterBinding] if necessary.
|
||||||
///
|
///
|
||||||
|
/// {@template flutter.widgets.runApp.shutdown}
|
||||||
/// ## Application shutdown
|
/// ## Application shutdown
|
||||||
///
|
///
|
||||||
/// This widget tree is not torn down when the application shuts down, because
|
/// This widget tree is not torn down when the application shuts down, because
|
||||||
@ -1178,29 +1189,32 @@ mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureB
|
|||||||
/// Applications are responsible for ensuring that they are well-behaved
|
/// Applications are responsible for ensuring that they are well-behaved
|
||||||
/// even in the face of a rapid unscheduled termination.
|
/// even in the face of a rapid unscheduled termination.
|
||||||
///
|
///
|
||||||
|
/// To listen for platform shutdown messages (and other lifecycle changes),
|
||||||
|
/// consider the [AppLifecycleListener] API.
|
||||||
|
/// {@endtemplate}
|
||||||
|
///
|
||||||
/// To artificially cause the entire widget tree to be disposed, consider
|
/// To artificially cause the entire widget tree to be disposed, consider
|
||||||
/// calling [runApp] with a widget such as [SizedBox.shrink].
|
/// calling [runApp] with a widget such as [SizedBox.shrink].
|
||||||
///
|
///
|
||||||
/// To listen for platform shutdown messages (and other lifecycle changes),
|
/// {@template flutter.widgets.runApp.dismissal}
|
||||||
/// consider the [AppLifecycleListener] API.
|
|
||||||
///
|
|
||||||
/// ## Dismissing Flutter UI via platform native methods
|
/// ## Dismissing Flutter UI via platform native methods
|
||||||
///
|
///
|
||||||
/// {@template flutter.widgets.runApp.dismissal}
|
|
||||||
/// An application may have both Flutter and non-Flutter UI in it. If the
|
/// An application may have both Flutter and non-Flutter UI in it. If the
|
||||||
/// application calls non-Flutter methods to remove Flutter based UI such as
|
/// application calls non-Flutter methods to remove Flutter based UI such as
|
||||||
/// platform native API to manipulate the platform native navigation stack,
|
/// platform native API to manipulate the platform native navigation stack,
|
||||||
/// the framework does not know if the developer intends to eagerly free
|
/// the framework does not know if the developer intends to eagerly free
|
||||||
/// resources or not. The widget tree remains mounted and ready to render
|
/// resources or not. The widget tree remains mounted and ready to render
|
||||||
/// as soon as it is displayed again.
|
/// as soon as it is displayed again.
|
||||||
|
/// {@endtemplate}
|
||||||
///
|
///
|
||||||
/// To release resources more eagerly, establish a [platform channel](https://flutter.dev/platform-channels/)
|
/// To release resources more eagerly, establish a [platform channel](https://flutter.dev/platform-channels/)
|
||||||
/// and use it to call [runApp] with a widget such as [SizedBox.shrink] when
|
/// and use it to call [runApp] with a widget such as [SizedBox.shrink] when
|
||||||
/// the framework should dispose of the active widget tree.
|
/// the framework should dispose of the active widget tree.
|
||||||
/// {@endtemplate}
|
|
||||||
///
|
///
|
||||||
/// See also:
|
/// See also:
|
||||||
///
|
///
|
||||||
|
/// * [runWidget], which bootstraps a widget tree without assuming the
|
||||||
|
/// [FlutterView] into which it will be rendered.
|
||||||
/// * [WidgetsBinding.attachRootWidget], which creates the root widget for the
|
/// * [WidgetsBinding.attachRootWidget], which creates the root widget for the
|
||||||
/// widget hierarchy.
|
/// widget hierarchy.
|
||||||
/// * [RenderObjectToWidgetAdapter.attachToRenderTree], which creates the root
|
/// * [RenderObjectToWidgetAdapter.attachToRenderTree], which creates the root
|
||||||
@ -1209,9 +1223,75 @@ mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureB
|
|||||||
/// ensure the widget, element, and render trees are all built.
|
/// ensure the widget, element, and render trees are all built.
|
||||||
void runApp(Widget app) {
|
void runApp(Widget app) {
|
||||||
final WidgetsBinding binding = WidgetsFlutterBinding.ensureInitialized();
|
final WidgetsBinding binding = WidgetsFlutterBinding.ensureInitialized();
|
||||||
assert(binding.debugCheckZone('runApp'));
|
_runWidget(binding.wrapWithDefaultView(app), binding, 'runApp');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inflate the given widget and bootstrap the widget tree.
|
||||||
|
///
|
||||||
|
// TODO(goderbauer): Update the paragraph below to include the Window widget once that exists.
|
||||||
|
/// Unlike [runApp], this method does not define a [FlutterView] into which the
|
||||||
|
/// provided `app` widget is rendered into. It is up to the caller to include at
|
||||||
|
/// least one [View] widget in the provided `app` widget that will bootstrap a
|
||||||
|
/// render tree and define the [FlutterView] into which content is rendered.
|
||||||
|
/// [RenderObjectWidget]s without an ancestor [View] widget will result in an
|
||||||
|
/// exception. Apps that want to render into the default view without dealing
|
||||||
|
/// with view management should consider calling [runApp] instead.
|
||||||
|
///
|
||||||
|
/// {@tool snippet}
|
||||||
|
/// The sample shows how to utilize [runWidget] to specify the [FlutterView]
|
||||||
|
/// into which the `MyApp` widget will be drawn:
|
||||||
|
///
|
||||||
|
/// ```dart
|
||||||
|
/// runWidget(
|
||||||
|
/// View(
|
||||||
|
/// view: myFlutterView,
|
||||||
|
/// child: const MyApp(),
|
||||||
|
/// ),
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
/// {@end-tool}
|
||||||
|
///
|
||||||
|
/// Calling [runWidget] again will detach the previous root widget and attach
|
||||||
|
/// the given widget in its place. The new widget tree is compared against the
|
||||||
|
/// previous widget tree and any differences are applied to the underlying
|
||||||
|
/// render tree, similar to what happens when a [StatefulWidget] rebuilds after
|
||||||
|
/// calling [State.setState].
|
||||||
|
///
|
||||||
|
/// Initializes the binding using [WidgetsFlutterBinding] if necessary.
|
||||||
|
///
|
||||||
|
/// {@macro flutter.widgets.runApp.shutdown}
|
||||||
|
///
|
||||||
|
/// To artificially cause the entire widget tree to be disposed, consider
|
||||||
|
/// calling [runWidget] with a [ViewCollection] that does not specify any
|
||||||
|
/// [ViewCollection.views].
|
||||||
|
///
|
||||||
|
/// ## Dismissing Flutter UI via platform native methods
|
||||||
|
///
|
||||||
|
/// {@macro flutter.widgets.runApp.dismissal}
|
||||||
|
///
|
||||||
|
/// To release resources more eagerly, establish a [platform channel](https://flutter.dev/platform-channels/)
|
||||||
|
/// and use it to remove the [View] whose widget resources should be released
|
||||||
|
/// from the `app` widget tree provided to [runWidget].
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [runApp], which bootstraps a widget tree and renders it into a default
|
||||||
|
/// [FlutterView].
|
||||||
|
/// * [WidgetsBinding.attachRootWidget], which creates the root widget for the
|
||||||
|
/// widget hierarchy.
|
||||||
|
/// * [RenderObjectToWidgetAdapter.attachToRenderTree], which creates the root
|
||||||
|
/// element for the element hierarchy.
|
||||||
|
/// * [WidgetsBinding.handleBeginFrame], which pumps the widget pipeline to
|
||||||
|
/// ensure the widget, element, and render trees are all built.
|
||||||
|
void runWidget(Widget app) {
|
||||||
|
final WidgetsBinding binding = WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
_runWidget(app, binding, 'runWidget');
|
||||||
|
}
|
||||||
|
|
||||||
|
void _runWidget(Widget app, WidgetsBinding binding, String debugEntryPoint) {
|
||||||
|
assert(binding.debugCheckZone(debugEntryPoint));
|
||||||
binding
|
binding
|
||||||
..scheduleAttachRootWidget(binding.wrapWithDefaultView(app))
|
..scheduleAttachRootWidget(app)
|
||||||
..scheduleWarmUpFrame();
|
..scheduleWarmUpFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1325,10 +1325,11 @@ abstract class State<T extends StatefulWidget> with Diagnosticable {
|
|||||||
/// To listen for platform shutdown messages (and other lifecycle changes),
|
/// To listen for platform shutdown messages (and other lifecycle changes),
|
||||||
/// consider the [AppLifecycleListener] API.
|
/// consider the [AppLifecycleListener] API.
|
||||||
///
|
///
|
||||||
/// ### Dismissing Flutter UI via platform native methods
|
|
||||||
///
|
|
||||||
/// {@macro flutter.widgets.runApp.dismissal}
|
/// {@macro flutter.widgets.runApp.dismissal}
|
||||||
///
|
///
|
||||||
|
/// See the method used to bootstrap the app (e.g. [runApp] or [runWidget])
|
||||||
|
/// for suggestions on how to release resources more eagerly.
|
||||||
|
///
|
||||||
/// See also:
|
/// See also:
|
||||||
///
|
///
|
||||||
/// * [deactivate], which is called prior to [dispose].
|
/// * [deactivate], which is called prior to [dispose].
|
||||||
@ -6970,9 +6971,16 @@ abstract class RenderTreeRootElement extends RenderObjectElement {
|
|||||||
'however, expects that a child will be attached.',
|
'however, expects that a child will be attached.',
|
||||||
),
|
),
|
||||||
ErrorHint(
|
ErrorHint(
|
||||||
'Try moving the subtree that contains the ${toStringShort()} widget into the '
|
'Try moving the subtree that contains the ${toStringShort()} widget '
|
||||||
'view property of a ViewAnchor widget or to the root of the widget tree, where '
|
'to a location where it is not expected to attach its RenderObject '
|
||||||
'it is not expected to attach its RenderObject to a parent.',
|
'to a parent. This could mean moving the subtree into the view '
|
||||||
|
'property of a "ViewAnchor" widget or - if the subtree is the root of '
|
||||||
|
'your widget tree - passing it to "runWidget" instead of "runApp".',
|
||||||
|
),
|
||||||
|
ErrorHint(
|
||||||
|
'If you are seeing this error in a test and the subtree containing '
|
||||||
|
'the ${toStringShort()} widget is passed to "WidgetTester.pumpWidget", '
|
||||||
|
'consider setting the "wrapWithView" parameter of that method to false.'
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -13,9 +13,9 @@ void main() {
|
|||||||
..physicalConstraints = ViewConstraints.tight(const Size(1008.0, 2198.0))
|
..physicalConstraints = ViewConstraints.tight(const Size(1008.0, 2198.0))
|
||||||
..devicePixelRatio = 1.912500023841858;
|
..devicePixelRatio = 1.912500023841858;
|
||||||
|
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: View(
|
View(
|
||||||
view: view,
|
view: view,
|
||||||
child: const SizedBox(),
|
child: const SizedBox(),
|
||||||
),
|
),
|
||||||
@ -35,9 +35,3 @@ class FlutterViewSpy extends TestFlutterView {
|
|||||||
sizes.add(size);
|
sizes.add(size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> pumpWidgetWithoutViewWrapper({required WidgetTester tester, required Widget widget}) {
|
|
||||||
tester.binding.attachRootWidget(widget);
|
|
||||||
tester.binding.scheduleFrame();
|
|
||||||
return tester.binding.pump();
|
|
||||||
}
|
|
||||||
|
@ -98,9 +98,9 @@ void main() {
|
|||||||
testWidgets('debugCheckHasMediaQuery control test', (WidgetTester tester) async {
|
testWidgets('debugCheckHasMediaQuery control test', (WidgetTester tester) async {
|
||||||
// Cannot use tester.pumpWidget here because it wraps the widget in a View,
|
// Cannot use tester.pumpWidget here because it wraps the widget in a View,
|
||||||
// which introduces a MediaQuery ancestor.
|
// which introduces a MediaQuery ancestor.
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: Builder(
|
Builder(
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
late FlutterError error;
|
late FlutterError error;
|
||||||
try {
|
try {
|
||||||
@ -343,9 +343,3 @@ void main() {
|
|||||||
expect(renderObject.debugLayer?.debugCreator, isNotNull);
|
expect(renderObject.debugLayer?.debugCreator, isNotNull);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> pumpWidgetWithoutViewWrapper({required WidgetTester tester, required Widget widget}) {
|
|
||||||
tester.binding.attachRootWidget(widget);
|
|
||||||
tester.binding.scheduleFrame();
|
|
||||||
return tester.binding.pump();
|
|
||||||
}
|
|
||||||
|
@ -47,9 +47,9 @@ void main() {
|
|||||||
late final FlutterError error;
|
late final FlutterError error;
|
||||||
// Cannot use tester.pumpWidget here because it wraps the widget in a View,
|
// Cannot use tester.pumpWidget here because it wraps the widget in a View,
|
||||||
// which introduces a MediaQuery ancestor.
|
// which introduces a MediaQuery ancestor.
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: Builder(
|
Builder(
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
try {
|
try {
|
||||||
MediaQuery.of(context);
|
MediaQuery.of(context);
|
||||||
@ -111,9 +111,9 @@ void main() {
|
|||||||
bool tested = false;
|
bool tested = false;
|
||||||
// Cannot use tester.pumpWidget here because it wraps the widget in a View,
|
// Cannot use tester.pumpWidget here because it wraps the widget in a View,
|
||||||
// which introduces a MediaQuery ancestor.
|
// which introduces a MediaQuery ancestor.
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: Builder(
|
Builder(
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
final MediaQueryData? data = MediaQuery.maybeOf(context);
|
final MediaQueryData? data = MediaQuery.maybeOf(context);
|
||||||
expect(data, isNull);
|
expect(data, isNull);
|
||||||
@ -287,9 +287,9 @@ void main() {
|
|||||||
|
|
||||||
late MediaQueryData data;
|
late MediaQueryData data;
|
||||||
MediaQueryData? outerData;
|
MediaQueryData? outerData;
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: Builder(
|
Builder(
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
outerData = MediaQuery.maybeOf(context);
|
outerData = MediaQuery.maybeOf(context);
|
||||||
return MediaQuery.fromView(
|
return MediaQuery.fromView(
|
||||||
@ -342,9 +342,9 @@ void main() {
|
|||||||
late MediaQueryData data;
|
late MediaQueryData data;
|
||||||
MediaQueryData? outerData;
|
MediaQueryData? outerData;
|
||||||
int rebuildCount = 0;
|
int rebuildCount = 0;
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: Builder(
|
Builder(
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
outerData = MediaQuery.maybeOf(context);
|
outerData = MediaQuery.maybeOf(context);
|
||||||
return MediaQuery.fromView(
|
return MediaQuery.fromView(
|
||||||
@ -1531,9 +1531,3 @@ void main() {
|
|||||||
]
|
]
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> pumpWidgetWithoutViewWrapper({required WidgetTester tester, required Widget widget}) {
|
|
||||||
tester.binding.attachRootWidget(widget);
|
|
||||||
tester.binding.scheduleFrame();
|
|
||||||
return tester.binding.pump();
|
|
||||||
}
|
|
||||||
|
@ -26,7 +26,7 @@ void main() {
|
|||||||
|
|
||||||
final RootWidget rootWidget = RootWidget(
|
final RootWidget rootWidget = RootWidget(
|
||||||
child: View(
|
child: View(
|
||||||
view: tester.view,
|
view: FakeFlutterView(tester.view),
|
||||||
child: const ColoredBox(color: Colors.orange),
|
child: const ColoredBox(color: Colors.orange),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -36,4 +36,52 @@ void main() {
|
|||||||
expect(tester.binding.rootElement!.widget, equals(rootWidget));
|
expect(tester.binding.rootElement!.widget, equals(rootWidget));
|
||||||
expect(tester.element(find.byType(ColoredBox)).owner, equals(tester.binding.buildOwner));
|
expect(tester.element(find.byType(ColoredBox)).owner, equals(tester.binding.buildOwner));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('runApp throws if given a View', (WidgetTester tester) async {
|
||||||
|
runApp(
|
||||||
|
View(
|
||||||
|
view: FakeFlutterView(tester.view),
|
||||||
|
child: const SizedBox.shrink(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
tester.takeException(),
|
||||||
|
isFlutterError.having(
|
||||||
|
(FlutterError e) => e.message,
|
||||||
|
'message',
|
||||||
|
contains('passing it to "runWidget" instead of "runApp"'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('runWidget throws if not given a View', (WidgetTester tester) async {
|
||||||
|
runWidget(const SizedBox.shrink());
|
||||||
|
expect(
|
||||||
|
tester.takeException(),
|
||||||
|
isFlutterError.having(
|
||||||
|
(FlutterError e) => e.message,
|
||||||
|
'message',
|
||||||
|
contains('Try wrapping your widget in a View widget'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('runWidget does not throw if given a View', (WidgetTester tester) async {
|
||||||
|
runWidget(
|
||||||
|
View(
|
||||||
|
view: FakeFlutterView(tester.view),
|
||||||
|
child: const SizedBox.shrink(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(find.byType(View), findsOne);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('can call runWidget with an empty ViewCollection', (WidgetTester tester) async {
|
||||||
|
runWidget(const ViewCollection(views: <Widget>[]));
|
||||||
|
expect(find.byType(ViewCollection), findsOne);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class FakeFlutterView extends TestFlutterView {
|
||||||
|
FakeFlutterView(TestFlutterView view) : super(view: view, display: view.display, platformDispatcher: view.platformDispatcher);
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,9 @@ void main() {
|
|||||||
child: const TestWidget(),
|
child: const TestWidget(),
|
||||||
);
|
);
|
||||||
|
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: widget,
|
widget,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(find.text('Hello'), findsOneWidget);
|
expect(find.text('Hello'), findsOneWidget);
|
||||||
@ -29,9 +29,9 @@ void main() {
|
|||||||
expect(find.text('World'), findsOneWidget);
|
expect(find.text('World'), findsOneWidget);
|
||||||
expect(tester.renderObject<RenderParagraph>(find.byType(Text)).text.toPlainText(), 'World');
|
expect(tester.renderObject<RenderParagraph>(find.byType(Text)).text.toPlainText(), 'World');
|
||||||
|
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: ViewCollection(
|
ViewCollection(
|
||||||
views: <Widget>[widget],
|
views: <Widget>[widget],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -40,9 +40,9 @@ void main() {
|
|||||||
expect(tester.renderObject<RenderParagraph>(find.byType(Text)).text.toPlainText(), 'World');
|
expect(tester.renderObject<RenderParagraph>(find.byType(Text)).text.toPlainText(), 'World');
|
||||||
|
|
||||||
tester.state<TestWidgetState>(find.byType(TestWidget)).text = 'FooBar';
|
tester.state<TestWidgetState>(find.byType(TestWidget)).text = 'FooBar';
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: widget,
|
widget,
|
||||||
);
|
);
|
||||||
expect(find.text('World'), findsNothing);
|
expect(find.text('World'), findsNothing);
|
||||||
expect(find.text('FooBar'), findsOneWidget);
|
expect(find.text('FooBar'), findsOneWidget);
|
||||||
@ -65,9 +65,9 @@ void main() {
|
|||||||
child: TestWidget(key: key2),
|
child: TestWidget(key: key2),
|
||||||
);
|
);
|
||||||
|
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: ViewCollection(
|
ViewCollection(
|
||||||
views: <Widget>[view1, view2],
|
views: <Widget>[view1, view2],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -91,9 +91,9 @@ void main() {
|
|||||||
expect(renderParagraphTexts(), <String>['Guten', 'Abend']);
|
expect(renderParagraphTexts(), <String>['Guten', 'Abend']);
|
||||||
|
|
||||||
tester.state<TestWidgetState>(find.byKey(key2)).text = 'Morgen';
|
tester.state<TestWidgetState>(find.byKey(key2)).text = 'Morgen';
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: ViewCollection(
|
ViewCollection(
|
||||||
views: <Widget>[view1, ViewCollection(views: <Widget>[view2])],
|
views: <Widget>[view1, ViewCollection(views: <Widget>[view2])],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -202,9 +202,3 @@ class TestWidgetState extends State<TestWidget> {
|
|||||||
return Text(text, textDirection: TextDirection.ltr);
|
return Text(text, textDirection: TextDirection.ltr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> pumpWidgetWithoutViewWrapper({required WidgetTester tester, required Widget widget}) {
|
|
||||||
tester.binding.attachRootWidget(widget);
|
|
||||||
tester.binding.scheduleFrame();
|
|
||||||
return tester.binding.pump();
|
|
||||||
}
|
|
||||||
|
@ -13,9 +13,9 @@ import 'multi_view_testing.dart';
|
|||||||
void main() {
|
void main() {
|
||||||
testWidgets('Providing a RenderObjectWidget directly to the RootWidget fails', (WidgetTester tester) async {
|
testWidgets('Providing a RenderObjectWidget directly to the RootWidget fails', (WidgetTester tester) async {
|
||||||
// No render tree exists to attach the RenderObjectWidget to.
|
// No render tree exists to attach the RenderObjectWidget to.
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: const ColoredBox(color: Colors.red),
|
const ColoredBox(color: Colors.red),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(tester.takeException(), isFlutterError.having(
|
expect(tester.takeException(), isFlutterError.having(
|
||||||
@ -31,18 +31,18 @@ void main() {
|
|||||||
color: Colors.red,
|
color: Colors.red,
|
||||||
);
|
);
|
||||||
|
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: View(
|
View(
|
||||||
view: tester.view,
|
view: tester.view,
|
||||||
child: globalKeyedWidget,
|
child: globalKeyedWidget,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(tester.takeException(), isNull);
|
expect(tester.takeException(), isNull);
|
||||||
|
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: globalKeyedWidget,
|
globalKeyedWidget,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(tester.takeException(), isFlutterError.having(
|
expect(tester.takeException(), isFlutterError.having(
|
||||||
@ -91,15 +91,15 @@ void main() {
|
|||||||
child: const ColoredBox(color: Colors.red),
|
child: const ColoredBox(color: Colors.red),
|
||||||
);
|
);
|
||||||
|
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: globalKeyedView,
|
globalKeyedView,
|
||||||
);
|
);
|
||||||
expect(tester.takeException(), isNull);
|
expect(tester.takeException(), isNull);
|
||||||
|
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: View(
|
View(
|
||||||
view: tester.view,
|
view: tester.view,
|
||||||
child: globalKeyedView,
|
child: globalKeyedView,
|
||||||
),
|
),
|
||||||
@ -155,9 +155,9 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('ViewAnchor cannot be used at the top of the widget tree (outside of View)', (WidgetTester tester) async {
|
testWidgets('ViewAnchor cannot be used at the top of the widget tree (outside of View)', (WidgetTester tester) async {
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: const ViewAnchor(
|
const ViewAnchor(
|
||||||
child: SizedBox(),
|
child: SizedBox(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -175,18 +175,18 @@ void main() {
|
|||||||
child: const SizedBox(),
|
child: const SizedBox(),
|
||||||
);
|
);
|
||||||
|
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: View(
|
View(
|
||||||
view: tester.view,
|
view: tester.view,
|
||||||
child: globalKeyedViewAnchor,
|
child: globalKeyedViewAnchor,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(tester.takeException(), isNull);
|
expect(tester.takeException(), isNull);
|
||||||
|
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: globalKeyedViewAnchor,
|
globalKeyedViewAnchor,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(tester.takeException(), isFlutterError.having(
|
expect(tester.takeException(), isFlutterError.having(
|
||||||
@ -197,9 +197,9 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('View can be used at the top of the widget tree', (WidgetTester tester) async {
|
testWidgets('View can be used at the top of the widget tree', (WidgetTester tester) async {
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: View(
|
View(
|
||||||
view: tester.view,
|
view: tester.view,
|
||||||
child: Container(),
|
child: Container(),
|
||||||
),
|
),
|
||||||
@ -214,9 +214,9 @@ void main() {
|
|||||||
child: const ColoredBox(color: Colors.red),
|
child: const ColoredBox(color: Colors.red),
|
||||||
);
|
);
|
||||||
|
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: View(
|
View(
|
||||||
view: tester.view,
|
view: tester.view,
|
||||||
child: ViewAnchor(
|
child: ViewAnchor(
|
||||||
view: globalKeyView, // This one has trouble when deactivating
|
view: globalKeyView, // This one has trouble when deactivating
|
||||||
@ -228,9 +228,9 @@ void main() {
|
|||||||
expect(find.byType(SizedBox), findsOneWidget);
|
expect(find.byType(SizedBox), findsOneWidget);
|
||||||
expect(find.byType(ColoredBox), findsOneWidget);
|
expect(find.byType(ColoredBox), findsOneWidget);
|
||||||
|
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: globalKeyView,
|
globalKeyView,
|
||||||
);
|
);
|
||||||
expect(tester.takeException(), isNull);
|
expect(tester.takeException(), isNull);
|
||||||
expect(find.byType(SizedBox), findsNothing);
|
expect(find.byType(SizedBox), findsNothing);
|
||||||
@ -238,9 +238,9 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('ViewCollection can be used at the top of the widget tree', (WidgetTester tester) async {
|
testWidgets('ViewCollection can be used at the top of the widget tree', (WidgetTester tester) async {
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: ViewCollection(
|
ViewCollection(
|
||||||
views: <Widget>[
|
views: <Widget>[
|
||||||
View(
|
View(
|
||||||
view: tester.view,
|
view: tester.view,
|
||||||
@ -291,9 +291,9 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('ViewCollection cannot have render object widgets as children', (WidgetTester tester) async {
|
testWidgets('ViewCollection cannot have render object widgets as children', (WidgetTester tester) async {
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: const ViewCollection(
|
const ViewCollection(
|
||||||
views: <Widget>[
|
views: <Widget>[
|
||||||
ColoredBox(color: Colors.red),
|
ColoredBox(color: Colors.red),
|
||||||
],
|
],
|
||||||
@ -319,9 +319,9 @@ void main() {
|
|||||||
child: const ColoredBox(color: Colors.red),
|
child: const ColoredBox(color: Colors.red),
|
||||||
);
|
);
|
||||||
|
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: ViewCollection(
|
ViewCollection(
|
||||||
views: <Widget>[
|
views: <Widget>[
|
||||||
greenView,
|
greenView,
|
||||||
ViewCollection(
|
ViewCollection(
|
||||||
@ -335,9 +335,9 @@ void main() {
|
|||||||
expect(tester.takeException(), isNull);
|
expect(tester.takeException(), isNull);
|
||||||
expect(find.byType(ColoredBox), findsNWidgets(2));
|
expect(find.byType(ColoredBox), findsNWidgets(2));
|
||||||
|
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: ViewCollection(
|
ViewCollection(
|
||||||
views: <Widget>[
|
views: <Widget>[
|
||||||
redView,
|
redView,
|
||||||
ViewCollection(
|
ViewCollection(
|
||||||
@ -371,9 +371,9 @@ void main() {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: ViewCollection(
|
ViewCollection(
|
||||||
views: <Widget>[
|
views: <Widget>[
|
||||||
View(
|
View(
|
||||||
view: greenView,
|
view: greenView,
|
||||||
@ -412,9 +412,9 @@ void main() {
|
|||||||
expect(leafRenderObject[redView.viewId], isNot(isA<RenderConstrainedBox>()));
|
expect(leafRenderObject[redView.viewId], isNot(isA<RenderConstrainedBox>()));
|
||||||
|
|
||||||
// Move the child.
|
// Move the child.
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: ViewCollection(
|
ViewCollection(
|
||||||
views: <Widget>[
|
views: <Widget>[
|
||||||
View(
|
View(
|
||||||
view: greenView,
|
view: greenView,
|
||||||
@ -476,9 +476,9 @@ void main() {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: ViewCollection(
|
ViewCollection(
|
||||||
views: <Widget>[
|
views: <Widget>[
|
||||||
View(
|
View(
|
||||||
view: greenView,
|
view: greenView,
|
||||||
@ -517,9 +517,9 @@ void main() {
|
|||||||
expect(leafRenderObject[greenView.viewId], isNot(isA<RenderConstrainedBox>()));
|
expect(leafRenderObject[greenView.viewId], isNot(isA<RenderConstrainedBox>()));
|
||||||
|
|
||||||
// Move the child.
|
// Move the child.
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: ViewCollection(
|
ViewCollection(
|
||||||
views: <Widget>[
|
views: <Widget>[
|
||||||
View(
|
View(
|
||||||
view: greenView,
|
view: greenView,
|
||||||
@ -571,9 +571,9 @@ void main() {
|
|||||||
key: GlobalKey(),
|
key: GlobalKey(),
|
||||||
);
|
);
|
||||||
|
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: ViewCollection(
|
ViewCollection(
|
||||||
views: <Widget>[
|
views: <Widget>[
|
||||||
View(
|
View(
|
||||||
key: greenKey,
|
key: greenKey,
|
||||||
@ -610,9 +610,9 @@ void main() {
|
|||||||
final RenderObject boxWithGlobalKey = tester.renderObject(find.byKey(globalKeyChild.key!));
|
final RenderObject boxWithGlobalKey = tester.renderObject(find.byKey(globalKeyChild.key!));
|
||||||
|
|
||||||
// Move the child and remove its view.
|
// Move the child and remove its view.
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: ViewCollection(
|
ViewCollection(
|
||||||
views: <Widget>[
|
views: <Widget>[
|
||||||
View(
|
View(
|
||||||
key: greenKey,
|
key: greenKey,
|
||||||
@ -652,9 +652,9 @@ void main() {
|
|||||||
key: GlobalKey(),
|
key: GlobalKey(),
|
||||||
);
|
);
|
||||||
|
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: ViewCollection(
|
ViewCollection(
|
||||||
views: <Widget>[
|
views: <Widget>[
|
||||||
View(
|
View(
|
||||||
key: greenKey,
|
key: greenKey,
|
||||||
@ -691,9 +691,9 @@ void main() {
|
|||||||
final RenderObject boxWithGlobalKey = tester.renderObject(find.byKey(globalKeyChild.key!));
|
final RenderObject boxWithGlobalKey = tester.renderObject(find.byKey(globalKeyChild.key!));
|
||||||
|
|
||||||
// Move the child and remove its view.
|
// Move the child and remove its view.
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: ViewCollection(
|
ViewCollection(
|
||||||
views: <Widget>[
|
views: <Widget>[
|
||||||
View(
|
View(
|
||||||
key: redKey,
|
key: redKey,
|
||||||
@ -1009,9 +1009,9 @@ void main() {
|
|||||||
final FlutterView redView = tester.view;
|
final FlutterView redView = tester.view;
|
||||||
final FlutterView greenView = FakeView(tester.view);
|
final FlutterView greenView = FakeView(tester.view);
|
||||||
|
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: ViewCollection(
|
ViewCollection(
|
||||||
views: <Widget>[
|
views: <Widget>[
|
||||||
View(
|
View(
|
||||||
view: redView,
|
view: redView,
|
||||||
@ -1029,9 +1029,9 @@ void main() {
|
|||||||
expect(findsColoredBox(Colors.red), findsOneWidget);
|
expect(findsColoredBox(Colors.red), findsOneWidget);
|
||||||
final RenderObject box = tester.renderObject(findsColoredBox(Colors.green));
|
final RenderObject box = tester.renderObject(findsColoredBox(Colors.green));
|
||||||
|
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: ViewCollection(
|
ViewCollection(
|
||||||
views: <Widget>[
|
views: <Widget>[
|
||||||
View(
|
View(
|
||||||
view: redView,
|
view: redView,
|
||||||
@ -1052,17 +1052,17 @@ void main() {
|
|||||||
child: const SizedBox(),
|
child: const SizedBox(),
|
||||||
);
|
);
|
||||||
|
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: view,
|
view,
|
||||||
);
|
);
|
||||||
|
|
||||||
final RenderObject renderView = tester.renderObject(find.byType(View));
|
final RenderObject renderView = tester.renderObject(find.byType(View));
|
||||||
final RenderObject renderSizedBox = tester.renderObject(find.byType(SizedBox));
|
final RenderObject renderSizedBox = tester.renderObject(find.byType(SizedBox));
|
||||||
|
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: ViewCollection(
|
ViewCollection(
|
||||||
views: <Widget>[view],
|
views: <Widget>[view],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -1070,9 +1070,9 @@ void main() {
|
|||||||
expect(tester.renderObject(find.byType(View)), same(renderView));
|
expect(tester.renderObject(find.byType(View)), same(renderView));
|
||||||
expect(tester.renderObject(find.byType(SizedBox)), same(renderSizedBox));
|
expect(tester.renderObject(find.byType(SizedBox)), same(renderSizedBox));
|
||||||
|
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: view,
|
view,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(tester.renderObject(find.byType(View)), same(renderView));
|
expect(tester.renderObject(find.byType(View)), same(renderView));
|
||||||
@ -1114,9 +1114,9 @@ void main() {
|
|||||||
child: const SizedBox(),
|
child: const SizedBox(),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: view,
|
view,
|
||||||
);
|
);
|
||||||
|
|
||||||
final RenderObject renderSemantics = tester.renderObject(find.bySemanticsLabel('Hello'));
|
final RenderObject renderSemantics = tester.renderObject(find.bySemanticsLabel('Hello'));
|
||||||
@ -1124,9 +1124,9 @@ void main() {
|
|||||||
expect(semantics.id, 1);
|
expect(semantics.id, 1);
|
||||||
expect(renderSemantics.debugSemantics, same(semantics));
|
expect(renderSemantics.debugSemantics, same(semantics));
|
||||||
|
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: ViewCollection(
|
ViewCollection(
|
||||||
views: <Widget>[
|
views: <Widget>[
|
||||||
view,
|
view,
|
||||||
],
|
],
|
||||||
@ -1144,9 +1144,3 @@ void main() {
|
|||||||
Finder findsColoredBox(Color color) {
|
Finder findsColoredBox(Color color) {
|
||||||
return find.byWidgetPredicate((Widget widget) => widget is ColoredBox && widget.color == color);
|
return find.byWidgetPredicate((Widget widget) => widget is ColoredBox && widget.color == color);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> pumpWidgetWithoutViewWrapper({required WidgetTester tester, required Widget widget}) {
|
|
||||||
tester.binding.attachRootWidget(widget);
|
|
||||||
tester.binding.scheduleFrame();
|
|
||||||
return tester.binding.pump();
|
|
||||||
}
|
|
||||||
|
@ -77,9 +77,9 @@ void main() {
|
|||||||
PipelineOwner? outsideParent;
|
PipelineOwner? outsideParent;
|
||||||
PipelineOwner? insideParent;
|
PipelineOwner? insideParent;
|
||||||
|
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: Builder(
|
Builder(
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
outsideView = View.maybeOf(context);
|
outsideView = View.maybeOf(context);
|
||||||
outsideParent = View.pipelineOwnerOf(context);
|
outsideParent = View.pipelineOwnerOf(context);
|
||||||
@ -114,9 +114,9 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('cannot have multiple views with same FlutterView', (WidgetTester tester) async {
|
testWidgets('cannot have multiple views with same FlutterView', (WidgetTester tester) async {
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: ViewCollection(
|
ViewCollection(
|
||||||
views: <Widget>[
|
views: <Widget>[
|
||||||
View(
|
View(
|
||||||
view: tester.view,
|
view: tester.view,
|
||||||
@ -224,9 +224,9 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('visitChildren of ViewCollection visits all children', (WidgetTester tester) async {
|
testWidgets('visitChildren of ViewCollection visits all children', (WidgetTester tester) async {
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: ViewCollection(
|
ViewCollection(
|
||||||
views: <Widget>[
|
views: <Widget>[
|
||||||
View(
|
View(
|
||||||
view: tester.view,
|
view: tester.view,
|
||||||
@ -250,9 +250,9 @@ void main() {
|
|||||||
});
|
});
|
||||||
expect(children, hasLength(3));
|
expect(children, hasLength(3));
|
||||||
|
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: ViewCollection(
|
ViewCollection(
|
||||||
views: <Widget>[
|
views: <Widget>[
|
||||||
View(
|
View(
|
||||||
view: tester.view,
|
view: tester.view,
|
||||||
@ -271,9 +271,9 @@ void main() {
|
|||||||
group('renderObject getter', () {
|
group('renderObject getter', () {
|
||||||
testWidgets('ancestors of view see RenderView as renderObject', (WidgetTester tester) async {
|
testWidgets('ancestors of view see RenderView as renderObject', (WidgetTester tester) async {
|
||||||
late BuildContext builderContext;
|
late BuildContext builderContext;
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: Builder(
|
Builder(
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
builderContext = context;
|
builderContext = context;
|
||||||
return View(
|
return View(
|
||||||
@ -293,9 +293,9 @@ void main() {
|
|||||||
|
|
||||||
testWidgets('ancestors of ViewCollection get null for renderObject', (WidgetTester tester) async {
|
testWidgets('ancestors of ViewCollection get null for renderObject', (WidgetTester tester) async {
|
||||||
late BuildContext builderContext;
|
late BuildContext builderContext;
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: Builder(
|
Builder(
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
builderContext = context;
|
builderContext = context;
|
||||||
return ViewCollection(
|
return ViewCollection(
|
||||||
@ -345,9 +345,9 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('correctly switches between view configurations', (WidgetTester tester) async {
|
testWidgets('correctly switches between view configurations', (WidgetTester tester) async {
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: View(
|
View(
|
||||||
view: tester.view,
|
view: tester.view,
|
||||||
deprecatedDoNotUseWillBeRemovedWithoutNoticePipelineOwner: tester.binding.pipelineOwner,
|
deprecatedDoNotUseWillBeRemovedWithoutNoticePipelineOwner: tester.binding.pipelineOwner,
|
||||||
deprecatedDoNotUseWillBeRemovedWithoutNoticeRenderView: tester.binding.renderView,
|
deprecatedDoNotUseWillBeRemovedWithoutNoticeRenderView: tester.binding.renderView,
|
||||||
@ -359,9 +359,9 @@ void main() {
|
|||||||
expect(renderView.owner, same(tester.binding.pipelineOwner));
|
expect(renderView.owner, same(tester.binding.pipelineOwner));
|
||||||
expect(tester.renderObject(find.byType(SizedBox)).owner, same(tester.binding.pipelineOwner));
|
expect(tester.renderObject(find.byType(SizedBox)).owner, same(tester.binding.pipelineOwner));
|
||||||
|
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: View(
|
View(
|
||||||
view: tester.view,
|
view: tester.view,
|
||||||
child: const SizedBox(),
|
child: const SizedBox(),
|
||||||
),
|
),
|
||||||
@ -371,9 +371,9 @@ void main() {
|
|||||||
expect(renderView.owner, isNot(same(tester.binding.pipelineOwner)));
|
expect(renderView.owner, isNot(same(tester.binding.pipelineOwner)));
|
||||||
expect(tester.renderObject(find.byType(SizedBox)).owner, isNot(same(tester.binding.pipelineOwner)));
|
expect(tester.renderObject(find.byType(SizedBox)).owner, isNot(same(tester.binding.pipelineOwner)));
|
||||||
|
|
||||||
await pumpWidgetWithoutViewWrapper(
|
await tester.pumpWidget(
|
||||||
tester: tester,
|
wrapWithView: false,
|
||||||
widget: View(
|
View(
|
||||||
view: tester.view,
|
view: tester.view,
|
||||||
deprecatedDoNotUseWillBeRemovedWithoutNoticePipelineOwner: tester.binding.pipelineOwner,
|
deprecatedDoNotUseWillBeRemovedWithoutNoticePipelineOwner: tester.binding.pipelineOwner,
|
||||||
deprecatedDoNotUseWillBeRemovedWithoutNoticeRenderView: tester.binding.renderView,
|
deprecatedDoNotUseWillBeRemovedWithoutNoticeRenderView: tester.binding.renderView,
|
||||||
@ -513,12 +513,6 @@ void main() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> pumpWidgetWithoutViewWrapper({required WidgetTester tester, required Widget widget}) {
|
|
||||||
tester.binding.attachRootWidget(widget);
|
|
||||||
tester.binding.scheduleFrame();
|
|
||||||
return tester.binding.pump();
|
|
||||||
}
|
|
||||||
|
|
||||||
class SpyRenderWidget extends SizedBox {
|
class SpyRenderWidget extends SizedBox {
|
||||||
const SpyRenderWidget({super.key, required this.label, required this.log, super.child});
|
const SpyRenderWidget({super.key, required this.label, required this.log, super.child});
|
||||||
|
|
||||||
|
@ -581,15 +581,23 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
|
|||||||
/// ```
|
/// ```
|
||||||
/// {@end-tool}
|
/// {@end-tool}
|
||||||
///
|
///
|
||||||
|
/// By default, the provided `widget` is rendered into [WidgetTester.view],
|
||||||
|
/// whose properties tests can modify to simulate different scenarios (e.g.
|
||||||
|
/// running on a large/small screen). Tests that want to control the
|
||||||
|
/// [FlutterView] into which content is rendered can set `wrapWithView` to
|
||||||
|
/// false and use [View] widgets in the provided `widget` tree to specify the
|
||||||
|
/// desired [FlutterView]s.
|
||||||
|
///
|
||||||
/// See also [LiveTestWidgetsFlutterBindingFramePolicy], which affects how
|
/// See also [LiveTestWidgetsFlutterBindingFramePolicy], which affects how
|
||||||
/// this method works when the test is run with `flutter run`.
|
/// this method works when the test is run with `flutter run`.
|
||||||
Future<void> pumpWidget(
|
Future<void> pumpWidget(
|
||||||
Widget widget, {
|
Widget widget, {
|
||||||
Duration? duration,
|
Duration? duration,
|
||||||
EnginePhase phase = EnginePhase.sendSemanticsUpdate,
|
EnginePhase phase = EnginePhase.sendSemanticsUpdate,
|
||||||
|
bool wrapWithView = true,
|
||||||
}) {
|
}) {
|
||||||
return TestAsyncUtils.guard<void>(() {
|
return TestAsyncUtils.guard<void>(() {
|
||||||
binding.attachRootWidget(binding.wrapWithDefaultView(widget));
|
binding.attachRootWidget(wrapWithView ? binding.wrapWithDefaultView(widget) : widget);
|
||||||
binding.scheduleFrame();
|
binding.scheduleFrame();
|
||||||
return binding.pump(duration, phase);
|
return binding.pump(duration, phase);
|
||||||
});
|
});
|
||||||
|
@ -112,7 +112,8 @@ Future<void> pumpViews({required WidgetTester tester, required List<Widget> vie
|
|||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
tester.binding.attachRootWidget(
|
return tester.pumpWidget(
|
||||||
|
wrapWithView: false,
|
||||||
Directionality(
|
Directionality(
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
child: ViewCollection(
|
child: ViewCollection(
|
||||||
@ -120,6 +121,4 @@ Future<void> pumpViews({required WidgetTester tester, required List<Widget> vie
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
tester.binding.scheduleFrame();
|
|
||||||
return tester.binding.pump();
|
|
||||||
}
|
}
|
||||||
|
@ -208,7 +208,8 @@ Future<void> pumpViews({required WidgetTester tester}) {
|
|||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
tester.binding.attachRootWidget(
|
return tester.pumpWidget(
|
||||||
|
wrapWithView: false,
|
||||||
Directionality(
|
Directionality(
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
child: ViewCollection(
|
child: ViewCollection(
|
||||||
@ -216,6 +217,4 @@ Future<void> pumpViews({required WidgetTester tester}) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
tester.binding.scheduleFrame();
|
|
||||||
return tester.binding.pump();
|
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,8 @@ import 'package:flutter_test/flutter_test.dart';
|
|||||||
import 'package:matcher/expect.dart' as matcher;
|
import 'package:matcher/expect.dart' as matcher;
|
||||||
import 'package:matcher/src/expect/async_matcher.dart';
|
import 'package:matcher/src/expect/async_matcher.dart';
|
||||||
|
|
||||||
|
import 'multi_view_testing.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('expectLater', () {
|
group('expectLater', () {
|
||||||
testWidgets('completes when matcher completes', (WidgetTester tester) async {
|
testWidgets('completes when matcher completes', (WidgetTester tester) async {
|
||||||
@ -638,6 +640,56 @@ void main() {
|
|||||||
.checkMockMessageHandler(SystemChannels.accessibility.name, null), isTrue);
|
.checkMockMessageHandler(SystemChannels.accessibility.name, null), isTrue);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('wrapWithView: false does not include View', (WidgetTester tester) async {
|
||||||
|
FlutterView? flutterView;
|
||||||
|
View? view;
|
||||||
|
int builderCount = 0;
|
||||||
|
await tester.pumpWidget(
|
||||||
|
wrapWithView: false,
|
||||||
|
Builder(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
builderCount++;
|
||||||
|
flutterView = View.maybeOf(context);
|
||||||
|
view = context.findAncestorWidgetOfExactType<View>();
|
||||||
|
return const ViewCollection(views: <Widget>[]);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(builderCount, 1);
|
||||||
|
expect(view, isNull);
|
||||||
|
expect(flutterView, isNull);
|
||||||
|
expect(find.byType(View), findsNothing);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('passing a view to pumpWidget with wrapWithView: true throws', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
View(
|
||||||
|
view: FakeView(tester.view),
|
||||||
|
child: const SizedBox.shrink(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
tester.takeException(),
|
||||||
|
isFlutterError.having(
|
||||||
|
(FlutterError e) => e.message,
|
||||||
|
'message',
|
||||||
|
contains('consider setting the "wrapWithView" parameter of that method to false'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('can pass a View to pumpWidget when wrapWithView: false', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
wrapWithView: false,
|
||||||
|
View(
|
||||||
|
view: tester.view,
|
||||||
|
child: const SizedBox.shrink(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(find.byType(View), findsOne);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class FakeMatcher extends AsyncMatcher {
|
class FakeMatcher extends AsyncMatcher {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user