// 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 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'test_widgets.dart'; void checkTree(WidgetTester tester, List expectedDecorations) { final MultiChildRenderObjectElement element = tester.element( find.byElementPredicate((Element element) => element is MultiChildRenderObjectElement), ); expect(element, isNotNull); expect(element.renderObject, isA()); final renderObject = element.renderObject as RenderStack; try { RenderObject? child = renderObject.firstChild; for (final decoration in expectedDecorations) { expect(child, isA()); final decoratedBox = child! as RenderDecoratedBox; expect(decoratedBox.decoration, equals(decoration)); final decoratedBoxParentData = decoratedBox.parentData! as StackParentData; child = decoratedBoxParentData.nextSibling; } expect(child, isNull); } catch (e) { debugPrint(renderObject.toStringDeep()); rethrow; } } void main() { testWidgets('MultiChildRenderObjectElement control test', (WidgetTester tester) async { await tester.pumpWidget( const Stack( textDirection: TextDirection.ltr, children: [ DecoratedBox(decoration: kBoxDecorationA), DecoratedBox(decoration: kBoxDecorationB), DecoratedBox(decoration: kBoxDecorationC), ], ), ); checkTree(tester, [kBoxDecorationA, kBoxDecorationB, kBoxDecorationC]); await tester.pumpWidget( const Stack( textDirection: TextDirection.ltr, children: [ DecoratedBox(decoration: kBoxDecorationA), DecoratedBox(decoration: kBoxDecorationC), ], ), ); checkTree(tester, [kBoxDecorationA, kBoxDecorationC]); await tester.pumpWidget( const Stack( textDirection: TextDirection.ltr, children: [ DecoratedBox(decoration: kBoxDecorationA), DecoratedBox(key: Key('b'), decoration: kBoxDecorationB), DecoratedBox(decoration: kBoxDecorationC), ], ), ); checkTree(tester, [kBoxDecorationA, kBoxDecorationB, kBoxDecorationC]); await tester.pumpWidget( const Stack( textDirection: TextDirection.ltr, children: [ DecoratedBox(key: Key('b'), decoration: kBoxDecorationB), DecoratedBox(decoration: kBoxDecorationC), DecoratedBox(key: Key('a'), decoration: kBoxDecorationA), ], ), ); checkTree(tester, [kBoxDecorationB, kBoxDecorationC, kBoxDecorationA]); await tester.pumpWidget( const Stack( textDirection: TextDirection.ltr, children: [ DecoratedBox(key: Key('a'), decoration: kBoxDecorationA), DecoratedBox(decoration: kBoxDecorationC), DecoratedBox(key: Key('b'), decoration: kBoxDecorationB), ], ), ); checkTree(tester, [kBoxDecorationA, kBoxDecorationC, kBoxDecorationB]); await tester.pumpWidget( const Stack( textDirection: TextDirection.ltr, children: [DecoratedBox(decoration: kBoxDecorationC)], ), ); checkTree(tester, [kBoxDecorationC]); await tester.pumpWidget(const Stack(textDirection: TextDirection.ltr)); checkTree(tester, []); }); testWidgets('MultiChildRenderObjectElement with stateless widgets', (WidgetTester tester) async { await tester.pumpWidget( const Stack( textDirection: TextDirection.ltr, children: [ DecoratedBox(decoration: kBoxDecorationA), DecoratedBox(decoration: kBoxDecorationB), DecoratedBox(decoration: kBoxDecorationC), ], ), ); checkTree(tester, [kBoxDecorationA, kBoxDecorationB, kBoxDecorationC]); await tester.pumpWidget( const Stack( textDirection: TextDirection.ltr, children: [ DecoratedBox(decoration: kBoxDecorationA), DummyWidget(child: DecoratedBox(decoration: kBoxDecorationB)), DecoratedBox(decoration: kBoxDecorationC), ], ), ); checkTree(tester, [kBoxDecorationA, kBoxDecorationB, kBoxDecorationC]); await tester.pumpWidget( const Stack( textDirection: TextDirection.ltr, children: [ DecoratedBox(decoration: kBoxDecorationA), DummyWidget( child: DummyWidget(child: DecoratedBox(decoration: kBoxDecorationB)), ), DecoratedBox(decoration: kBoxDecorationC), ], ), ); checkTree(tester, [kBoxDecorationA, kBoxDecorationB, kBoxDecorationC]); await tester.pumpWidget( const Stack( textDirection: TextDirection.ltr, children: [ DummyWidget( child: DummyWidget(child: DecoratedBox(decoration: kBoxDecorationB)), ), DummyWidget(child: DecoratedBox(decoration: kBoxDecorationA)), DecoratedBox(decoration: kBoxDecorationC), ], ), ); checkTree(tester, [kBoxDecorationB, kBoxDecorationA, kBoxDecorationC]); await tester.pumpWidget( const Stack( textDirection: TextDirection.ltr, children: [ DummyWidget(child: DecoratedBox(decoration: kBoxDecorationB)), DummyWidget(child: DecoratedBox(decoration: kBoxDecorationA)), DecoratedBox(decoration: kBoxDecorationC), ], ), ); checkTree(tester, [kBoxDecorationB, kBoxDecorationA, kBoxDecorationC]); await tester.pumpWidget( const Stack( textDirection: TextDirection.ltr, children: [ DummyWidget( key: Key('b'), child: DecoratedBox(decoration: kBoxDecorationB), ), DummyWidget( key: Key('a'), child: DecoratedBox(decoration: kBoxDecorationA), ), ], ), ); checkTree(tester, [kBoxDecorationB, kBoxDecorationA]); await tester.pumpWidget( const Stack( textDirection: TextDirection.ltr, children: [ DummyWidget( key: Key('a'), child: DecoratedBox(decoration: kBoxDecorationA), ), DummyWidget( key: Key('b'), child: DecoratedBox(decoration: kBoxDecorationB), ), ], ), ); checkTree(tester, [kBoxDecorationA, kBoxDecorationB]); await tester.pumpWidget(const Stack(textDirection: TextDirection.ltr)); checkTree(tester, []); }); testWidgets('MultiChildRenderObjectElement with stateful widgets', (WidgetTester tester) async { await tester.pumpWidget( const Stack( textDirection: TextDirection.ltr, children: [ DecoratedBox(decoration: kBoxDecorationA), DecoratedBox(decoration: kBoxDecorationB), ], ), ); checkTree(tester, [kBoxDecorationA, kBoxDecorationB]); await tester.pumpWidget( const Stack( textDirection: TextDirection.ltr, children: [ FlipWidget( left: DecoratedBox(decoration: kBoxDecorationA), right: DecoratedBox(decoration: kBoxDecorationB), ), DecoratedBox(decoration: kBoxDecorationC), ], ), ); checkTree(tester, [kBoxDecorationA, kBoxDecorationC]); flipStatefulWidget(tester); await tester.pump(); checkTree(tester, [kBoxDecorationB, kBoxDecorationC]); await tester.pumpWidget( const Stack( textDirection: TextDirection.ltr, children: [ FlipWidget( left: DecoratedBox(decoration: kBoxDecorationA), right: DecoratedBox(decoration: kBoxDecorationB), ), ], ), ); checkTree(tester, [kBoxDecorationB]); flipStatefulWidget(tester); await tester.pump(); checkTree(tester, [kBoxDecorationA]); await tester.pumpWidget( const Stack( textDirection: TextDirection.ltr, children: [ FlipWidget( key: Key('flip'), left: DecoratedBox(decoration: kBoxDecorationA), right: DecoratedBox(decoration: kBoxDecorationB), ), ], ), ); await tester.pumpWidget( const Stack( textDirection: TextDirection.ltr, children: [ DecoratedBox(key: Key('c'), decoration: kBoxDecorationC), FlipWidget( key: Key('flip'), left: DecoratedBox(decoration: kBoxDecorationA), right: DecoratedBox(decoration: kBoxDecorationB), ), ], ), ); checkTree(tester, [kBoxDecorationC, kBoxDecorationA]); flipStatefulWidget(tester); await tester.pump(); checkTree(tester, [kBoxDecorationC, kBoxDecorationB]); await tester.pumpWidget( const Stack( textDirection: TextDirection.ltr, children: [ FlipWidget( key: Key('flip'), left: DecoratedBox(decoration: kBoxDecorationA), right: DecoratedBox(decoration: kBoxDecorationB), ), DecoratedBox(key: Key('c'), decoration: kBoxDecorationC), ], ), ); checkTree(tester, [kBoxDecorationB, kBoxDecorationC]); }); } class DummyWidget extends StatelessWidget { const DummyWidget({super.key, required this.child}); final Widget child; @override Widget build(BuildContext context) => child; }