// Copyright 2015 The Chromium 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/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_test/flutter_test.dart'; import 'rendering_tester.dart'; void main() { test('should size to render view', () { final RenderBox root = RenderDecoratedBox( decoration: BoxDecoration( color: const Color(0xFF00FF00), gradient: RadialGradient( center: Alignment.topLeft, radius: 1.8, colors: [Colors.yellow[500], Colors.blue[500]], ), boxShadow: kElevationToShadow[3], ), ); layout(root); expect(root.size.width, equals(800.0)); expect(root.size.height, equals(600.0)); }); test('Flex and padding', () { final RenderBox size = RenderConstrainedBox( additionalConstraints: const BoxConstraints().tighten(height: 100.0), ); final RenderBox inner = RenderDecoratedBox( decoration: const BoxDecoration( color: Color(0xFF00FF00), ), child: size, ); final RenderBox padding = RenderPadding( padding: const EdgeInsets.all(50.0), child: inner, ); final RenderBox flex = RenderFlex( children: [padding], direction: Axis.vertical, crossAxisAlignment: CrossAxisAlignment.stretch, ); final RenderBox outer = RenderDecoratedBox( decoration: const BoxDecoration( color: Color(0xFF0000FF), ), child: flex, ); layout(outer); expect(size.size.width, equals(700.0)); expect(size.size.height, equals(100.0)); expect(inner.size.width, equals(700.0)); expect(inner.size.height, equals(100.0)); expect(padding.size.width, equals(800.0)); expect(padding.size.height, equals(200.0)); expect(flex.size.width, equals(800.0)); expect(flex.size.height, equals(600.0)); expect(outer.size.width, equals(800.0)); expect(outer.size.height, equals(600.0)); }); test('should not have a 0 sized colored Box', () { final RenderBox coloredBox = RenderDecoratedBox( decoration: const BoxDecoration(), ); expect(coloredBox, hasAGoodToStringDeep); expect( coloredBox.toStringDeep(minLevel: DiagnosticLevel.info), equalsIgnoringHashCodes( 'RenderDecoratedBox#00000 NEEDS-LAYOUT NEEDS-PAINT DETACHED\n' ' parentData: MISSING\n' ' constraints: MISSING\n' ' size: MISSING\n' ' decoration: BoxDecoration:\n' ' \n' ' configuration: ImageConfiguration()\n'), ); final RenderBox paddingBox = RenderPadding( padding: const EdgeInsets.all(10.0), child: coloredBox, ); final RenderBox root = RenderDecoratedBox( decoration: const BoxDecoration(), child: paddingBox, ); layout(root); expect(coloredBox.size.width, equals(780.0)); expect(coloredBox.size.height, equals(580.0)); expect(coloredBox, hasAGoodToStringDeep); expect( coloredBox.toStringDeep(minLevel: DiagnosticLevel.info), equalsIgnoringHashCodes( 'RenderDecoratedBox#00000 NEEDS-PAINT\n' ' parentData: offset=Offset(10.0, 10.0) (can use size)\n' ' constraints: BoxConstraints(w=780.0, h=580.0)\n' ' size: Size(780.0, 580.0)\n' ' decoration: BoxDecoration:\n' ' \n' ' configuration: ImageConfiguration()\n', ), ); }); test('reparenting should clear position', () { final RenderDecoratedBox coloredBox = RenderDecoratedBox( decoration: const BoxDecoration(), ); final RenderPadding paddedBox = RenderPadding( child: coloredBox, padding: const EdgeInsets.all(10.0), ); layout(paddedBox); final BoxParentData parentData = coloredBox.parentData; expect(parentData.offset.dx, isNot(equals(0.0))); paddedBox.child = null; final RenderConstrainedBox constraintedBox = RenderConstrainedBox( child: coloredBox, additionalConstraints: const BoxConstraints(), ); layout(constraintedBox); expect(coloredBox.parentData?.runtimeType, ParentData); }); test('UnconstrainedBox expands to fit children', () { final RenderUnconstrainedBox unconstrained = RenderUnconstrainedBox( constrainedAxis: Axis.horizontal, // This is reset to null below. textDirection: TextDirection.ltr, child: RenderConstrainedBox( additionalConstraints: const BoxConstraints.tightFor(width: 200.0, height: 200.0), ), alignment: Alignment.center, ); layout( unconstrained, constraints: const BoxConstraints( minWidth: 200.0, maxWidth: 200.0, minHeight: 200.0, maxHeight: 200.0, ), ); // Check that we can update the constrained axis to null. unconstrained.constrainedAxis = null; renderer.reassembleApplication(); expect(unconstrained.size.width, equals(200.0), reason: 'unconstrained width'); expect(unconstrained.size.height, equals(200.0), reason: 'unconstrained height'); }); test('UnconstrainedBox handles vertical overflow', () { final RenderUnconstrainedBox unconstrained = RenderUnconstrainedBox( textDirection: TextDirection.ltr, child: RenderConstrainedBox( additionalConstraints: const BoxConstraints.tightFor(height: 200.0), ), alignment: Alignment.center, ); const BoxConstraints viewport = BoxConstraints(maxHeight: 100.0, maxWidth: 100.0); layout(unconstrained, constraints: viewport); expect(unconstrained.getMinIntrinsicHeight(100.0), equals(200.0)); expect(unconstrained.getMaxIntrinsicHeight(100.0), equals(200.0)); expect(unconstrained.getMinIntrinsicWidth(100.0), equals(0.0)); expect(unconstrained.getMaxIntrinsicWidth(100.0), equals(0.0)); }); test('UnconstrainedBox handles horizontal overflow', () { final RenderUnconstrainedBox unconstrained = RenderUnconstrainedBox( textDirection: TextDirection.ltr, child: RenderConstrainedBox( additionalConstraints: const BoxConstraints.tightFor(width: 200.0), ), alignment: Alignment.center, ); const BoxConstraints viewport = BoxConstraints(maxHeight: 100.0, maxWidth: 100.0); layout(unconstrained, constraints: viewport); expect(unconstrained.getMinIntrinsicHeight(100.0), equals(0.0)); expect(unconstrained.getMaxIntrinsicHeight(100.0), equals(0.0)); expect(unconstrained.getMinIntrinsicWidth(100.0), equals(200.0)); expect(unconstrained.getMaxIntrinsicWidth(100.0), equals(200.0)); }); test('UnconstrainedBox.toStringDeep returns useful information', () { final RenderUnconstrainedBox unconstrained = RenderUnconstrainedBox( textDirection: TextDirection.ltr, alignment: Alignment.center, ); expect(unconstrained.alignment, Alignment.center); expect(unconstrained.textDirection, TextDirection.ltr); expect(unconstrained, hasAGoodToStringDeep); expect( unconstrained.toStringDeep(minLevel: DiagnosticLevel.info), equalsIgnoringHashCodes( 'RenderUnconstrainedBox#00000 NEEDS-LAYOUT NEEDS-PAINT DETACHED\n' ' parentData: MISSING\n' ' constraints: MISSING\n' ' size: MISSING\n' ' alignment: center\n' ' textDirection: ltr\n'), ); }); test('UnconstrainedBox honors constrainedAxis=Axis.horizontal', () { final RenderConstrainedBox flexible = RenderConstrainedBox(additionalConstraints: const BoxConstraints.expand(height: 200.0)); final RenderUnconstrainedBox unconstrained = RenderUnconstrainedBox( constrainedAxis: Axis.horizontal, textDirection: TextDirection.ltr, child: RenderFlex( direction: Axis.horizontal, textDirection: TextDirection.ltr, children: [flexible], ), alignment: Alignment.center, ); final FlexParentData flexParentData = flexible.parentData; flexParentData.flex = 1; flexParentData.fit = FlexFit.tight; const BoxConstraints viewport = BoxConstraints(maxWidth: 100.0); layout(unconstrained, constraints: viewport); expect(unconstrained.size.width, equals(100.0), reason: 'constrained width'); expect(unconstrained.size.height, equals(200.0), reason: 'unconstrained height'); }); test('UnconstrainedBox honors constrainedAxis=Axis.vertical', () { final RenderConstrainedBox flexible = RenderConstrainedBox(additionalConstraints: const BoxConstraints.expand(width: 200.0)); final RenderUnconstrainedBox unconstrained = RenderUnconstrainedBox( constrainedAxis: Axis.vertical, textDirection: TextDirection.ltr, child: RenderFlex( direction: Axis.vertical, textDirection: TextDirection.ltr, children: [flexible], ), alignment: Alignment.center, ); final FlexParentData flexParentData = flexible.parentData; flexParentData.flex = 1; flexParentData.fit = FlexFit.tight; const BoxConstraints viewport = BoxConstraints(maxHeight: 100.0); layout(unconstrained, constraints: viewport); expect(unconstrained.size.width, equals(200.0), reason: 'unconstrained width'); expect(unconstrained.size.height, equals(100.0), reason: 'constrained height'); }); group('hit testing', () { test('BoxHitTestResult wrapping HitTestResult', () { final HitTestEntry entry1 = HitTestEntry(_DummyHitTestTarget()); final HitTestEntry entry2 = HitTestEntry(_DummyHitTestTarget()); final HitTestEntry entry3 = HitTestEntry(_DummyHitTestTarget()); final Matrix4 transform = Matrix4.translationValues(40.0, 150.0, 0.0); final HitTestResult wrapped = MyHitTestResult() ..publicPushTransform(transform); wrapped.add(entry1); expect(wrapped.path, equals([entry1])); expect(entry1.transform, transform); final BoxHitTestResult wrapping = BoxHitTestResult.wrap(wrapped); expect(wrapping.path, equals([entry1])); expect(wrapping.path, same(wrapped.path)); wrapping.add(entry2); expect(wrapping.path, equals([entry1, entry2])); expect(wrapped.path, equals([entry1, entry2])); expect(entry2.transform, transform); wrapped.add(entry3); expect(wrapping.path, equals([entry1, entry2, entry3])); expect(wrapped.path, equals([entry1, entry2, entry3])); expect(entry3.transform, transform); }); test('addWithPaintTransform', () { final BoxHitTestResult result = BoxHitTestResult(); final List positions = []; bool isHit = result.addWithPaintTransform( transform: null, position: null, hitTest: (BoxHitTestResult result, Offset position) { expect(result, isNotNull); positions.add(position); return true; }, ); expect(isHit, isTrue); expect(positions.single, isNull); positions.clear(); isHit = result.addWithPaintTransform( transform: Matrix4.translationValues(20, 30, 0), position: null, hitTest: (BoxHitTestResult result, Offset position) { expect(result, isNotNull); positions.add(position); return true; }, ); expect(isHit, isTrue); expect(positions.single, isNull); positions.clear(); const Offset position = Offset(3, 4); isHit = result.addWithPaintTransform( transform: null, position: position, hitTest: (BoxHitTestResult result, Offset position) { expect(result, isNotNull); positions.add(position); return false; }, ); expect(isHit, isFalse); expect(positions.single, position); positions.clear(); isHit = result.addWithPaintTransform( transform: Matrix4.identity(), position: position, hitTest: (BoxHitTestResult result, Offset position) { expect(result, isNotNull); positions.add(position); return true; }, ); expect(isHit, isTrue); expect(positions.single, position); positions.clear(); isHit = result.addWithPaintTransform( transform: Matrix4.translationValues(20, 30, 0), position: position, hitTest: (BoxHitTestResult result, Offset position) { expect(result, isNotNull); positions.add(position); return true; }, ); expect(isHit, isTrue); expect(positions.single, position - const Offset(20, 30)); positions.clear(); isHit = result.addWithPaintTransform( transform: MatrixUtils.forceToPoint(position), // cannot be inverted position: position, hitTest: (BoxHitTestResult result, Offset position) { expect(result, isNotNull); positions.add(position); return true; }, ); expect(isHit, isFalse); expect(positions, isEmpty); positions.clear(); }); test('addWithPaintOffset', () { final BoxHitTestResult result = BoxHitTestResult(); final List positions = []; bool isHit = result.addWithPaintOffset( offset: null, position: null, hitTest: (BoxHitTestResult result, Offset position) { expect(result, isNotNull); positions.add(position); return true; }, ); expect(isHit, isTrue); expect(positions.single, isNull); positions.clear(); isHit = result.addWithPaintOffset( offset: const Offset(55, 32), position: null, hitTest: (BoxHitTestResult result, Offset position) { expect(result, isNotNull); positions.add(position); return true; }, ); expect(isHit, isTrue); expect(positions.single, isNull); positions.clear(); const Offset position = Offset(3, 4); isHit = result.addWithPaintOffset( offset: null, position: position, hitTest: (BoxHitTestResult result, Offset position) { expect(result, isNotNull); positions.add(position); return false; }, ); expect(isHit, isFalse); expect(positions.single, position); positions.clear(); isHit = result.addWithPaintOffset( offset: Offset.zero, position: position, hitTest: (BoxHitTestResult result, Offset position) { expect(result, isNotNull); positions.add(position); return true; }, ); expect(isHit, isTrue); expect(positions.single, position); positions.clear(); isHit = result.addWithPaintOffset( offset: const Offset(20, 30), position: position, hitTest: (BoxHitTestResult result, Offset position) { expect(result, isNotNull); positions.add(position); return true; }, ); expect(isHit, isTrue); expect(positions.single, position - const Offset(20, 30)); positions.clear(); }); test('addWithRawTransform', () { final BoxHitTestResult result = BoxHitTestResult(); final List positions = []; bool isHit = result.addWithRawTransform( transform: null, position: null, hitTest: (BoxHitTestResult result, Offset position) { expect(result, isNotNull); positions.add(position); return true; }, ); expect(isHit, isTrue); expect(positions.single, isNull); positions.clear(); isHit = result.addWithRawTransform( transform: Matrix4.translationValues(20, 30, 0), position: null, hitTest: (BoxHitTestResult result, Offset position) { expect(result, isNotNull); positions.add(position); return true; }, ); expect(isHit, isTrue); expect(positions.single, isNull); positions.clear(); const Offset position = Offset(3, 4); isHit = result.addWithRawTransform( transform: null, position: position, hitTest: (BoxHitTestResult result, Offset position) { expect(result, isNotNull); positions.add(position); return false; }, ); expect(isHit, isFalse); expect(positions.single, position); positions.clear(); isHit = result.addWithRawTransform( transform: Matrix4.identity(), position: position, hitTest: (BoxHitTestResult result, Offset position) { expect(result, isNotNull); positions.add(position); return true; }, ); expect(isHit, isTrue); expect(positions.single, position); positions.clear(); isHit = result.addWithRawTransform( transform: Matrix4.translationValues(20, 30, 0), position: position, hitTest: (BoxHitTestResult result, Offset position) { expect(result, isNotNull); positions.add(position); return true; }, ); expect(isHit, isTrue); expect(positions.single, position + const Offset(20, 30)); positions.clear(); }); }); } class _DummyHitTestTarget implements HitTestTarget { @override void handleEvent(PointerEvent event, HitTestEntry entry) { // Nothing to do. } } class MyHitTestResult extends HitTestResult { void publicPushTransform(Matrix4 transform) => pushTransform(transform); }