Fix WidgetsApp to wrap its contents in a Directionality (#12018)
...rather than only wrapping the children. Also, improvements to Banner to better support RTL, and mock_canvas to make debugging things easier.
This commit is contained in:
parent
c46347b6df
commit
fad36baa6f
@ -104,7 +104,7 @@ enum _WordWrapParseMode { inSpace, inWord, atBreak }
|
|||||||
///
|
///
|
||||||
/// The default [debugPrint] implementation uses this for its line wrapping.
|
/// The default [debugPrint] implementation uses this for its line wrapping.
|
||||||
Iterable<String> debugWordWrap(String message, int width, { String wrapIndent: '' }) sync* {
|
Iterable<String> debugWordWrap(String message, int width, { String wrapIndent: '' }) sync* {
|
||||||
if (message.length < width || message[0] == '#') {
|
if (message.length < width || message.trimLeft()[0] == '#') {
|
||||||
yield message;
|
yield message;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2293,7 +2293,8 @@ class RenderCustomPaint extends RenderProxyBox {
|
|||||||
int debugPreviousCanvasSaveCount;
|
int debugPreviousCanvasSaveCount;
|
||||||
canvas.save();
|
canvas.save();
|
||||||
assert(() { debugPreviousCanvasSaveCount = canvas.getSaveCount(); return true; });
|
assert(() { debugPreviousCanvasSaveCount = canvas.getSaveCount(); return true; });
|
||||||
canvas.translate(offset.dx, offset.dy);
|
if (offset != Offset.zero)
|
||||||
|
canvas.translate(offset.dx, offset.dy);
|
||||||
painter.paint(canvas, size);
|
painter.paint(canvas, size);
|
||||||
assert(() {
|
assert(() {
|
||||||
// This isn't perfect. For example, we can't catch the case of
|
// This isn't perfect. For example, we can't catch the case of
|
||||||
|
@ -296,28 +296,18 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Widget result = new MediaQuery(
|
Widget result = new Navigator(
|
||||||
data: new MediaQueryData.fromWindow(ui.window),
|
key: _navigator,
|
||||||
child: new Localizations(
|
initialRoute: widget.initialRoute ?? ui.window.defaultRouteName,
|
||||||
locale: widget.locale ?? _locale,
|
onGenerateRoute: widget.onGenerateRoute,
|
||||||
delegates: _localizationsDelegates.toList(),
|
onUnknownRoute: widget.onUnknownRoute,
|
||||||
child: new Title(
|
observers: widget.navigatorObservers,
|
||||||
title: widget.title,
|
|
||||||
color: widget.color,
|
|
||||||
child: new Navigator(
|
|
||||||
key: _navigator,
|
|
||||||
initialRoute: widget.initialRoute ?? ui.window.defaultRouteName,
|
|
||||||
onGenerateRoute: widget.onGenerateRoute,
|
|
||||||
onUnknownRoute: widget.onUnknownRoute,
|
|
||||||
observers: widget.navigatorObservers
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (widget.textStyle != null) {
|
if (widget.textStyle != null) {
|
||||||
result = new DefaultTextStyle(
|
result = new DefaultTextStyle(
|
||||||
style: widget.textStyle,
|
style: widget.textStyle,
|
||||||
child: result
|
child: result,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,7 +325,6 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
|
|||||||
checkerboardOffscreenLayers: widget.checkerboardOffscreenLayers,
|
checkerboardOffscreenLayers: widget.checkerboardOffscreenLayers,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (performanceOverlay != null) {
|
if (performanceOverlay != null) {
|
||||||
result = new Stack(
|
result = new Stack(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@ -344,11 +333,13 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (widget.showSemanticsDebugger) {
|
if (widget.showSemanticsDebugger) {
|
||||||
result = new SemanticsDebugger(
|
result = new SemanticsDebugger(
|
||||||
child: result,
|
child: result,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(() {
|
assert(() {
|
||||||
if (widget.debugShowWidgetInspector || WidgetsApp.debugShowWidgetInspectorOverride) {
|
if (widget.debugShowWidgetInspector || WidgetsApp.debugShowWidgetInspectorOverride) {
|
||||||
result = new WidgetInspector(
|
result = new WidgetInspector(
|
||||||
@ -364,7 +355,17 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
return new MediaQuery(
|
||||||
|
data: new MediaQueryData.fromWindow(ui.window),
|
||||||
|
child: new Localizations(
|
||||||
|
locale: widget.locale ?? _locale,
|
||||||
|
delegates: _localizationsDelegates.toList(),
|
||||||
|
child: new Title(
|
||||||
|
title: widget.title,
|
||||||
|
color: widget.color,
|
||||||
|
child: result,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,25 +23,30 @@ const TextStyle _kTextStyle = const TextStyle(
|
|||||||
);
|
);
|
||||||
|
|
||||||
/// Where to show a [Banner].
|
/// Where to show a [Banner].
|
||||||
|
///
|
||||||
|
/// The start and end locations are relative to the ambient [Directionality]
|
||||||
|
/// (which can be overridden by [Banner.layoutDirection]).
|
||||||
enum BannerLocation {
|
enum BannerLocation {
|
||||||
/// Show the banner in the top-right corner when the ambient [Directionality]
|
/// Show the banner in the top-right corner when the ambient [Directionality]
|
||||||
/// is [TextDirection.rtl] and in the top-left corner when the ambient
|
/// (or [Banner.layoutDirection]) is [TextDirection.rtl] and in the top-left
|
||||||
/// [Directionality] is [TextDirection.ltr].
|
/// corner when the ambient [Directionality] is [TextDirection.ltr].
|
||||||
topStart,
|
topStart,
|
||||||
|
|
||||||
/// Show the banner in the top-left corner when the ambient [Directionality]
|
/// Show the banner in the top-left corner when the ambient [Directionality]
|
||||||
/// is [TextDirection.rtl] and in the top-right corner when the ambient
|
/// (or [Banner.layoutDirection]) is [TextDirection.rtl] and in the top-right
|
||||||
/// [Directionality] is [TextDirection.ltr].
|
/// corner when the ambient [Directionality] is [TextDirection.ltr].
|
||||||
topEnd,
|
topEnd,
|
||||||
|
|
||||||
/// Show the banner in the bottom-right corner when the ambient
|
/// Show the banner in the bottom-right corner when the ambient
|
||||||
/// [Directionality] is [TextDirection.rtl] and in the bottom-left corner when
|
/// [Directionality] (or [Banner.layoutDirection]) is [TextDirection.rtl] and
|
||||||
/// the ambient [Directionality] is [TextDirection.ltr].
|
/// in the bottom-left corner when the ambient [Directionality] is
|
||||||
|
/// [TextDirection.ltr].
|
||||||
bottomStart,
|
bottomStart,
|
||||||
|
|
||||||
/// Show the banner in the bottom-left corner when the ambient
|
/// Show the banner in the bottom-left corner when the ambient
|
||||||
/// [Directionality] is [TextDirection.rtl] and in the bottom-right corner when
|
/// [Directionality] (or [Banner.layoutDirection]) is [TextDirection.rtl] and
|
||||||
/// the ambient [Directionality] is [TextDirection.ltr].
|
/// in the bottom-right corner when the ambient [Directionality] is
|
||||||
|
/// [TextDirection.ltr].
|
||||||
bottomEnd,
|
bottomEnd,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,11 +54,13 @@ enum BannerLocation {
|
|||||||
class BannerPainter extends CustomPainter {
|
class BannerPainter extends CustomPainter {
|
||||||
/// Creates a banner painter.
|
/// Creates a banner painter.
|
||||||
///
|
///
|
||||||
/// The [message], [textDirection], and [location] arguments must not be null.
|
/// The [message], [textDirection], [location], and [layoutDirection]
|
||||||
|
/// arguments must not be null.
|
||||||
BannerPainter({
|
BannerPainter({
|
||||||
@required this.message,
|
@required this.message,
|
||||||
@required this.textDirection,
|
@required this.textDirection,
|
||||||
@required this.location,
|
@required this.location,
|
||||||
|
@required this.layoutDirection,
|
||||||
this.color: _kColor,
|
this.color: _kColor,
|
||||||
this.textStyle: _kTextStyle,
|
this.textStyle: _kTextStyle,
|
||||||
}) : assert(message != null),
|
}) : assert(message != null),
|
||||||
@ -74,12 +81,21 @@ class BannerPainter extends CustomPainter {
|
|||||||
/// context, the English phrase will be on the right and the Hebrow phrase on
|
/// context, the English phrase will be on the right and the Hebrow phrase on
|
||||||
/// its left.
|
/// its left.
|
||||||
///
|
///
|
||||||
/// This value is also used to interpret the [location] of the banner.
|
/// See also [layoutDirection], which controls the interpretation of values in
|
||||||
|
/// [location].
|
||||||
final TextDirection textDirection;
|
final TextDirection textDirection;
|
||||||
|
|
||||||
/// Where to show the banner (e.g., the upper right corder).
|
/// Where to show the banner (e.g., the upper right corder).
|
||||||
final BannerLocation location;
|
final BannerLocation location;
|
||||||
|
|
||||||
|
/// The directionality of the layout.
|
||||||
|
///
|
||||||
|
/// This value is used to interpret the [location] of the banner.
|
||||||
|
///
|
||||||
|
/// See also [textDirection], which controls the reading direction of the
|
||||||
|
/// [message].
|
||||||
|
final TextDirection layoutDirection;
|
||||||
|
|
||||||
/// The color to paint behind the [message].
|
/// The color to paint behind the [message].
|
||||||
///
|
///
|
||||||
/// Defaults to a dark red.
|
/// Defaults to a dark red.
|
||||||
@ -136,8 +152,8 @@ class BannerPainter extends CustomPainter {
|
|||||||
|
|
||||||
double _translationX(double width) {
|
double _translationX(double width) {
|
||||||
assert(location != null);
|
assert(location != null);
|
||||||
assert(textDirection != null);
|
assert(layoutDirection != null);
|
||||||
switch (textDirection) {
|
switch (layoutDirection) {
|
||||||
case TextDirection.rtl:
|
case TextDirection.rtl:
|
||||||
switch (location) {
|
switch (location) {
|
||||||
case BannerLocation.bottomEnd:
|
case BannerLocation.bottomEnd:
|
||||||
@ -181,8 +197,8 @@ class BannerPainter extends CustomPainter {
|
|||||||
|
|
||||||
double get _rotation {
|
double get _rotation {
|
||||||
assert(location != null);
|
assert(location != null);
|
||||||
assert(textDirection != null);
|
assert(layoutDirection != null);
|
||||||
switch (textDirection) {
|
switch (layoutDirection) {
|
||||||
case TextDirection.rtl:
|
case TextDirection.rtl:
|
||||||
switch (location) {
|
switch (location) {
|
||||||
case BannerLocation.bottomStart:
|
case BannerLocation.bottomStart:
|
||||||
@ -215,7 +231,8 @@ class BannerPainter extends CustomPainter {
|
|||||||
///
|
///
|
||||||
/// See also:
|
/// See also:
|
||||||
///
|
///
|
||||||
/// * [CheckedModeBanner].
|
/// * [CheckedModeBanner], which the [WidgetsApp] widget includes by default in
|
||||||
|
/// debug mode, to show a banner that says "SLOW MODE".
|
||||||
class Banner extends StatelessWidget {
|
class Banner extends StatelessWidget {
|
||||||
/// Creates a banner.
|
/// Creates a banner.
|
||||||
///
|
///
|
||||||
@ -226,6 +243,7 @@ class Banner extends StatelessWidget {
|
|||||||
@required this.message,
|
@required this.message,
|
||||||
this.textDirection,
|
this.textDirection,
|
||||||
@required this.location,
|
@required this.location,
|
||||||
|
this.layoutDirection,
|
||||||
this.color: _kColor,
|
this.color: _kColor,
|
||||||
this.textStyle: _kTextStyle,
|
this.textStyle: _kTextStyle,
|
||||||
}) : assert(message != null),
|
}) : assert(message != null),
|
||||||
@ -250,11 +268,24 @@ class Banner extends StatelessWidget {
|
|||||||
/// its left.
|
/// its left.
|
||||||
///
|
///
|
||||||
/// Defaults to the ambient [Directionality], if any.
|
/// Defaults to the ambient [Directionality], if any.
|
||||||
|
///
|
||||||
|
/// See also [layoutDirection], which controls the interpretation of the
|
||||||
|
/// [location].
|
||||||
final TextDirection textDirection;
|
final TextDirection textDirection;
|
||||||
|
|
||||||
/// Where to show the banner (e.g., the upper right corder).
|
/// Where to show the banner (e.g., the upper right corder).
|
||||||
final BannerLocation location;
|
final BannerLocation location;
|
||||||
|
|
||||||
|
/// The directionality of the layout.
|
||||||
|
///
|
||||||
|
/// This is used to resolve the [location] values.
|
||||||
|
///
|
||||||
|
/// Defaults to the ambient [Directionality], if any.
|
||||||
|
///
|
||||||
|
/// See also [textDirection], which controls the reading direction of the
|
||||||
|
/// [message].
|
||||||
|
final TextDirection layoutDirection;
|
||||||
|
|
||||||
/// The color of the banner.
|
/// The color of the banner.
|
||||||
final Color color;
|
final Color color;
|
||||||
|
|
||||||
@ -268,6 +299,7 @@ class Banner extends StatelessWidget {
|
|||||||
message: message,
|
message: message,
|
||||||
textDirection: textDirection ?? Directionality.of(context),
|
textDirection: textDirection ?? Directionality.of(context),
|
||||||
location: location,
|
location: location,
|
||||||
|
layoutDirection: layoutDirection ?? Directionality.of(context),
|
||||||
color: color,
|
color: color,
|
||||||
textStyle: textStyle,
|
textStyle: textStyle,
|
||||||
),
|
),
|
||||||
@ -281,6 +313,7 @@ class Banner extends StatelessWidget {
|
|||||||
description.add(new StringProperty('message', message, showName: false));
|
description.add(new StringProperty('message', message, showName: false));
|
||||||
description.add(new EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
|
description.add(new EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
|
||||||
description.add(new EnumProperty<BannerLocation>('location', location));
|
description.add(new EnumProperty<BannerLocation>('location', location));
|
||||||
|
description.add(new EnumProperty<TextDirection>('layoutDirection', layoutDirection, defaultValue: null));
|
||||||
description.add(new DiagnosticsProperty<Color>('color', color, showName: false));
|
description.add(new DiagnosticsProperty<Color>('color', color, showName: false));
|
||||||
textStyle?.debugFillProperties(description, prefix: 'text ');
|
textStyle?.debugFillProperties(description, prefix: 'text ');
|
||||||
}
|
}
|
||||||
|
@ -248,6 +248,13 @@ abstract class PaintPattern {
|
|||||||
void something(PaintPatternPredicate predicate);
|
void something(PaintPatternPredicate predicate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _MismatchedCall {
|
||||||
|
const _MismatchedCall(this.message, this.callIntroduction, this.call) : assert(call != null);
|
||||||
|
final String message;
|
||||||
|
final String callIntroduction;
|
||||||
|
final RecordedInvocation call;
|
||||||
|
}
|
||||||
|
|
||||||
abstract class _TestRecordingCanvasMatcher extends Matcher {
|
abstract class _TestRecordingCanvasMatcher extends Matcher {
|
||||||
@override
|
@override
|
||||||
bool matches(Object object, Map<dynamic, dynamic> matchState) {
|
bool matches(Object object, Map<dynamic, dynamic> matchState) {
|
||||||
@ -276,17 +283,16 @@ abstract class _TestRecordingCanvasMatcher extends Matcher {
|
|||||||
final StringBuffer description = new StringBuffer();
|
final StringBuffer description = new StringBuffer();
|
||||||
final bool result = _evaluatePredicates(canvas.invocations, description);
|
final bool result = _evaluatePredicates(canvas.invocations, description);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
const String indent = '\n '; // the length of ' Which: ' in spaces, plus two more
|
|
||||||
if (canvas.invocations.isNotEmpty)
|
if (canvas.invocations.isNotEmpty)
|
||||||
description.write(' The complete display list was:');
|
description.write('The complete display list was:');
|
||||||
for (Invocation call in canvas.invocations)
|
for (RecordedInvocation call in canvas.invocations)
|
||||||
description.write('$indent${_describeInvocation(call)}');
|
description.write('\n * $call');
|
||||||
|
matchState[this] = 'did not match the pattern.\n$description';
|
||||||
}
|
}
|
||||||
matchState[this] = description.toString();
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _evaluatePredicates(Iterable<Invocation> calls, StringBuffer description);
|
bool _evaluatePredicates(Iterable<RecordedInvocation> calls, StringBuffer description);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Description describeMismatch(
|
Description describeMismatch(
|
||||||
@ -306,10 +312,13 @@ class _TestRecordingCanvasPaintsNothingMatcher extends _TestRecordingCanvasMatch
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool _evaluatePredicates(Iterable<Invocation> calls, StringBuffer description) {
|
bool _evaluatePredicates(Iterable<RecordedInvocation> calls, StringBuffer description) {
|
||||||
if (calls.isEmpty)
|
if (calls.isEmpty)
|
||||||
return true;
|
return true;
|
||||||
description.write('painted the following.');
|
description.write(
|
||||||
|
'painted something, the first call having the following stack:\n'
|
||||||
|
'${calls.first.stackToString(indent: " ")}\n'
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -409,32 +418,42 @@ class _TestRecordingCanvasPatternMatcher extends _TestRecordingCanvasMatcher imp
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool _evaluatePredicates(Iterable<Invocation> calls, StringBuffer description) {
|
bool _evaluatePredicates(Iterable<RecordedInvocation> calls, StringBuffer description) {
|
||||||
if (calls.isEmpty) {
|
if (calls.isEmpty) {
|
||||||
description.write('painted nothing.');
|
description.writeln('It painted nothing.');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (_predicates.isEmpty) {
|
if (_predicates.isEmpty) {
|
||||||
description.write(
|
description.writeln(
|
||||||
'painted something, but you must now add a pattern to the paints matcher '
|
'It painted something, but you must now add a pattern to the paints matcher '
|
||||||
'in the test to verify that it matches the important parts of the following.'
|
'in the test to verify that it matches the important parts of the following.'
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final Iterator<_PaintPredicate> predicate = _predicates.iterator;
|
final Iterator<_PaintPredicate> predicate = _predicates.iterator;
|
||||||
final Iterator<Invocation> call = calls.iterator..moveNext();
|
final Iterator<RecordedInvocation> call = calls.iterator..moveNext();
|
||||||
try {
|
try {
|
||||||
while (predicate.moveNext()) {
|
while (predicate.moveNext()) {
|
||||||
if (call.current == null) {
|
if (call.current == null) {
|
||||||
throw 'painted less on its canvas than the paint pattern expected. '
|
throw 'It painted less on its canvas than the paint pattern expected. '
|
||||||
'The first missing paint call was: ${predicate.current}';
|
'The first missing paint call was: ${predicate.current}';
|
||||||
}
|
}
|
||||||
predicate.current.match(call);
|
predicate.current.match(call);
|
||||||
}
|
}
|
||||||
assert(predicate.current == null);
|
assert(predicate.current == null);
|
||||||
// We allow painting more than expected.
|
// We allow painting more than expected.
|
||||||
|
} on _MismatchedCall catch (data) {
|
||||||
|
description.writeln(data.message);
|
||||||
|
description.writeln(data.callIntroduction);
|
||||||
|
description.writeln(data.call.stackToString(indent: ' '));
|
||||||
|
return false;
|
||||||
} on String catch (s) {
|
} on String catch (s) {
|
||||||
description.write(s);
|
description.writeln(s);
|
||||||
|
if (call.current != null) {
|
||||||
|
description.write('The stack of the offending call was:\n${call.current.stackToString(indent: " ")}\n');
|
||||||
|
} else {
|
||||||
|
description.write('The stack of the first call was:\n${calls.first.stackToString(indent: " ")}\n');
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -442,7 +461,7 @@ class _TestRecordingCanvasPatternMatcher extends _TestRecordingCanvasMatcher imp
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract class _PaintPredicate {
|
abstract class _PaintPredicate {
|
||||||
void match(Iterator<Invocation> call);
|
void match(Iterator<RecordedInvocation> call);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
@ -468,20 +487,24 @@ abstract class _DrawCommandPaintPredicate extends _PaintPredicate {
|
|||||||
String get methodName => _symbolName(symbol);
|
String get methodName => _symbolName(symbol);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void match(Iterator<Invocation> call) {
|
void match(Iterator<RecordedInvocation> call) {
|
||||||
int others = 0;
|
int others = 0;
|
||||||
final Invocation firstCall = call.current;
|
final RecordedInvocation firstCall = call.current;
|
||||||
while (!call.current.isMethod || call.current.memberName != symbol) {
|
while (!call.current.invocation.isMethod || call.current.invocation.memberName != symbol) {
|
||||||
others += 1;
|
others += 1;
|
||||||
if (!call.moveNext())
|
if (!call.moveNext())
|
||||||
throw 'called $others other method${ others == 1 ? "" : "s" } on the canvas, '
|
throw new _MismatchedCall(
|
||||||
'the first of which was ${_describeInvocation(firstCall)}, but did not '
|
'It called $others other method${ others == 1 ? "" : "s" } on the canvas, '
|
||||||
'call $methodName at the time where $this was expected.';
|
'the first of which was $firstCall, but did not '
|
||||||
|
'call $methodName at the time where $this was expected.',
|
||||||
|
'The stack for the call to $firstCall was:',
|
||||||
|
firstCall,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
final int actualArgumentCount = call.current.positionalArguments.length;
|
final int actualArgumentCount = call.current.invocation.positionalArguments.length;
|
||||||
if (actualArgumentCount != argumentCount)
|
if (actualArgumentCount != argumentCount)
|
||||||
throw 'called $methodName with $actualArgumentCount argument${actualArgumentCount == 1 ? "" : "s"}; expected $argumentCount.';
|
throw 'It called $methodName with $actualArgumentCount argument${actualArgumentCount == 1 ? "" : "s"}; expected $argumentCount.';
|
||||||
verifyArguments(call.current.positionalArguments);
|
verifyArguments(call.current.invocation.positionalArguments);
|
||||||
call.moveNext();
|
call.moveNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -490,17 +513,17 @@ abstract class _DrawCommandPaintPredicate extends _PaintPredicate {
|
|||||||
void verifyArguments(List<dynamic> arguments) {
|
void verifyArguments(List<dynamic> arguments) {
|
||||||
final Paint paintArgument = arguments[paintArgumentIndex];
|
final Paint paintArgument = arguments[paintArgumentIndex];
|
||||||
if (color != null && paintArgument.color != color)
|
if (color != null && paintArgument.color != color)
|
||||||
throw 'called $methodName with a paint whose color, ${paintArgument.color}, was not exactly the expected color ($color).';
|
throw 'It called $methodName with a paint whose color, ${paintArgument.color}, was not exactly the expected color ($color).';
|
||||||
if (strokeWidth != null && paintArgument.strokeWidth != strokeWidth)
|
if (strokeWidth != null && paintArgument.strokeWidth != strokeWidth)
|
||||||
throw 'called $methodName with a paint whose strokeWidth, ${paintArgument.strokeWidth}, was not exactly the expected strokeWidth ($strokeWidth).';
|
throw 'It called $methodName with a paint whose strokeWidth, ${paintArgument.strokeWidth}, was not exactly the expected strokeWidth ($strokeWidth).';
|
||||||
if (hasMaskFilter != null && (paintArgument.maskFilter != null) != hasMaskFilter) {
|
if (hasMaskFilter != null && (paintArgument.maskFilter != null) != hasMaskFilter) {
|
||||||
if (hasMaskFilter)
|
if (hasMaskFilter)
|
||||||
throw 'called $methodName with a paint that did not have a mask filter, despite expecting one.';
|
throw 'It called $methodName with a paint that did not have a mask filter, despite expecting one.';
|
||||||
else
|
else
|
||||||
throw 'called $methodName with a paint that did have a mask filter, despite not expecting one.';
|
throw 'It called $methodName with a paint that did have a mask filter, despite not expecting one.';
|
||||||
}
|
}
|
||||||
if (style != null && paintArgument.style != style)
|
if (style != null && paintArgument.style != style)
|
||||||
throw 'called $methodName with a paint whose style, ${paintArgument.style}, was not exactly the expected style ($style).';
|
throw 'It called $methodName with a paint whose style, ${paintArgument.style}, was not exactly the expected style ($style).';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -544,7 +567,7 @@ class _OneParameterPaintPredicate<T> extends _DrawCommandPaintPredicate {
|
|||||||
super.verifyArguments(arguments);
|
super.verifyArguments(arguments);
|
||||||
final T actual = arguments[0];
|
final T actual = arguments[0];
|
||||||
if (expected != null && actual != expected)
|
if (expected != null && actual != expected)
|
||||||
throw 'called $methodName with $T, $actual, which was not exactly the expected $T ($expected).';
|
throw 'It called $methodName with $T, $actual, which was not exactly the expected $T ($expected).';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -601,16 +624,16 @@ class _CirclePaintPredicate extends _DrawCommandPaintPredicate {
|
|||||||
if (x != null && y != null) {
|
if (x != null && y != null) {
|
||||||
final Offset point = new Offset(x, y);
|
final Offset point = new Offset(x, y);
|
||||||
if (point != pointArgument)
|
if (point != pointArgument)
|
||||||
throw 'called $methodName with a center coordinate, $pointArgument, which was not exactly the expected coordinate ($point).';
|
throw 'It called $methodName with a center coordinate, $pointArgument, which was not exactly the expected coordinate ($point).';
|
||||||
} else {
|
} else {
|
||||||
if (x != null && pointArgument.dx != x)
|
if (x != null && pointArgument.dx != x)
|
||||||
throw 'called $methodName with a center coordinate, $pointArgument, whose x-coordinate not exactly the expected coordinate (${x.toStringAsFixed(1)}).';
|
throw 'It called $methodName with a center coordinate, $pointArgument, whose x-coordinate not exactly the expected coordinate (${x.toStringAsFixed(1)}).';
|
||||||
if (y != null && pointArgument.dy != y)
|
if (y != null && pointArgument.dy != y)
|
||||||
throw 'called $methodName with a center coordinate, $pointArgument, whose y-coordinate not exactly the expected coordinate (${y.toStringAsFixed(1)}).';
|
throw 'It called $methodName with a center coordinate, $pointArgument, whose y-coordinate not exactly the expected coordinate (${y.toStringAsFixed(1)}).';
|
||||||
}
|
}
|
||||||
final double radiusArgument = arguments[1];
|
final double radiusArgument = arguments[1];
|
||||||
if (radius != null && radiusArgument != radius)
|
if (radius != null && radiusArgument != radius)
|
||||||
throw 'called $methodName with radius, ${radiusArgument.toStringAsFixed(1)}, which was not exactly the expected radius (${radius.toStringAsFixed(1)}).';
|
throw 'It called $methodName with radius, ${radiusArgument.toStringAsFixed(1)}, which was not exactly the expected radius (${radius.toStringAsFixed(1)}).';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -654,24 +677,24 @@ class _SomethingPaintPredicate extends _PaintPredicate {
|
|||||||
final PaintPatternPredicate predicate;
|
final PaintPatternPredicate predicate;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void match(Iterator<Invocation> call) {
|
void match(Iterator<RecordedInvocation> call) {
|
||||||
assert(predicate != null);
|
assert(predicate != null);
|
||||||
Invocation currentCall;
|
RecordedInvocation currentCall;
|
||||||
do {
|
do {
|
||||||
currentCall = call.current;
|
currentCall = call.current;
|
||||||
if (currentCall == null)
|
if (currentCall == null)
|
||||||
throw 'did not call anything that was matched by the predicate passed to a "something" step of the paint pattern.';
|
throw 'It did not call anything that was matched by the predicate passed to a "something" step of the paint pattern.';
|
||||||
if (!currentCall.isMethod)
|
if (!currentCall.invocation.isMethod)
|
||||||
throw 'called ${_describeInvocation(currentCall)}, which was not a method, when the paint pattern expected a method call';
|
throw 'It called $currentCall, which was not a method, when the paint pattern expected a method call';
|
||||||
call.moveNext();
|
call.moveNext();
|
||||||
} while (!_runPredicate(currentCall.memberName, currentCall.positionalArguments));
|
} while (!_runPredicate(currentCall.invocation.memberName, currentCall.invocation.positionalArguments));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _runPredicate(Symbol methodName, List<dynamic> arguments) {
|
bool _runPredicate(Symbol methodName, List<dynamic> arguments) {
|
||||||
try {
|
try {
|
||||||
return predicate(methodName, arguments);
|
return predicate(methodName, arguments);
|
||||||
} on String catch (s) {
|
} on String catch (s) {
|
||||||
throw 'painted something that the predicate passed to a "something" step '
|
throw 'It painted something that the predicate passed to a "something" step '
|
||||||
'in the paint pattern considered incorrect:\n $s\n ';
|
'in the paint pattern considered incorrect:\n $s\n ';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -688,23 +711,28 @@ class _FunctionPaintPredicate extends _PaintPredicate {
|
|||||||
final List<dynamic> arguments;
|
final List<dynamic> arguments;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void match(Iterator<Invocation> call) {
|
void match(Iterator<RecordedInvocation> call) {
|
||||||
int others = 0;
|
int others = 0;
|
||||||
final Invocation firstCall = call.current;
|
final RecordedInvocation firstCall = call.current;
|
||||||
while (!call.current.isMethod || call.current.memberName != symbol) {
|
while (!call.current.invocation.isMethod || call.current.invocation.memberName != symbol) {
|
||||||
others += 1;
|
others += 1;
|
||||||
if (!call.moveNext())
|
if (!call.moveNext())
|
||||||
throw 'called $others other method${ others == 1 ? "" : "s" } on the canvas, '
|
throw new _MismatchedCall(
|
||||||
'the first of which was ${_describeInvocation(firstCall)}, but did not '
|
'It called $others other method${ others == 1 ? "" : "s" } on the canvas, '
|
||||||
'call ${_symbolName(symbol)}() at the time where $this was expected.';
|
'the first of which was $firstCall, but did not '
|
||||||
|
'call ${_symbolName(symbol)}() at the time where $this was expected.',
|
||||||
|
'The first method that was called when the call to ${_symbolName(symbol)}() '
|
||||||
|
'was expected, $firstCall, was called with the following stack:',
|
||||||
|
firstCall,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (call.current.positionalArguments.length != arguments.length)
|
if (call.current.invocation.positionalArguments.length != arguments.length)
|
||||||
throw 'called ${_symbolName(symbol)} with ${call.current.positionalArguments.length} arguments; expected ${arguments.length}.';
|
throw 'It called ${_symbolName(symbol)} with ${call.current.invocation.positionalArguments.length} arguments; expected ${arguments.length}.';
|
||||||
for (int index = 0; index < arguments.length; index += 1) {
|
for (int index = 0; index < arguments.length; index += 1) {
|
||||||
final dynamic actualArgument = call.current.positionalArguments[index];
|
final dynamic actualArgument = call.current.invocation.positionalArguments[index];
|
||||||
final dynamic desiredArgument = arguments[index];
|
final dynamic desiredArgument = arguments[index];
|
||||||
if (desiredArgument != null && desiredArgument != actualArgument)
|
if (desiredArgument != null && desiredArgument != actualArgument)
|
||||||
throw 'called ${_symbolName(symbol)} with argument $index having value ${_valueName(actualArgument)} when ${_valueName(desiredArgument)} was expected.';
|
throw 'It called ${_symbolName(symbol)} with argument $index having value ${_valueName(actualArgument)} when ${_valueName(desiredArgument)} was expected.';
|
||||||
}
|
}
|
||||||
call.moveNext();
|
call.moveNext();
|
||||||
}
|
}
|
||||||
@ -720,24 +748,29 @@ class _FunctionPaintPredicate extends _PaintPredicate {
|
|||||||
|
|
||||||
class _SaveRestorePairPaintPredicate extends _PaintPredicate {
|
class _SaveRestorePairPaintPredicate extends _PaintPredicate {
|
||||||
@override
|
@override
|
||||||
void match(Iterator<Invocation> call) {
|
void match(Iterator<RecordedInvocation> call) {
|
||||||
int others = 0;
|
int others = 0;
|
||||||
final Invocation firstCall = call.current;
|
final RecordedInvocation firstCall = call.current;
|
||||||
while (!call.current.isMethod || call.current.memberName != #save) {
|
while (!call.current.invocation.isMethod || call.current.invocation.memberName != #save) {
|
||||||
others += 1;
|
others += 1;
|
||||||
if (!call.moveNext())
|
if (!call.moveNext())
|
||||||
throw 'called $others other method${ others == 1 ? "" : "s" } on the canvas, '
|
throw new _MismatchedCall(
|
||||||
'the first of which was ${_describeInvocation(firstCall)}, but did not '
|
'It called $others other method${ others == 1 ? "" : "s" } on the canvas, '
|
||||||
'call save() at the time where $this was expected.';
|
'the first of which was $firstCall, but did not '
|
||||||
|
'call save() at the time where $this was expected.',
|
||||||
|
'The first method that was called when the call to save() '
|
||||||
|
'was expected, $firstCall, was called with the following stack:',
|
||||||
|
firstCall,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
int depth = 1;
|
int depth = 1;
|
||||||
while (depth > 0) {
|
while (depth > 0) {
|
||||||
if (!call.moveNext())
|
if (!call.moveNext())
|
||||||
throw 'did not have a matching restore() for the save() that was found where $this was expected.';
|
throw 'It did not have a matching restore() for the save() that was found where $this was expected.';
|
||||||
if (call.current.isMethod) {
|
if (call.current.invocation.isMethod) {
|
||||||
if (call.current.memberName == #save)
|
if (call.current.invocation.memberName == #save)
|
||||||
depth += 1;
|
depth += 1;
|
||||||
else if (call.current.memberName == #restore)
|
else if (call.current.invocation.memberName == #restore)
|
||||||
depth -= 1;
|
depth -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -761,25 +794,3 @@ String _symbolName(Symbol symbol) {
|
|||||||
final String s = '$symbol';
|
final String s = '$symbol';
|
||||||
return s.substring(8, s.length - 2);
|
return s.substring(8, s.length - 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Workaround for https://github.com/dart-lang/sdk/issues/28373
|
|
||||||
String _describeInvocation(Invocation call) {
|
|
||||||
final StringBuffer buffer = new StringBuffer();
|
|
||||||
buffer.write(_symbolName(call.memberName));
|
|
||||||
if (call.isSetter) {
|
|
||||||
buffer.write(call.positionalArguments[0].toString());
|
|
||||||
} else if (call.isMethod) {
|
|
||||||
buffer.write('(');
|
|
||||||
buffer.writeAll(call.positionalArguments.map<String>(_valueName), ', ');
|
|
||||||
String separator = call.positionalArguments.isEmpty ? '' : ', ';
|
|
||||||
call.namedArguments.forEach((Symbol name, Object value) {
|
|
||||||
buffer.write(separator);
|
|
||||||
buffer.write(_symbolName(name));
|
|
||||||
buffer.write(': ');
|
|
||||||
buffer.write(_valueName(value));
|
|
||||||
separator = ', ';
|
|
||||||
});
|
|
||||||
buffer.write(')');
|
|
||||||
}
|
|
||||||
return buffer.toString();
|
|
||||||
}
|
|
||||||
|
@ -2,8 +2,34 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
|
|
||||||
|
/// An [Invocation] and the [stack] trace that led to it.
|
||||||
|
///
|
||||||
|
/// Used by [TestRecordingCanvas] to trace canvas calls.
|
||||||
|
class RecordedInvocation {
|
||||||
|
/// Create a record for an invocation list.
|
||||||
|
const RecordedInvocation(this.invocation, { this.stack });
|
||||||
|
|
||||||
|
/// The method that was called and its arguments.
|
||||||
|
final Invocation invocation;
|
||||||
|
|
||||||
|
/// The stack trace at the time of the method call.
|
||||||
|
final StackTrace stack;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => _describeInvocation(invocation);
|
||||||
|
|
||||||
|
/// Converts [stack] to a string using the [FlutterError.defaultStackFilter] logic.
|
||||||
|
String stackToString({ String indent: '' }) {
|
||||||
|
assert(indent != null);
|
||||||
|
return indent + FlutterError.defaultStackFilter(
|
||||||
|
stack.toString().trimRight().split('\n')
|
||||||
|
).join('\n$indent');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A [Canvas] for tests that records its method calls.
|
/// A [Canvas] for tests that records its method calls.
|
||||||
///
|
///
|
||||||
/// This class can be used in conjuction with [TestRecordingPaintingContext]
|
/// This class can be used in conjuction with [TestRecordingPaintingContext]
|
||||||
@ -25,7 +51,7 @@ import 'package:flutter/rendering.dart';
|
|||||||
/// pattern matching API over [TestRecordingCanvas].
|
/// pattern matching API over [TestRecordingCanvas].
|
||||||
class TestRecordingCanvas implements Canvas {
|
class TestRecordingCanvas implements Canvas {
|
||||||
/// All of the method calls on this canvas.
|
/// All of the method calls on this canvas.
|
||||||
final List<Invocation> invocations = <Invocation>[];
|
final List<RecordedInvocation> invocations = <RecordedInvocation>[];
|
||||||
|
|
||||||
int _saveCount = 0;
|
int _saveCount = 0;
|
||||||
|
|
||||||
@ -35,25 +61,25 @@ class TestRecordingCanvas implements Canvas {
|
|||||||
@override
|
@override
|
||||||
void save() {
|
void save() {
|
||||||
_saveCount += 1;
|
_saveCount += 1;
|
||||||
invocations.add(new _MethodCall(#save));
|
invocations.add(new RecordedInvocation(new _MethodCall(#save), stack: StackTrace.current));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void saveLayer(Rect bounds, Paint paint) {
|
void saveLayer(Rect bounds, Paint paint) {
|
||||||
_saveCount += 1;
|
_saveCount += 1;
|
||||||
invocations.add(new _MethodCall(#saveLayer, <dynamic>[bounds, paint]));
|
invocations.add(new RecordedInvocation(new _MethodCall(#saveLayer, <dynamic>[bounds, paint]), stack: StackTrace.current));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void restore() {
|
void restore() {
|
||||||
_saveCount -= 1;
|
_saveCount -= 1;
|
||||||
assert(_saveCount >= 0);
|
assert(_saveCount >= 0);
|
||||||
invocations.add(new _MethodCall(#restore));
|
invocations.add(new RecordedInvocation(new _MethodCall(#restore), stack: StackTrace.current));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void noSuchMethod(Invocation invocation) {
|
void noSuchMethod(Invocation invocation) {
|
||||||
invocations.add(invocation);
|
invocations.add(new RecordedInvocation(invocation, stack: StackTrace.current));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,3 +127,39 @@ class _MethodCall implements Invocation {
|
|||||||
@override
|
@override
|
||||||
List<dynamic> get positionalArguments => _arguments;
|
List<dynamic> get positionalArguments => _arguments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _valueName(Object value) {
|
||||||
|
if (value is double)
|
||||||
|
return value.toStringAsFixed(1);
|
||||||
|
return value.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Workaround for https://github.com/dart-lang/sdk/issues/28372
|
||||||
|
String _symbolName(Symbol symbol) {
|
||||||
|
// WARNING: Assumes a fixed format for Symbol.toString which is *not*
|
||||||
|
// guaranteed anywhere.
|
||||||
|
final String s = '$symbol';
|
||||||
|
return s.substring(8, s.length - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Workaround for https://github.com/dart-lang/sdk/issues/28373
|
||||||
|
String _describeInvocation(Invocation call) {
|
||||||
|
final StringBuffer buffer = new StringBuffer();
|
||||||
|
buffer.write(_symbolName(call.memberName));
|
||||||
|
if (call.isSetter) {
|
||||||
|
buffer.write(call.positionalArguments[0].toString());
|
||||||
|
} else if (call.isMethod) {
|
||||||
|
buffer.write('(');
|
||||||
|
buffer.writeAll(call.positionalArguments.map<String>(_valueName), ', ');
|
||||||
|
String separator = call.positionalArguments.isEmpty ? '' : ', ';
|
||||||
|
call.namedArguments.forEach((Symbol name, Object value) {
|
||||||
|
buffer.write(separator);
|
||||||
|
buffer.write(_symbolName(name));
|
||||||
|
buffer.write(': ');
|
||||||
|
buffer.write(_valueName(value));
|
||||||
|
separator = ', ';
|
||||||
|
});
|
||||||
|
buffer.write(')');
|
||||||
|
}
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
@ -27,14 +27,11 @@ class TestRoute<T> extends PageRoute<T> {
|
|||||||
|
|
||||||
Future<Null> pumpApp(WidgetTester tester) async {
|
Future<Null> pumpApp(WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
new Directionality(
|
new WidgetsApp(
|
||||||
textDirection: TextDirection.ltr,
|
color: const Color(0xFF333333),
|
||||||
child: new WidgetsApp(
|
onGenerateRoute: (RouteSettings settings) {
|
||||||
color: const Color(0xFF333333),
|
return new TestRoute<Null>(settings: settings, child: new Container());
|
||||||
onGenerateRoute: (RouteSettings settings) {
|
},
|
||||||
return new TestRoute<Null>(settings: settings, child: new Container());
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,10 @@
|
|||||||
|
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
import '../rendering/mock_canvas.dart';
|
||||||
|
|
||||||
class TestCanvas implements Canvas {
|
class TestCanvas implements Canvas {
|
||||||
final List<Invocation> invocations = <Invocation>[];
|
final List<Invocation> invocations = <Invocation>[];
|
||||||
@ -17,11 +19,16 @@ class TestCanvas implements Canvas {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
// the textDirection values below are intentionally sometimes different and
|
||||||
|
// sometimes the same as the layoutDirection, to make sure that they don't
|
||||||
|
// affect the layout.
|
||||||
|
|
||||||
test('A Banner with a location of topStart paints in the top left (LTR)', () {
|
test('A Banner with a location of topStart paints in the top left (LTR)', () {
|
||||||
final BannerPainter bannerPainter = new BannerPainter(
|
final BannerPainter bannerPainter = new BannerPainter(
|
||||||
message: 'foo',
|
message: 'foo',
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.rtl,
|
||||||
location: BannerLocation.topStart
|
location: BannerLocation.topStart,
|
||||||
|
layoutDirection: TextDirection.ltr,
|
||||||
);
|
);
|
||||||
|
|
||||||
final TestCanvas canvas = new TestCanvas();
|
final TestCanvas canvas = new TestCanvas();
|
||||||
@ -47,8 +54,9 @@ void main() {
|
|||||||
test('A Banner with a location of topStart paints in the top right (RTL)', () {
|
test('A Banner with a location of topStart paints in the top right (RTL)', () {
|
||||||
final BannerPainter bannerPainter = new BannerPainter(
|
final BannerPainter bannerPainter = new BannerPainter(
|
||||||
message: 'foo',
|
message: 'foo',
|
||||||
textDirection: TextDirection.rtl,
|
textDirection: TextDirection.ltr,
|
||||||
location: BannerLocation.topStart,
|
location: BannerLocation.topStart,
|
||||||
|
layoutDirection: TextDirection.rtl,
|
||||||
);
|
);
|
||||||
|
|
||||||
final TestCanvas canvas = new TestCanvas();
|
final TestCanvas canvas = new TestCanvas();
|
||||||
@ -75,7 +83,8 @@ void main() {
|
|||||||
final BannerPainter bannerPainter = new BannerPainter(
|
final BannerPainter bannerPainter = new BannerPainter(
|
||||||
message: 'foo',
|
message: 'foo',
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
location: BannerLocation.topEnd
|
location: BannerLocation.topEnd,
|
||||||
|
layoutDirection: TextDirection.ltr,
|
||||||
);
|
);
|
||||||
|
|
||||||
final TestCanvas canvas = new TestCanvas();
|
final TestCanvas canvas = new TestCanvas();
|
||||||
@ -103,6 +112,7 @@ void main() {
|
|||||||
message: 'foo',
|
message: 'foo',
|
||||||
textDirection: TextDirection.rtl,
|
textDirection: TextDirection.rtl,
|
||||||
location: BannerLocation.topEnd,
|
location: BannerLocation.topEnd,
|
||||||
|
layoutDirection: TextDirection.rtl,
|
||||||
);
|
);
|
||||||
|
|
||||||
final TestCanvas canvas = new TestCanvas();
|
final TestCanvas canvas = new TestCanvas();
|
||||||
@ -129,7 +139,8 @@ void main() {
|
|||||||
final BannerPainter bannerPainter = new BannerPainter(
|
final BannerPainter bannerPainter = new BannerPainter(
|
||||||
message: 'foo',
|
message: 'foo',
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
location: BannerLocation.bottomStart
|
location: BannerLocation.bottomStart,
|
||||||
|
layoutDirection: TextDirection.ltr,
|
||||||
);
|
);
|
||||||
|
|
||||||
final TestCanvas canvas = new TestCanvas();
|
final TestCanvas canvas = new TestCanvas();
|
||||||
@ -157,6 +168,7 @@ void main() {
|
|||||||
message: 'foo',
|
message: 'foo',
|
||||||
textDirection: TextDirection.rtl,
|
textDirection: TextDirection.rtl,
|
||||||
location: BannerLocation.bottomStart,
|
location: BannerLocation.bottomStart,
|
||||||
|
layoutDirection: TextDirection.rtl,
|
||||||
);
|
);
|
||||||
|
|
||||||
final TestCanvas canvas = new TestCanvas();
|
final TestCanvas canvas = new TestCanvas();
|
||||||
@ -182,8 +194,9 @@ void main() {
|
|||||||
test('A Banner with a location of bottomEnd paints in the bottom right (LTR)', () {
|
test('A Banner with a location of bottomEnd paints in the bottom right (LTR)', () {
|
||||||
final BannerPainter bannerPainter = new BannerPainter(
|
final BannerPainter bannerPainter = new BannerPainter(
|
||||||
message: 'foo',
|
message: 'foo',
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.rtl,
|
||||||
location: BannerLocation.bottomEnd
|
location: BannerLocation.bottomEnd,
|
||||||
|
layoutDirection: TextDirection.ltr,
|
||||||
);
|
);
|
||||||
|
|
||||||
final TestCanvas canvas = new TestCanvas();
|
final TestCanvas canvas = new TestCanvas();
|
||||||
@ -209,8 +222,9 @@ void main() {
|
|||||||
test('A Banner with a location of bottomEnd paints in the bottom left (RTL)', () {
|
test('A Banner with a location of bottomEnd paints in the bottom left (RTL)', () {
|
||||||
final BannerPainter bannerPainter = new BannerPainter(
|
final BannerPainter bannerPainter = new BannerPainter(
|
||||||
message: 'foo',
|
message: 'foo',
|
||||||
textDirection: TextDirection.rtl,
|
textDirection: TextDirection.ltr,
|
||||||
location: BannerLocation.bottomEnd,
|
location: BannerLocation.bottomEnd,
|
||||||
|
layoutDirection: TextDirection.rtl,
|
||||||
);
|
);
|
||||||
|
|
||||||
final TestCanvas canvas = new TestCanvas();
|
final TestCanvas canvas = new TestCanvas();
|
||||||
@ -233,4 +247,34 @@ void main() {
|
|||||||
expect(rotateCommand.positionalArguments[0], equals(math.PI / 4.0));
|
expect(rotateCommand.positionalArguments[0], equals(math.PI / 4.0));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('Banner widget', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
const Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: const Banner(message: 'Hello', location: BannerLocation.topEnd),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(find.byType(CustomPaint), paints
|
||||||
|
..save
|
||||||
|
..translate(x: 800.0, y: 0.0)
|
||||||
|
..rotate(angle: math.PI / 4.0)
|
||||||
|
..rect(rect: new Rect.fromLTRB(-40.0, 28.0, 40.0, 40.0), color: const Color(0x7f000000), hasMaskFilter: true)
|
||||||
|
..rect(rect: new Rect.fromLTRB(-40.0, 28.0, 40.0, 40.0), color: const Color(0xa0b71c1c), hasMaskFilter: false)
|
||||||
|
..paragraph(offset: const Offset(-40.0, 29.0))
|
||||||
|
..restore
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Banner widget in MaterialApp', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(new MaterialApp(home: const Placeholder()));
|
||||||
|
expect(find.byType(CheckedModeBanner), paints
|
||||||
|
..save
|
||||||
|
..translate(x: 800.0, y: 0.0)
|
||||||
|
..rotate(angle: math.PI / 4.0)
|
||||||
|
..rect(rect: new Rect.fromLTRB(-40.0, 28.0, 40.0, 40.0), color: const Color(0x7f000000), hasMaskFilter: true)
|
||||||
|
..rect(rect: new Rect.fromLTRB(-40.0, 28.0, 40.0, 40.0), color: const Color(0xa0b71c1c), hasMaskFilter: false)
|
||||||
|
..paragraph(offset: const Offset(-40.0, 24.0))
|
||||||
|
..restore
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user