Fix ShapeDecoration (#14009)
And add a test that catches these problems.
This commit is contained in:
parent
e8b666249a
commit
80d6095d98
@ -596,12 +596,12 @@ class _CompoundBorder extends ShapeBorder {
|
|||||||
Path getInnerPath(Rect rect, { TextDirection textDirection }) {
|
Path getInnerPath(Rect rect, { TextDirection textDirection }) {
|
||||||
for (int index = 0; index < borders.length - 1; index += 1)
|
for (int index = 0; index < borders.length - 1; index += 1)
|
||||||
rect = borders[index].dimensions.resolve(textDirection).deflateRect(rect);
|
rect = borders[index].dimensions.resolve(textDirection).deflateRect(rect);
|
||||||
return borders.last.getInnerPath(rect);
|
return borders.last.getInnerPath(rect, textDirection: textDirection);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Path getOuterPath(Rect rect, { TextDirection textDirection }) {
|
Path getOuterPath(Rect rect, { TextDirection textDirection }) {
|
||||||
return borders.first.getOuterPath(rect);
|
return borders.first.getOuterPath(rect, textDirection: textDirection);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -25,7 +25,7 @@ class CircleBorder extends ShapeBorder {
|
|||||||
/// Create a circle border.
|
/// Create a circle border.
|
||||||
///
|
///
|
||||||
/// The [side] argument must not be null.
|
/// The [side] argument must not be null.
|
||||||
const CircleBorder({ this.side = BorderSide.none }) : assert(side != null);
|
const CircleBorder({ this.side: BorderSide.none }) : assert(side != null);
|
||||||
|
|
||||||
/// The style of this border.
|
/// The style of this border.
|
||||||
final BorderSide side;
|
final BorderSide side;
|
||||||
|
@ -162,6 +162,11 @@ abstract class Decoration extends Diagnosticable {
|
|||||||
/// `textDirection` argument should therefore be provided. If it is known that
|
/// `textDirection` argument should therefore be provided. If it is known that
|
||||||
/// the decoration is not affected by the text direction, then the argument
|
/// the decoration is not affected by the text direction, then the argument
|
||||||
/// may be omitted or set to null.
|
/// may be omitted or set to null.
|
||||||
|
///
|
||||||
|
/// When a [Decoration] is painted in a [Container] or [DecoratedBox] (which
|
||||||
|
/// is what [Container] uses), the `textDirection` parameter will be populated
|
||||||
|
/// based on the ambient [Directionality] (by way of the [RenderDecoratedBox]
|
||||||
|
/// renderer).
|
||||||
bool hitTest(Size size, Offset position, { TextDirection textDirection }) => true;
|
bool hitTest(Size size, Offset position, { TextDirection textDirection }) => true;
|
||||||
|
|
||||||
/// Returns a [BoxPainter] that will paint this decoration.
|
/// Returns a [BoxPainter] that will paint this decoration.
|
||||||
@ -207,6 +212,10 @@ abstract class BoxPainter {
|
|||||||
/// Implementations should paint their decorations on the canvas in a
|
/// Implementations should paint their decorations on the canvas in a
|
||||||
/// rectangle whose top left corner is at the given `offset` and whose size is
|
/// rectangle whose top left corner is at the given `offset` and whose size is
|
||||||
/// given by `configuration.size`.
|
/// given by `configuration.size`.
|
||||||
|
///
|
||||||
|
/// When a [Decoration] is painted in a [Container] or [DecoratedBox] (which
|
||||||
|
/// is what [Container] uses), the [ImageConfiguration.textDirection] property
|
||||||
|
/// will be populated based on the ambient [Directionality].
|
||||||
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration);
|
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration);
|
||||||
|
|
||||||
/// Callback that is invoked if an asynchronously-loading resource used by the
|
/// Callback that is invoked if an asynchronously-loading resource used by the
|
||||||
|
@ -118,7 +118,7 @@ class ImageConfiguration {
|
|||||||
if (devicePixelRatio != null) {
|
if (devicePixelRatio != null) {
|
||||||
if (hasArguments)
|
if (hasArguments)
|
||||||
result.write(', ');
|
result.write(', ');
|
||||||
result.write('devicePixelRatio: $devicePixelRatio');
|
result.write('devicePixelRatio: ${devicePixelRatio.toStringAsFixed(1)}');
|
||||||
hasArguments = true;
|
hasArguments = true;
|
||||||
}
|
}
|
||||||
if (locale != null) {
|
if (locale != null) {
|
||||||
|
@ -323,13 +323,13 @@ class _ShapeDecorationPainter extends BoxPainter {
|
|||||||
}
|
}
|
||||||
for (int index = 0; index < _shadowCount; index += 1) {
|
for (int index = 0; index < _shadowCount; index += 1) {
|
||||||
final BoxShadow shadow = _decoration.shadows[index];
|
final BoxShadow shadow = _decoration.shadows[index];
|
||||||
_shadowPaths[index] = _decoration.shape.getOuterPath(rect.shift(shadow.offset).inflate(shadow.spreadRadius));
|
_shadowPaths[index] = _decoration.shape.getOuterPath(rect.shift(shadow.offset).inflate(shadow.spreadRadius), textDirection: textDirection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_interiorPaint != null || _shadowCount != null)
|
if (_interiorPaint != null || _shadowCount != null)
|
||||||
_outerPath = _decoration.shape.getOuterPath(rect);
|
_outerPath = _decoration.shape.getOuterPath(rect, textDirection: textDirection);
|
||||||
if (_decoration.image != null)
|
if (_decoration.image != null)
|
||||||
_innerPath = _decoration.shape.getInnerPath(rect);
|
_innerPath = _decoration.shape.getInnerPath(rect, textDirection: textDirection);
|
||||||
|
|
||||||
_lastRect = rect;
|
_lastRect = rect;
|
||||||
_lastTextDirection = textDirection;
|
_lastTextDirection = textDirection;
|
||||||
|
@ -24,9 +24,10 @@ class TestImageInfo implements ImageInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class TestImageProvider extends ImageProvider<int> {
|
class TestImageProvider extends ImageProvider<int> {
|
||||||
const TestImageProvider(this.key, this.imageValue);
|
const TestImageProvider(this.key, this.imageValue, { this.image });
|
||||||
final int key;
|
final int key;
|
||||||
final int imageValue;
|
final int imageValue;
|
||||||
|
final ui.Image image;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<int> obtainKey(ImageConfiguration configuration) {
|
Future<int> obtainKey(ImageConfiguration configuration) {
|
||||||
@ -36,7 +37,7 @@ class TestImageProvider extends ImageProvider<int> {
|
|||||||
@override
|
@override
|
||||||
ImageStreamCompleter load(int key) {
|
ImageStreamCompleter load(int key) {
|
||||||
return new OneFrameImageStreamCompleter(
|
return new OneFrameImageStreamCompleter(
|
||||||
new SynchronousFuture<ImageInfo>(new TestImageInfo(imageValue))
|
new SynchronousFuture<ImageInfo>(new TestImageInfo(imageValue, image: image))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
137
packages/flutter/test/widgets/shape_decoration_test.dart
Normal file
137
packages/flutter/test/widgets/shape_decoration_test.dart
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
// Copyright 2018 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 'dart:typed_data';
|
||||||
|
import 'dart:ui' as ui show Image;
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
import '../painting/image_data.dart';
|
||||||
|
import '../painting/mocks_for_image_cache.dart';
|
||||||
|
import '../rendering/mock_canvas.dart';
|
||||||
|
|
||||||
|
Future<Null> main() async {
|
||||||
|
final ui.Image rawImage = await decodeImageFromList(new Uint8List.fromList(kTransparentImage));
|
||||||
|
final ImageProvider image = new TestImageProvider(0, 0, image: rawImage);
|
||||||
|
testWidgets('ShapeDecoration.image', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
new MaterialApp(
|
||||||
|
home: new DecoratedBox(
|
||||||
|
decoration: new ShapeDecoration(
|
||||||
|
shape: new Border.all(width: 1.0, color: Colors.white) +
|
||||||
|
new Border.all(width: 1.0, color: Colors.black),
|
||||||
|
image: new DecorationImage(
|
||||||
|
image: image,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
find.byType(DecoratedBox),
|
||||||
|
paints
|
||||||
|
..drawImageRect(image: rawImage)
|
||||||
|
..rect(color: Colors.black)
|
||||||
|
..rect(color: Colors.white)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('ShapeDecoration.color', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
new MaterialApp(
|
||||||
|
home: new DecoratedBox(
|
||||||
|
decoration: new ShapeDecoration(
|
||||||
|
shape: new Border.all(width: 1.0, color: Colors.white) +
|
||||||
|
new Border.all(width: 1.0, color: Colors.black),
|
||||||
|
color: Colors.blue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
find.byType(DecoratedBox),
|
||||||
|
paints
|
||||||
|
..path(color: new Color(Colors.blue.value))
|
||||||
|
..rect(color: Colors.black)
|
||||||
|
..rect(color: Colors.white)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('TestBorder and Directionality - 1', (WidgetTester tester) async {
|
||||||
|
final List<String> log = <String>[];
|
||||||
|
await tester.pumpWidget(
|
||||||
|
new MaterialApp(
|
||||||
|
home: new DecoratedBox(
|
||||||
|
decoration: new ShapeDecoration(
|
||||||
|
shape: new TestBorder(log.add),
|
||||||
|
color: Colors.green,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
log,
|
||||||
|
<String>[
|
||||||
|
'getOuterPath Rect.fromLTRB(0.0, 0.0, 800.0, 600.0) TextDirection.ltr',
|
||||||
|
'paint Rect.fromLTRB(0.0, 0.0, 800.0, 600.0) TextDirection.ltr'
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('TestBorder and Directionality - 2', (WidgetTester tester) async {
|
||||||
|
final List<String> log = <String>[];
|
||||||
|
await tester.pumpWidget(
|
||||||
|
new Directionality(
|
||||||
|
textDirection: TextDirection.rtl,
|
||||||
|
child: new DecoratedBox(
|
||||||
|
decoration: new ShapeDecoration(
|
||||||
|
shape: new TestBorder(log.add),
|
||||||
|
image: new DecorationImage(
|
||||||
|
image: image,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
log,
|
||||||
|
<String>[
|
||||||
|
'getInnerPath Rect.fromLTRB(0.0, 0.0, 800.0, 600.0) TextDirection.rtl',
|
||||||
|
'paint Rect.fromLTRB(0.0, 0.0, 800.0, 600.0) TextDirection.rtl'
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef void Logger(String caller);
|
||||||
|
|
||||||
|
class TestBorder extends ShapeBorder {
|
||||||
|
const TestBorder(this.onLog) : assert(onLog != null);
|
||||||
|
|
||||||
|
final Logger onLog;
|
||||||
|
|
||||||
|
@override
|
||||||
|
EdgeInsetsGeometry get dimensions => const EdgeInsetsDirectional.only(start: 1.0);
|
||||||
|
|
||||||
|
@override
|
||||||
|
ShapeBorder scale(double t) => new TestBorder(onLog);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Path getInnerPath(Rect rect, { TextDirection textDirection }) {
|
||||||
|
onLog('getInnerPath $rect $textDirection');
|
||||||
|
return new Path();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Path getOuterPath(Rect rect, { TextDirection textDirection }) {
|
||||||
|
onLog('getOuterPath $rect $textDirection');
|
||||||
|
return new Path();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void paint(Canvas canvas, Rect rect, { TextDirection textDirection }) {
|
||||||
|
onLog('paint $rect $textDirection');
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user