Revert "Inject current FlutterView into tree and make available via View.of(context) (#116924)" (#117214)

This reverts commit a34e419484.
This commit is contained in:
Casey Hillers 2022-12-16 09:48:23 -08:00 committed by GitHub
parent 23a2fa31d2
commit 9102f2fe0b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 108 additions and 352 deletions

View file

@ -19,7 +19,6 @@ import 'framework.dart';
import 'platform_menu_bar.dart';
import 'router.dart';
import 'service_extensions.dart';
import 'view.dart';
import 'widget_inspector.dart';
export 'dart:ui' show AppLifecycleState, Locale;
@ -897,22 +896,6 @@ mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureB
@override
bool get framesEnabled => super.framesEnabled && _readyToProduceFrames;
/// Used by [runApp] to wrap the provided `rootWidget` in the default [View].
///
/// The [View] determines into what [FlutterView] the app is rendered into.
/// For backwards-compatibility reasons, this method currently chooses
/// [window] (which is a [FlutterView]) as the rendering target. This will
/// change in a future version of Flutter.
///
/// The `rootWidget` widget provided to this method must not already be
/// wrapped in a [View].
Widget wrapWithDefaultView(Widget rootWidget) {
return View(
view: window,
child: rootWidget,
);
}
/// Schedules a [Timer] for attaching the root widget.
///
/// This is called by [runApp] to configure the widget tree. Consider using
@ -1031,9 +1014,8 @@ mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureB
/// * [WidgetsBinding.handleBeginFrame], which pumps the widget pipeline to
/// ensure the widget, element, and render trees are all built.
void runApp(Widget app) {
final WidgetsBinding binding = WidgetsFlutterBinding.ensureInitialized();
binding
..scheduleAttachRootWidget(binding.wrapWithDefaultView(app))
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}

View file

@ -1,94 +0,0 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui' show FlutterView;
import 'framework.dart';
import 'lookup_boundary.dart';
/// Injects a [FlutterView] into the tree and makes it available to descendants
/// within the same [LookupBoundary] via [View.of] and [View.maybeOf].
///
/// In a future version of Flutter, the functionality of this widget will be
/// extended to actually bootstrap the render tree that is going to be rendered
/// into the provided [view]. This will enable rendering content into multiple
/// [FlutterView]s from a single widget tree.
///
/// Each [FlutterView] can be associated with at most one [View] widget in the
/// widget tree. Two or more [View] widgets configured with the same
/// [FlutterView] must never exist within the same widget tree at the same time.
/// Internally, this limitation is enforced by a [GlobalObjectKey] that derives
/// its identity from the [view] provided to this widget.
class View extends InheritedWidget {
/// Injects the provided [view] into the widget tree.
View({required this.view, required super.child}) : super(key: GlobalObjectKey(view));
/// The [FlutterView] to be injected into the tree.
final FlutterView view;
@override
bool updateShouldNotify(View oldWidget) => view != oldWidget.view;
/// Returns the [FlutterView] that the provided `context` will render into.
///
/// Returns null if the `context` is not associated with a [FlutterView].
///
/// The method creates a dependency on the `context`, which will be informed
/// when the identity of the [FlutterView] changes (i.e. the `context` is
/// moved to render into a different [FlutterView] then before). The context
/// will not be informed when the properties on the [FlutterView] itself
/// change their values. To access the property values of a [FlutterView] it
/// is best practise to use [MediaQuery.maybeOf] instead, which will ensure
/// that the `context` is informed when the view properties change.
///
/// See also:
///
/// * [View.of], which throws instead of returning null if no [FlutterView]
/// is found.
static FlutterView? maybeOf(BuildContext context) {
return LookupBoundary.dependOnInheritedWidgetOfExactType<View>(context)?.view;
}
/// Returns the [FlutterView] that the provided `context` will render into.
///
/// Throws if the `context` is not associated with a [FlutterView].
///
/// The method creates a dependency on the `context`, which will be informed
/// when the identity of the [FlutterView] changes (i.e. the `context` is
/// moved to render into a different [FlutterView] then before). The context
/// will not be informed when the properties on the [FlutterView] itself
/// change their values. To access the property values of a [FlutterView] it
/// is best practise to use [MediaQuery.of] instead, which will ensure that
/// the `context` is informed when the view properties change.
///
/// See also:
///
/// * [View.maybeOf], which throws instead of returning null if no
/// [FlutterView] is found.
static FlutterView of(BuildContext context) {
final FlutterView? result = maybeOf(context);
assert(() {
if (result == null) {
final bool hiddenByBoundary = LookupBoundary.debugIsHidingAncestorWidgetOfExactType<View>(context);
final List<DiagnosticsNode> information = <DiagnosticsNode>[
if (hiddenByBoundary) ...<DiagnosticsNode>[
ErrorSummary('View.of() was called with a context that does not have access to a View widget.'),
ErrorDescription('The context provided to View.of() does have a View widget ancestor, but it is hidden by a LookupBoundary.'),
] else ...<DiagnosticsNode>[
ErrorSummary('View.of() was called with a context that does not contain a View widget.'),
ErrorDescription('No View widget ancestor could be found starting from the context that was passed to View.of().'),
],
ErrorDescription(
'The context used was:\n'
' $context',
),
ErrorHint('This usually means that the provided context is not associated with a View.'),
];
throw FlutterError.fromParts(information);
}
return true;
}());
return result!;
}
}

View file

@ -1,8 +0,0 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/// Placeholder to be used in a future version of Flutter.
abstract class Window {
const Window._();
}

View file

@ -148,11 +148,8 @@ export 'src/widgets/transitions.dart';
export 'src/widgets/tween_animation_builder.dart';
export 'src/widgets/unique_widget.dart';
export 'src/widgets/value_listenable_builder.dart';
// TODO(goderbauer): Enable once clean-up in google3 is done.
// export 'src/widgets/view.dart';
export 'src/widgets/viewport.dart';
export 'src/widgets/visibility.dart';
export 'src/widgets/widget_inspector.dart';
export 'src/widgets/widget_span.dart';
export 'src/widgets/will_pop_scope.dart';
export 'src/widgets/window.dart';

View file

@ -8,7 +8,7 @@ import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('debugCheckHasMaterial control test', (WidgetTester tester) async {
await tester.pumpWidget(const Center(child: Chip(label: Text('label'))));
await tester.pumpWidget(const Chip(label: Text('label')));
final dynamic exception = tester.takeException();
expect(exception, isFlutterError);
final FlutterError error = exception as FlutterError;
@ -25,7 +25,7 @@ void main() {
expect(error.diagnostics[3], isA<DiagnosticsProperty<Element>>());
expect(error.diagnostics[4], isA<DiagnosticsBlock>());
expect(
error.toStringDeep(), startsWith(
error.toStringDeep(),
'FlutterError\n'
' No Material widget found.\n'
' Chip widgets require a Material widget ancestor within the\n'
@ -42,13 +42,12 @@ void main() {
' The specific widget that could not find a Material ancestor was:\n'
' Chip\n'
' The ancestors of this widget were:\n'
' Center\n'
// End of ancestor chain omitted, not relevant for test.
));
' [root]\n',
);
});
testWidgets('debugCheckHasMaterialLocalizations control test', (WidgetTester tester) async {
await tester.pumpWidget(const Center(child: BackButton()));
await tester.pumpWidget(const BackButton());
final dynamic exception = tester.takeException();
expect(exception, isFlutterError);
final FlutterError error = exception as FlutterError;
@ -65,7 +64,7 @@ void main() {
expect(error.diagnostics[4], isA<DiagnosticsProperty<Element>>());
expect(error.diagnostics[5], isA<DiagnosticsBlock>());
expect(
error.toStringDeep(), startsWith(
error.toStringDeep(),
'FlutterError\n'
' No MaterialLocalizations found.\n'
' BackButton widgets require MaterialLocalizations to be provided\n'
@ -79,9 +78,8 @@ void main() {
' ancestor was:\n'
' BackButton\n'
' The ancestors of this widget were:\n'
' Center\n'
// End of ancestor chain omitted, not relevant for test.
));
' [root]\n',
);
});
testWidgets('debugCheckHasScaffold control test', (WidgetTester tester) async {
@ -235,7 +233,6 @@ void main() {
' HeroControllerScope\n'
' ScrollConfiguration\n'
' MaterialApp\n'
' View-[GlobalObjectKey TestWindow#00000]\n'
' [root]\n'
' Typically, the Scaffold widget is introduced by the MaterialApp\n'
' or WidgetsApp widget at the top of your application widget tree.\n'
@ -380,7 +377,6 @@ void main() {
' Scaffold-[LabeledGlobalKey<ScaffoldState>#00000]\n'
' MediaQuery\n'
' Directionality\n'
' View-[GlobalObjectKey TestWindow#00000]\n'
' [root]\n'
' Typically, the ScaffoldMessenger widget is introduced by the\n'
' MaterialApp at the top of your application widget tree.\n'

View file

@ -2458,7 +2458,6 @@ void main() {
' Scaffold\n'
' MediaQuery\n'
' Directionality\n'
' View-[GlobalObjectKey TestWindow#e6136]\n'
' [root]\n'
' Typically, the ScaffoldMessenger widget is introduced by the\n'
' MaterialApp at the top of your application widget tree.\n',

View file

@ -7431,6 +7431,7 @@ void main() {
final dynamic exception = tester.takeException();
expect(exception, isFlutterError);
expect(exception.toString(), startsWith('No Material widget found.'));
expect(exception.toString(), endsWith(':\n $textField\nThe ancestors of this widget were:\n [root]'));
});
testWidgets('TextField loses focus when disabled', (WidgetTester tester) async {

View file

@ -122,16 +122,14 @@ void main() {
box.toStringDeep(),
equalsIgnoringHashCodes(
'RenderPadding#00000 relayoutBoundary=up1\n'
' │ creator: Padding ← Container ← Align ← View-[GlobalObjectKey\n'
' │ TestWindow#00000] ← [root]\n'
' │ creator: Padding ← Container ← Align ← [root]\n'
' │ parentData: offset=Offset(0.0, 0.0) (can use size)\n'
' │ constraints: BoxConstraints(0.0<=w<=800.0, 0.0<=h<=600.0)\n'
' │ size: Size(63.0, 88.0)\n'
' │ padding: EdgeInsets.all(5.0)\n'
'\n'
' └─child: RenderConstrainedBox#00000 relayoutBoundary=up2\n'
' │ creator: ConstrainedBox ← Padding ← Container ← Align ←\n'
' │ View-[GlobalObjectKey TestWindow#00000] ← [root]\n'
' │ creator: ConstrainedBox ← Padding ← Container ← Align ← [root]\n'
' │ parentData: offset=Offset(5.0, 5.0) (can use size)\n'
' │ constraints: BoxConstraints(0.0<=w<=790.0, 0.0<=h<=590.0)\n'
' │ size: Size(53.0, 78.0)\n'
@ -139,7 +137,7 @@ void main() {
'\n'
' └─child: RenderDecoratedBox#00000\n'
' │ creator: DecoratedBox ← ConstrainedBox ← Padding ← Container ←\n'
' │ Align ← View-[GlobalObjectKey TestWindow#00000] ← [root]\n'
' │ Align ← [root]\n'
' │ parentData: <none> (can use size)\n'
' │ constraints: BoxConstraints(w=53.0, h=78.0)\n'
' │ size: Size(53.0, 78.0)\n'
@ -151,8 +149,7 @@ void main() {
'\n'
' └─child: _RenderColoredBox#00000\n'
' │ creator: ColoredBox ← DecoratedBox ← ConstrainedBox ← Padding ←\n'
' │ Container ← Align ← View-[GlobalObjectKey TestWindow#00000] ←\n'
' │ [root]\n'
' │ Container ← Align ← [root]\n'
' │ parentData: <none> (can use size)\n'
' │ constraints: BoxConstraints(w=53.0, h=78.0)\n'
' │ size: Size(53.0, 78.0)\n'
@ -160,8 +157,7 @@ void main() {
'\n'
' └─child: RenderPadding#00000\n'
' │ creator: Padding ← ColoredBox ← DecoratedBox ← ConstrainedBox ←\n'
' │ Padding ← Container ← Align ← View-[GlobalObjectKey\n'
' │ TestWindow#00000] ← [root]\n'
' │ Padding ← Container ← Align ← [root]\n'
' │ parentData: <none> (can use size)\n'
' │ constraints: BoxConstraints(w=53.0, h=78.0)\n'
' │ size: Size(53.0, 78.0)\n'
@ -169,8 +165,7 @@ void main() {
'\n'
' └─child: RenderPositionedBox#00000\n'
' │ creator: Align ← Padding ← ColoredBox ← DecoratedBox ←\n'
' │ ConstrainedBox ← Padding ← Container ← Align ←\n'
' │ View-[GlobalObjectKey TestWindow#00000] ← [root]\n'
' │ ConstrainedBox ← Padding ← Container ← Align ← [root]\n'
' │ parentData: offset=Offset(7.0, 7.0) (can use size)\n'
' │ constraints: BoxConstraints(w=39.0, h=64.0)\n'
' │ size: Size(39.0, 64.0)\n'
@ -180,8 +175,7 @@ void main() {
'\n'
' └─child: RenderConstrainedBox#00000 relayoutBoundary=up1\n'
' │ creator: SizedBox ← Align ← Padding ← ColoredBox ← DecoratedBox ←\n'
' │ ConstrainedBox ← Padding ← Container ← Align ←\n'
' │ View-[GlobalObjectKey TestWindow#00000] ← [root]\n'
' │ ConstrainedBox ← Padding ← Container ← Align ← [root]\n'
' │ parentData: offset=Offset(14.0, 31.0) (can use size)\n'
' │ constraints: BoxConstraints(0.0<=w<=39.0, 0.0<=h<=64.0)\n'
' │ size: Size(25.0, 33.0)\n'
@ -190,7 +184,7 @@ void main() {
' └─child: RenderDecoratedBox#00000\n'
' creator: DecoratedBox ← SizedBox ← Align ← Padding ← ColoredBox ←\n'
' DecoratedBox ← ConstrainedBox ← Padding ← Container ← Align ←\n'
' View-[GlobalObjectKey TestWindow#00000] ← [root]\n'
' [root]\n'
' parentData: <none> (can use size)\n'
' constraints: BoxConstraints(w=25.0, h=33.0)\n'
' size: Size(25.0, 33.0)\n'
@ -198,7 +192,7 @@ void main() {
' color: Color(0xffffff00)\n'
' configuration: ImageConfiguration(bundle:\n'
' PlatformAssetBundle#00000(), devicePixelRatio: 1.0, platform:\n'
' android)\n'
' android)\n',
),
);
@ -206,8 +200,7 @@ void main() {
box.toStringDeep(minLevel: DiagnosticLevel.fine),
equalsIgnoringHashCodes(
'RenderPadding#00000 relayoutBoundary=up1\n'
' │ creator: Padding ← Container ← Align ← View-[GlobalObjectKey\n'
' │ TestWindow#00000] ← [root]\n'
' │ creator: Padding ← Container ← Align ← [root]\n'
' │ parentData: offset=Offset(0.0, 0.0) (can use size)\n'
' │ constraints: BoxConstraints(0.0<=w<=800.0, 0.0<=h<=600.0)\n'
' │ layer: null\n'
@ -217,8 +210,7 @@ void main() {
' │ textDirection: null\n'
'\n'
' └─child: RenderConstrainedBox#00000 relayoutBoundary=up2\n'
' │ creator: ConstrainedBox ← Padding ← Container ← Align ←\n'
' │ View-[GlobalObjectKey TestWindow#00000] ← [root]\n'
' │ creator: ConstrainedBox ← Padding ← Container ← Align ← [root]\n'
' │ parentData: offset=Offset(5.0, 5.0) (can use size)\n'
' │ constraints: BoxConstraints(0.0<=w<=790.0, 0.0<=h<=590.0)\n'
' │ layer: null\n'
@ -228,7 +220,7 @@ void main() {
'\n'
' └─child: RenderDecoratedBox#00000\n'
' │ creator: DecoratedBox ← ConstrainedBox ← Padding ← Container ←\n'
' │ Align ← View-[GlobalObjectKey TestWindow#00000] ← [root]\n'
' │ Align ← [root]\n'
' │ parentData: <none> (can use size)\n'
' │ constraints: BoxConstraints(w=53.0, h=78.0)\n'
' │ layer: null\n'
@ -248,8 +240,7 @@ void main() {
'\n'
' └─child: _RenderColoredBox#00000\n'
' │ creator: ColoredBox ← DecoratedBox ← ConstrainedBox ← Padding ←\n'
' │ Container ← Align ← View-[GlobalObjectKey TestWindow#00000] ←\n'
' │ [root]\n'
' │ Container ← Align ← [root]\n'
' │ parentData: <none> (can use size)\n'
' │ constraints: BoxConstraints(w=53.0, h=78.0)\n'
' │ layer: null\n'
@ -259,8 +250,7 @@ void main() {
'\n'
' └─child: RenderPadding#00000\n'
' │ creator: Padding ← ColoredBox ← DecoratedBox ← ConstrainedBox ←\n'
' │ Padding ← Container ← Align ← View-[GlobalObjectKey\n'
' │ TestWindow#00000] ← [root]\n'
' │ Padding ← Container ← Align ← [root]\n'
' │ parentData: <none> (can use size)\n'
' │ constraints: BoxConstraints(w=53.0, h=78.0)\n'
' │ layer: null\n'
@ -271,8 +261,7 @@ void main() {
'\n'
' └─child: RenderPositionedBox#00000\n'
' │ creator: Align ← Padding ← ColoredBox ← DecoratedBox ←\n'
' │ ConstrainedBox ← Padding ← Container ← Align ←\n'
' │ View-[GlobalObjectKey TestWindow#00000] ← [root]\n'
' │ ConstrainedBox ← Padding ← Container ← Align ← [root]\n'
' │ parentData: offset=Offset(7.0, 7.0) (can use size)\n'
' │ constraints: BoxConstraints(w=39.0, h=64.0)\n'
' │ layer: null\n'
@ -285,8 +274,7 @@ void main() {
'\n'
' └─child: RenderConstrainedBox#00000 relayoutBoundary=up1\n'
' │ creator: SizedBox ← Align ← Padding ← ColoredBox ← DecoratedBox ←\n'
' │ ConstrainedBox ← Padding ← Container ← Align ←\n'
' │ View-[GlobalObjectKey TestWindow#00000] ← [root]\n'
' │ ConstrainedBox ← Padding ← Container ← Align ← [root]\n'
' │ parentData: offset=Offset(14.0, 31.0) (can use size)\n'
' │ constraints: BoxConstraints(0.0<=w<=39.0, 0.0<=h<=64.0)\n'
' │ layer: null\n'
@ -297,7 +285,7 @@ void main() {
' └─child: RenderDecoratedBox#00000\n'
' creator: DecoratedBox ← SizedBox ← Align ← Padding ← ColoredBox ←\n'
' DecoratedBox ← ConstrainedBox ← Padding ← Container ← Align ←\n'
' View-[GlobalObjectKey TestWindow#00000] ← [root]\n'
' [root]\n'
' parentData: <none> (can use size)\n'
' constraints: BoxConstraints(w=25.0, h=33.0)\n'
' layer: null\n'
@ -313,7 +301,7 @@ void main() {
' shape: rectangle\n'
' configuration: ImageConfiguration(bundle:\n'
' PlatformAssetBundle#00000(), devicePixelRatio: 1.0, platform:\n'
' android)\n'
' android)\n',
),
);
@ -322,8 +310,7 @@ void main() {
equalsIgnoringHashCodes(
'RenderPadding#00000 relayoutBoundary=up1\n'
' │ needsCompositing: false\n'
' │ creator: Padding ← Container ← Align ← View-[GlobalObjectKey\n'
' │ TestWindow#00000] ← [root]\n'
' │ creator: Padding ← Container ← Align ← [root]\n'
' │ parentData: offset=Offset(0.0, 0.0) (can use size)\n'
' │ constraints: BoxConstraints(0.0<=w<=800.0, 0.0<=h<=600.0)\n'
' │ layer: null\n'
@ -336,8 +323,7 @@ void main() {
'\n'
' └─child: RenderConstrainedBox#00000 relayoutBoundary=up2\n'
' │ needsCompositing: false\n'
' │ creator: ConstrainedBox ← Padding ← Container ← Align ←\n'
' │ View-[GlobalObjectKey TestWindow#00000] ← [root]\n'
' │ creator: ConstrainedBox ← Padding ← Container ← Align ← [root]\n'
' │ parentData: offset=Offset(5.0, 5.0) (can use size)\n'
' │ constraints: BoxConstraints(0.0<=w<=790.0, 0.0<=h<=590.0)\n'
' │ layer: null\n'
@ -350,7 +336,7 @@ void main() {
' └─child: RenderDecoratedBox#00000\n'
' │ needsCompositing: false\n'
' │ creator: DecoratedBox ← ConstrainedBox ← Padding ← Container ←\n'
' │ Align ← View-[GlobalObjectKey TestWindow#00000] ← [root]\n'
' │ Align ← [root]\n'
' │ parentData: <none> (can use size)\n'
' │ constraints: BoxConstraints(w=53.0, h=78.0)\n'
' │ layer: null\n'
@ -373,8 +359,7 @@ void main() {
' └─child: _RenderColoredBox#00000\n'
' │ needsCompositing: false\n'
' │ creator: ColoredBox ← DecoratedBox ← ConstrainedBox ← Padding ←\n'
' │ Container ← Align ← View-[GlobalObjectKey TestWindow#00000] ←\n'
' │ [root]\n'
' │ Container ← Align ← [root]\n'
' │ parentData: <none> (can use size)\n'
' │ constraints: BoxConstraints(w=53.0, h=78.0)\n'
' │ layer: null\n'
@ -387,8 +372,7 @@ void main() {
' └─child: RenderPadding#00000\n'
' │ needsCompositing: false\n'
' │ creator: Padding ← ColoredBox ← DecoratedBox ← ConstrainedBox ←\n'
' │ Padding ← Container ← Align ← View-[GlobalObjectKey\n'
' │ TestWindow#00000] ← [root]\n'
' │ Padding ← Container ← Align ← [root]\n'
' │ parentData: <none> (can use size)\n'
' │ constraints: BoxConstraints(w=53.0, h=78.0)\n'
' │ layer: null\n'
@ -402,8 +386,7 @@ void main() {
' └─child: RenderPositionedBox#00000\n'
' │ needsCompositing: false\n'
' │ creator: Align ← Padding ← ColoredBox ← DecoratedBox ←\n'
' │ ConstrainedBox ← Padding ← Container ← Align ←\n'
' │ View-[GlobalObjectKey TestWindow#00000] ← [root]\n'
' │ ConstrainedBox ← Padding ← Container ← Align ← [root]\n'
' │ parentData: offset=Offset(7.0, 7.0) (can use size)\n'
' │ constraints: BoxConstraints(w=39.0, h=64.0)\n'
' │ layer: null\n'
@ -419,8 +402,7 @@ void main() {
' └─child: RenderConstrainedBox#00000 relayoutBoundary=up1\n'
' │ needsCompositing: false\n'
' │ creator: SizedBox ← Align ← Padding ← ColoredBox ← DecoratedBox ←\n'
' │ ConstrainedBox ← Padding ← Container ← Align ←\n'
' │ View-[GlobalObjectKey TestWindow#00000] ← [root]\n'
' │ ConstrainedBox ← Padding ← Container ← Align ← [root]\n'
' │ parentData: offset=Offset(14.0, 31.0) (can use size)\n'
' │ constraints: BoxConstraints(0.0<=w<=39.0, 0.0<=h<=64.0)\n'
' │ layer: null\n'
@ -434,7 +416,7 @@ void main() {
' needsCompositing: false\n'
' creator: DecoratedBox ← SizedBox ← Align ← Padding ← ColoredBox ←\n'
' DecoratedBox ← ConstrainedBox ← Padding ← Container ← Align ←\n'
' View-[GlobalObjectKey TestWindow#00000] ← [root]\n'
' [root]\n'
' parentData: <none> (can use size)\n'
' constraints: BoxConstraints(w=25.0, h=33.0)\n'
' layer: null\n'
@ -452,7 +434,7 @@ void main() {
' shape: rectangle\n'
' configuration: ImageConfiguration(bundle:\n'
' PlatformAssetBundle#00000(), devicePixelRatio: 1.0, platform:\n'
' android)\n'
' android)\n',
),
);

View file

@ -372,8 +372,7 @@ void main() {
' in its parent data.\n'
' The following child has no ID: RenderConstrainedBox#00000 NEEDS-LAYOUT NEEDS-PAINT:\n'
' creator: ConstrainedBox ← Container ← LayoutWithMissingId ←\n'
' CustomMultiChildLayout ← Center ← View-[GlobalObjectKey\n'
' TestWindow#00000] ← [root]\n'
' CustomMultiChildLayout ← Center ← [root]\n'
' parentData: offset=Offset(0.0, 0.0); id=null\n'
' constraints: MISSING\n'
' size: MISSING\n'

View file

@ -79,13 +79,14 @@ void main() {
expect(error.diagnostics[2], isA<DiagnosticsProperty<Element>>());
expect(
error.toStringDeep(),
startsWith(
equalsIgnoringHashCodes(
'FlutterError\n'
' No Table widget found.\n'
' Builder widgets require a Table widget ancestor.\n'
' The specific widget that could not find a Table ancestor was:\n'
' Builder\n'
' The ownership chain for the affected widget is: "Builder ←', // End of ownership chain omitted, not relevant for test.
' The ownership chain for the affected widget is: "Builder ←\n'
' [root]"\n',
),
);
}
@ -121,20 +122,15 @@ void main() {
);
expect(
error.toStringDeep(),
startsWith(
equalsIgnoringHashCodes(
'FlutterError\n'
' No MediaQuery widget ancestor found.\n'
' Builder widgets require a MediaQuery widget ancestor.\n'
' The specific widget that could not find a MediaQuery ancestor\n'
' was:\n'
' Builder\n'
' The ownership chain for the affected widget is: "Builder ←' // Full chain omitted, not relevant for test.
),
);
expect(
error.toStringDeep(),
endsWith(
'[root]"\n' // End of ownership chain.
' The ownership chain for the affected widget is: "Builder ←\n'
' [root]"\n'
' No MediaQuery ancestor could be found starting from the context\n'
' that was passed to MediaQuery.of(). This can happen because you\n'
' have not added a WidgetsApp, CupertinoApp, or MaterialApp widget\n'

View file

@ -1228,8 +1228,7 @@ void main() {
equalsIgnoringHashCodes(
'FocusManager#00000\n'
' │ primaryFocus: FocusNode#00000(Child 4 [PRIMARY FOCUS])\n'
' │ primaryFocusCreator: Container-[GlobalKey#00000] ←\n'
' │ View-[GlobalObjectKey TestWindow#00000] ← [root]\n'
' │ primaryFocusCreator: Container-[GlobalKey#00000] ← [root]\n'
'\n'
' └─rootScope: FocusScopeNode#00000(Root Focus Scope [IN FOCUS PATH])\n'
' │ IN FOCUS PATH\n'
@ -1267,6 +1266,7 @@ void main() {
});
});
group('Autofocus', () {
testWidgets(
'works when the previous focused node is detached',
@ -1695,6 +1695,7 @@ void main() {
debugPrint = oldDebugPrint;
}
final String messagesStr = messages.toString();
expect(messagesStr.split('\n').length, equals(58));
expect(messagesStr, contains(RegExp(r' └─Child 1: FocusScopeNode#[a-f0-9]{5}\(parent1 \[PRIMARY FOCUS\]\)')));
expect(messagesStr, contains('FOCUS: Notified 2 dirty nodes'));
expect(messagesStr, contains(RegExp(r'FOCUS: Scheduling update, current focus is null, next focus will be FocusScopeNode#.*parent1')));

View file

@ -1183,13 +1183,13 @@ void main() {
expect(exception, isFlutterError);
expect(
exception.toString(),
startsWith(
equalsIgnoringHashCodes(
'The children of `MultiChildRenderObjectElement` must each has an associated render object.\n'
'This typically means that the `_EmptyWidget` or its children\n'
'are not a subtype of `RenderObjectWidget`.\n'
'The following element does not have an associated render object:\n'
' _EmptyWidget\n'
'debugCreator: _EmptyWidget ← Column ← ', // Omitted end of debugCreator chain because it's irrelevant for test.
'debugCreator: _EmptyWidget ← Column ← [root]',
),
);
});
@ -1216,13 +1216,13 @@ void main() {
expect(exception, isFlutterError);
expect(
exception.toString(),
startsWith(
equalsIgnoringHashCodes(
'The children of `MultiChildRenderObjectElement` must each has an associated render object.\n'
'This typically means that the `_EmptyWidget` or its children\n'
'are not a subtype of `RenderObjectWidget`.\n'
'The following element does not have an associated render object:\n'
' _EmptyWidget\n'
'debugCreator: _EmptyWidget ← Column ← ', // Omitted end of debugCreator chain because it's irrelevant for test.
'debugCreator: _EmptyWidget ← Column ← [root]',
),
);
});

View file

@ -30,7 +30,7 @@ class TestWidgetState extends State<TestWidget> {
void main() {
testWidgets('initState() is called when we are in the tree', (WidgetTester tester) async {
await tester.pumpWidget(const Parent(child: TestWidget()));
expect(ancestors, equals(<String>['Parent', 'View', 'RenderObjectToWidgetAdapter<RenderBox>']));
expect(ancestors, equals(<String>['Parent', 'RenderObjectToWidgetAdapter<RenderBox>']));
});
}

View file

@ -63,20 +63,15 @@ void main() {
expect(error.diagnostics.last, isA<ErrorHint>());
expect(
error.toStringDeep(),
startsWith(
equalsIgnoringHashCodes(
'FlutterError\n'
' No MediaQuery widget ancestor found.\n'
' Builder widgets require a MediaQuery widget ancestor.\n'
' The specific widget that could not find a MediaQuery ancestor\n'
' was:\n'
' Builder\n'
' The ownership chain for the affected widget is: "Builder ←', // Full ownership chain omitted, not relevant for test.
),
);
expect(
error.toStringDeep(),
endsWith(
'[root]"\n' // End of ownership chain.
' The ownership chain for the affected widget is: "Builder ←\n'
' [root]"\n'
' No MediaQuery ancestor could be found starting from the context\n'
' that was passed to MediaQuery.of(). This can happen because you\n'
' have not added a WidgetsApp, CupertinoApp, or MaterialApp widget\n'

View file

@ -274,7 +274,7 @@ void main() {
expect(exception, isFlutterError);
expect(
exception.toString(),
startsWith(
equalsIgnoringHashCodes(
'Incorrect use of ParentDataWidget.\n'
'The following ParentDataWidgets are providing parent data to the same RenderObject:\n'
'- Positioned(left: 7.0, top: 6.0) (typically placed directly inside a Stack widget)\n'
@ -283,7 +283,7 @@ void main() {
'Usually, this indicates that at least one of the offending ParentDataWidgets listed '
'above is not placed directly inside a compatible ancestor widget.\n'
'The ownership chain for the RenderObject that received the parent data was:\n'
' DecoratedBox ← Positioned ← Positioned ← Stack ← Directionality ← ', // End of chain omitted, not relevant for test.
' DecoratedBox ← Positioned ← Positioned ← Stack ← Directionality ← [root]',
),
);
@ -311,7 +311,7 @@ void main() {
expect(exception, isFlutterError);
expect(
exception.toString(),
startsWith(
equalsIgnoringHashCodes(
'Incorrect use of ParentDataWidget.\n'
'The ParentDataWidget Positioned(left: 7.0, top: 6.0) wants to apply ParentData of type '
'StackParentData to a RenderObject, which has been set up to accept ParentData of '
@ -320,7 +320,7 @@ void main() {
'Typically, Positioned widgets are placed directly inside Stack widgets.\n'
'The offending Positioned is currently placed inside a Row widget.\n'
'The ownership chain for the RenderObject that received the incompatible parent data was:\n'
' DecoratedBox ← Positioned ← Row ← DummyWidget ← Directionality ← ', // End of chain omitted, not relevant for test.
' DecoratedBox ← Positioned ← Row ← DummyWidget ← Directionality ← [root]',
),
);
@ -410,7 +410,7 @@ void main() {
expect(exception, isFlutterError);
expect(
exception.toString(),
startsWith(
equalsIgnoringHashCodes(
'Incorrect use of ParentDataWidget.\n'
'The ParentDataWidget Expanded(flex: 1) wants to apply ParentData of type '
'FlexParentData to a RenderObject, which has been set up to accept ParentData of '
@ -419,7 +419,7 @@ void main() {
'Typically, Expanded widgets are placed directly inside Flex widgets.\n'
'The offending Expanded is currently placed inside a Stack widget.\n'
'The ownership chain for the RenderObject that received the incompatible parent data was:\n'
' LimitedBox ← Container ← Expanded ← Stack ← Row ← Directionality ← ', // Omitted end of debugCreator chain because it's irrelevant for test.
' LimitedBox ← Container ← Expanded ← Stack ← Row ← Directionality ← [root]',
),
);
});

View file

@ -2486,7 +2486,7 @@ void main() {
expect(
tester.allWidgets.map((Widget widget) => widget.runtimeType.toString()).toList(),
containsAllInOrder(<String>['PlatformViewLink', '_PlatformViewPlaceHolder']),
equals(<String>['PlatformViewLink', '_PlatformViewPlaceHolder']),
);
onPlatformViewCreatedCallBack(createdPlatformViewId);
@ -2495,7 +2495,7 @@ void main() {
expect(
tester.allWidgets.map((Widget widget) => widget.runtimeType.toString()).toList(),
containsAllInOrder(<String>['PlatformViewLink', 'Focus', '_FocusMarker', 'Semantics', 'PlatformViewSurface']),
equals(<String>['PlatformViewLink', 'Focus', '_FocusMarker', 'Semantics', 'PlatformViewSurface']),
);
expect(createdPlatformViewId, currentViewId + 1);
@ -2535,7 +2535,7 @@ void main() {
expect(
tester.allWidgets.map((Widget widget) => widget.runtimeType.toString()).toList(),
containsAllInOrder(<String>['Center', 'SizedBox', 'PlatformViewLink', '_PlatformViewPlaceHolder']),
equals(<String>['Center', 'SizedBox', 'PlatformViewLink', '_PlatformViewPlaceHolder']),
);
// 'create' should not have been called by PlatformViewLink, since its
@ -2580,7 +2580,7 @@ void main() {
expect(
tester.allWidgets.map((Widget widget) => widget.runtimeType.toString()).toList(),
containsAllInOrder(<String>['PlatformViewLink', '_PlatformViewPlaceHolder']),
equals(<String>['PlatformViewLink', '_PlatformViewPlaceHolder']),
);
// Layout should have triggered a create call. Simulate the callback
@ -2592,7 +2592,7 @@ void main() {
expect(
tester.allWidgets.map((Widget widget) => widget.runtimeType.toString()).toList(),
containsAllInOrder(<String>['PlatformViewLink', 'Focus', '_FocusMarker', 'Semantics', 'PlatformViewSurface']),
equals(<String>['PlatformViewLink', 'Focus', '_FocusMarker', 'Semantics', 'PlatformViewSurface']),
);
expect(createdPlatformViewId, currentViewId + 1);
@ -2678,7 +2678,7 @@ void main() {
expect(
tester.allWidgets.map((Widget widget) => widget.runtimeType.toString()).toList(),
containsAllInOrder(<String>['PlatformViewLink', '_PlatformViewPlaceHolder']),
equals(<String>['PlatformViewLink', '_PlatformViewPlaceHolder']),
);
onPlatformViewCreatedCallBack(createdPlatformViewId);
@ -2687,7 +2687,7 @@ void main() {
expect(
tester.allWidgets.map((Widget widget) => widget.runtimeType.toString()).toList(),
containsAllInOrder(<String>['PlatformViewLink', 'Focus', '_FocusMarker', 'Semantics', 'PlatformViewSurface']),
equals(<String>['PlatformViewLink', 'Focus', '_FocusMarker', 'Semantics', 'PlatformViewSurface']),
);
expect(createdPlatformViewId, currentViewId + 1);

View file

@ -219,30 +219,27 @@ void main() {
expect(
tester.renderObject(find.byType(_Diagonal)).toStringDeep(),
equalsIgnoringHashCodes(
'_RenderDiagonal#00000 relayoutBoundary=up1\n'
' │ creator: _Diagonal ← Align ← Directionality ←\n'
' │ View-[GlobalObjectKey TestWindow#00000] ← [root]\n'
' │ parentData: offset=Offset(0.0, 0.0) (can use size)\n'
' │ constraints: BoxConstraints(0.0<=w<=800.0, 0.0<=h<=600.0)\n'
' │ size: Size(190.0, 220.0)\n'
'\n'
' ├─topLeft: RenderConstrainedBox#00000 relayoutBoundary=up2\n'
' │ creator: SizedBox ← _Diagonal ← Align ← Directionality ←\n'
' │ View-[GlobalObjectKey TestWindow#00000] ← [root]\n'
' │ parentData: offset=Offset(0.0, 0.0) (can use size)\n'
' │ constraints: BoxConstraints(unconstrained)\n'
' │ size: Size(80.0, 100.0)\n'
' │ additionalConstraints: BoxConstraints(w=80.0, h=100.0)\n'
'\n'
' └─bottomRight: RenderConstrainedBox#00000 relayoutBoundary=up2\n'
' creator: SizedBox ← _Diagonal ← Align ← Directionality ←\n'
' View-[GlobalObjectKey TestWindow#00000] ← [root]\n'
' parentData: offset=Offset(80.0, 100.0) (can use size)\n'
' constraints: BoxConstraints(unconstrained)\n'
' size: Size(110.0, 120.0)\n'
' additionalConstraints: BoxConstraints(w=110.0, h=120.0)\n',
)
equalsIgnoringHashCodes(r'''
_RenderDiagonal#00000 relayoutBoundary=up1
creator: _Diagonal Align Directionality [root]
parentData: offset=Offset(0.0, 0.0) (can use size)
constraints: BoxConstraints(0.0<=w<=800.0, 0.0<=h<=600.0)
size: Size(190.0, 220.0)
topLeft: RenderConstrainedBox#00000 relayoutBoundary=up2
creator: SizedBox _Diagonal Align Directionality [root]
parentData: offset=Offset(0.0, 0.0) (can use size)
constraints: BoxConstraints(unconstrained)
size: Size(80.0, 100.0)
additionalConstraints: BoxConstraints(w=80.0, h=100.0)
bottomRight: RenderConstrainedBox#00000 relayoutBoundary=up2
creator: SizedBox _Diagonal Align Directionality [root]
parentData: offset=Offset(80.0, 100.0) (can use size)
constraints: BoxConstraints(unconstrained)
size: Size(110.0, 120.0)
additionalConstraints: BoxConstraints(w=110.0, h=120.0)
''')
);
});
}

View file

@ -783,27 +783,21 @@ void main() {
await tester.pumpWidget(
Stack(),
);
final String exception = tester.takeException().toString();
expect(
exception, startsWith(
tester.takeException().toString(),
'No Directionality widget found.\n'
"Stack widgets require a Directionality widget ancestor to resolve the 'alignment' argument.\n"
"The default value for 'alignment' is AlignmentDirectional.topStart, which requires a text direction.\n"
'The specific widget that could not find a Directionality ancestor was:\n'
' Stack\n'
'The ownership chain for the affected widget is: "Stack ← ', // Omitted full ownership chain because it is not relevant for the test.
));
expect(
exception, endsWith(
'← [root]"\n' // End of ownership chain.
'The ownership chain for the affected widget is: "Stack ← [root]"\n'
'Typically, the Directionality widget is introduced by the MaterialApp or WidgetsApp widget at the '
'top of your application widget tree. It determines the ambient reading direction and is used, for '
'example, to determine how to lay out text, how to interpret "start" and "end" values, and to resolve '
'EdgeInsetsDirectional, AlignmentDirectional, and other *Directional objects.\n'
'Instead of providing a Directionality widget, another solution would be passing a non-directional '
"'alignment', or an explicit 'textDirection', to the Stack.",
));
);
});
testWidgets('Can update clipBehavior of IndexedStack',

View file

@ -1,71 +0,0 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui';
import 'package:flutter/src/widgets/view.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Widgets running with runApp can find View', (WidgetTester tester) async {
FlutterView? viewOf;
FlutterView? viewMaybeOf;
runApp(
Builder(
builder: (BuildContext context) {
viewOf = View.of(context);
viewMaybeOf = View.maybeOf(context);
return Container();
},
),
);
expect(viewOf, isNotNull);
expect(viewOf, isA<FlutterView>());
expect(viewMaybeOf, isNotNull);
expect(viewMaybeOf, isA<FlutterView>());
});
testWidgets('Widgets running with pumpWidget can find View', (WidgetTester tester) async {
FlutterView? view;
FlutterView? viewMaybeOf;
await tester.pumpWidget(
Builder(
builder: (BuildContext context) {
view = View.of(context);
viewMaybeOf = View.maybeOf(context);
return Container();
},
),
);
expect(view, isNotNull);
expect(view, isA<FlutterView>());
expect(viewMaybeOf, isNotNull);
expect(viewMaybeOf, isA<FlutterView>());
});
testWidgets('cannot find View behind a LookupBoundary', (WidgetTester tester) async {
await tester.pumpWidget(
LookupBoundary(
child: Container(),
),
);
final BuildContext context = tester.element(find.byType(Container));
expect(View.maybeOf(context), isNull);
expect(
() => View.of(context),
throwsA(isA<FlutterError>().having(
(FlutterError error) => error.message,
'message',
contains('The context provided to View.of() does have a View widget ancestor, but it is hidden by a LookupBoundary.'),
)),
);
});
}

View file

@ -4277,7 +4277,11 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService {
final Element element = tester.element(find.byType(Directionality).first);
Element? root;
element.visitAncestorElements((Element ancestor) {
root = ancestor;
if (root == null) {
root = ancestor;
// Stop traversing ancestors.
return false;
}
return true;
});
expect(root, isNotNull);

View file

@ -553,24 +553,12 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
EnginePhase phase = EnginePhase.sendSemanticsUpdate,
]) {
return TestAsyncUtils.guard<void>(() {
return _pumpWidget(
binding.wrapWithDefaultView(widget),
duration,
phase,
);
binding.attachRootWidget(widget);
binding.scheduleFrame();
return binding.pump(duration, phase);
});
}
Future<void> _pumpWidget(
Widget widget, [
Duration? duration,
EnginePhase phase = EnginePhase.sendSemanticsUpdate,
]) {
binding.attachRootWidget(widget);
binding.scheduleFrame();
return binding.pump(duration, phase);
}
@override
Future<List<Duration>> handlePointerEventRecord(Iterable<PointerEventRecord> records) {
assert(records != null);
@ -709,7 +697,7 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
// The interval following the last frame doesn't have to be within the fullDuration.
Duration elapsed = Duration.zero;
return TestAsyncUtils.guard<void>(() async {
binding.attachRootWidget(binding.wrapWithDefaultView(target));
binding.attachRootWidget(target);
binding.scheduleFrame();
while (elapsed < maxDuration) {
await binding.pump(interval);
@ -732,14 +720,12 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
'therefore no restoration data has been collected to restore from. Did you forget to wrap '
'your widget tree in a RootRestorationScope?',
);
return TestAsyncUtils.guard<void>(() async {
final Widget widget = ((binding.renderViewElement! as RenderObjectToWidgetElement<RenderObject>).widget as RenderObjectToWidgetAdapter<RenderObject>).child!;
final TestRestorationData restorationData = binding.restorationManager.restorationData;
runApp(Container(key: UniqueKey()));
await pump();
binding.restorationManager.restoreFrom(restorationData);
return _pumpWidget(widget);
});
final Widget widget = ((binding.renderViewElement! as RenderObjectToWidgetElement<RenderObject>).widget as RenderObjectToWidgetAdapter<RenderObject>).child!;
final TestRestorationData restorationData = binding.restorationManager.restorationData;
runApp(Container(key: UniqueKey()));
await pump();
binding.restorationManager.restoreFrom(restorationData);
return pumpWidget(widget);
}
/// Retrieves the current restoration data from the [RestorationManager].