mirror of
https://github.com/flutter/flutter
synced 2024-10-14 04:02:56 +00:00
Add RTL support to AnimatedContainer (#11910)
Also, fix the interpolation between visual and directional fractional offsets. The interpolation now works visually in whatever direction the result eventually gets resolved into. Fixes #11847 Fixes #11357
This commit is contained in:
parent
74b0bf6480
commit
baf3b45e0d
|
@ -752,10 +752,10 @@ class EdgeInsetsDirectional extends EdgeInsetsGeometry {
|
|||
EdgeInsets resolve(TextDirection direction) {
|
||||
assert(direction != null);
|
||||
switch (direction) {
|
||||
case TextDirection.ltr:
|
||||
return new EdgeInsets.fromLTRB(start, top, end, bottom);
|
||||
case TextDirection.rtl:
|
||||
return new EdgeInsets.fromLTRB(end, top, start, bottom);
|
||||
case TextDirection.ltr:
|
||||
return new EdgeInsets.fromLTRB(start, top, end, bottom);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -856,10 +856,10 @@ class _MixedEdgeInsets extends EdgeInsetsGeometry {
|
|||
EdgeInsets resolve(TextDirection direction) {
|
||||
assert(direction != null);
|
||||
switch (direction) {
|
||||
case TextDirection.rtl:
|
||||
return new EdgeInsets.fromLTRB(_end + _left, _top, _start + _right, _bottom);
|
||||
case TextDirection.ltr:
|
||||
return new EdgeInsets.fromLTRB(_start + _left, _top, _end + _right, _bottom);
|
||||
case TextDirection.rtl:
|
||||
return new EdgeInsets.fromLTRB(_end + _left, _top, _start + _left, _bottom);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -21,8 +21,12 @@ abstract class FractionalOffsetGeometry {
|
|||
/// const constructors so that they can be used in const expressions.
|
||||
const FractionalOffsetGeometry();
|
||||
|
||||
double get _dx;
|
||||
double get _start;
|
||||
/// The [FractionalOffset.dx] to which this object will [resolve] in [TextDirection.ltr].
|
||||
double get _dxForRTL;
|
||||
|
||||
/// The [FractionalOffset.dx] to which this object will [resolve] in [TextDirection.ltr].
|
||||
double get _dxForLTR;
|
||||
|
||||
double get _dy;
|
||||
|
||||
/// Returns the difference between two [FractionalOffsetGeometry] objects.
|
||||
|
@ -42,9 +46,9 @@ abstract class FractionalOffsetGeometry {
|
|||
/// negating the argument (using the prefix unary `-` operator or multiplying
|
||||
/// the argument by -1.0 using the `*` operator).
|
||||
FractionalOffsetGeometry subtract(FractionalOffsetGeometry other) {
|
||||
return new _MixedFractionalOffset(
|
||||
_dx - other._dx,
|
||||
_start - other._start,
|
||||
return new _SchrodingersFractionalOffset(
|
||||
_dxForRTL - other._dxForRTL,
|
||||
_dxForLTR - other._dxForLTR,
|
||||
_dy - other._dy,
|
||||
);
|
||||
}
|
||||
|
@ -61,9 +65,9 @@ abstract class FractionalOffsetGeometry {
|
|||
/// representing a combination of both is returned. That object can be turned
|
||||
/// into a concrete [FractionalOffset] using [resolve].
|
||||
FractionalOffsetGeometry add(FractionalOffsetGeometry other) {
|
||||
return new _MixedFractionalOffset(
|
||||
_dx + other._dx,
|
||||
_start + other._start,
|
||||
return new _SchrodingersFractionalOffset(
|
||||
_dxForRTL + other._dxForRTL,
|
||||
_dxForLTR + other._dxForLTR,
|
||||
_dy + other._dy,
|
||||
);
|
||||
}
|
||||
|
@ -113,22 +117,22 @@ abstract class FractionalOffsetGeometry {
|
|||
if ((a == null || a is FractionalOffsetDirectional) && (b == null || b is FractionalOffsetDirectional))
|
||||
return FractionalOffsetDirectional.lerp(a, b, t);
|
||||
if (a == null) {
|
||||
return new _MixedFractionalOffset(
|
||||
ui.lerpDouble(0.5, b._dx, t),
|
||||
ui.lerpDouble(0.0, b._start, t),
|
||||
return new _SchrodingersFractionalOffset(
|
||||
ui.lerpDouble(0.5, b._dxForRTL, t),
|
||||
ui.lerpDouble(0.5, b._dxForLTR, t),
|
||||
ui.lerpDouble(0.5, b._dy, t),
|
||||
);
|
||||
}
|
||||
if (b == null) {
|
||||
return new _MixedFractionalOffset(
|
||||
ui.lerpDouble(a._dx, 0.5, t),
|
||||
ui.lerpDouble(a._start, 0.0, t),
|
||||
return new _SchrodingersFractionalOffset(
|
||||
ui.lerpDouble(a._dxForRTL, 0.5, t),
|
||||
ui.lerpDouble(a._dxForLTR, 0.5, t),
|
||||
ui.lerpDouble(a._dy, 0.5, t),
|
||||
);
|
||||
}
|
||||
return new _MixedFractionalOffset(
|
||||
ui.lerpDouble(a._dx, b._dx, t),
|
||||
ui.lerpDouble(a._start, b._start, t),
|
||||
return new _SchrodingersFractionalOffset(
|
||||
ui.lerpDouble(a._dxForRTL, b._dxForRTL, t),
|
||||
ui.lerpDouble(a._dxForLTR, b._dxForLTR, t),
|
||||
ui.lerpDouble(a._dy, b._dy, t),
|
||||
);
|
||||
}
|
||||
|
@ -142,62 +146,15 @@ abstract class FractionalOffsetGeometry {
|
|||
/// * [FractionalOffset], for which this is a no-op (returns itself).
|
||||
/// * [FractionalOffsetDirectional], which flips the horizontal direction
|
||||
/// based on the `direction` argument.
|
||||
FractionalOffset resolve(TextDirection direction);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
double x = _dx;
|
||||
double start = _start;
|
||||
if (this is FractionalOffset) {
|
||||
assert(start == 0.0);
|
||||
start = null;
|
||||
} else if (start == 0.5) {
|
||||
x += 0.5;
|
||||
start = null;
|
||||
FractionalOffset resolve(TextDirection direction) {
|
||||
assert(direction != null);
|
||||
switch (direction) {
|
||||
case TextDirection.rtl:
|
||||
return new FractionalOffset(_dxForRTL, _dy);
|
||||
case TextDirection.ltr:
|
||||
return new FractionalOffset(_dxForLTR, _dy);
|
||||
}
|
||||
if (start == null) {
|
||||
if (x == 0.0 && _dy == 0.0)
|
||||
return 'FractionalOffset.topLeft';
|
||||
if (x == 0.5 && _dy == 0.0)
|
||||
return 'FractionalOffset.topCenter';
|
||||
if (x == 1.0 && _dy == 0.0)
|
||||
return 'FractionalOffset.topRight';
|
||||
if (x == 0.0 && _dy == 0.5)
|
||||
return 'FractionalOffset.centerLeft';
|
||||
if (x == 0.5 && _dy == 0.5)
|
||||
return 'FractionalOffset.center';
|
||||
if (x == 1.0 && _dy == 0.5)
|
||||
return 'FractionalOffset.centerRight';
|
||||
if (x == 0.0 && _dy == 1.0)
|
||||
return 'FractionalOffset.bottomLeft';
|
||||
if (x == 0.5 && _dy == 1.0)
|
||||
return 'FractionalOffset.bottomCenter';
|
||||
if (x == 1.0 && _dy == 1.0)
|
||||
return 'FractionalOffset.bottomRight';
|
||||
return 'FractionalOffset(${x.toStringAsFixed(1)}, '
|
||||
'${_dy.toStringAsFixed(1)})';
|
||||
} else if (x == 0.0) {
|
||||
assert(start != 0.5);
|
||||
if (start == 0.0 && _dy == 0.0)
|
||||
return 'FractionalOffsetDirectional.topStart';
|
||||
if (start == 1.0 && _dy == 0.0)
|
||||
return 'FractionalOffsetDirectional.topEnd';
|
||||
if (start == 0.0 && _dy == 0.5)
|
||||
return 'FractionalOffsetDirectional.centerStart';
|
||||
if (start == 1.0 && _dy == 0.5)
|
||||
return 'FractionalOffsetDirectional.centerEnd';
|
||||
if (start == 0.0 && _dy == 1.0)
|
||||
return 'FractionalOffsetDirectional.bottomStart';
|
||||
if (start == 1.0 && _dy == 1.0)
|
||||
return 'FractionalOffsetDirectional.bottomEnd';
|
||||
return 'FractionalOffsetDirectional(${start.toStringAsFixed(1)}, '
|
||||
'${_dy.toStringAsFixed(1)})';
|
||||
}
|
||||
return 'FractionalOffset(${_dx.toStringAsFixed(1)}, '
|
||||
'${_dy.toStringAsFixed(1)})'
|
||||
' + '
|
||||
'FractionalOffsetDirectional(${_start.toStringAsFixed(1)}, '
|
||||
'0.0)';
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -205,13 +162,13 @@ abstract class FractionalOffsetGeometry {
|
|||
if (other is! FractionalOffsetGeometry)
|
||||
return false;
|
||||
final FractionalOffsetGeometry typedOther = other;
|
||||
return _dx == typedOther._dx &&
|
||||
_start == typedOther._start &&
|
||||
return _dxForRTL == typedOther._dxForRTL &&
|
||||
_dxForLTR == typedOther._dxForLTR &&
|
||||
_dy == typedOther._dy;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => hashValues(_dx, _start, _dy);
|
||||
int get hashCode => hashValues(_dxForRTL, _dxForLTR, _dy);
|
||||
}
|
||||
|
||||
/// An offset that's expressed as a fraction of a [Size].
|
||||
|
@ -279,7 +236,10 @@ class FractionalOffset extends FractionalOffsetGeometry {
|
|||
final double dx;
|
||||
|
||||
@override
|
||||
double get _dx => dx;
|
||||
double get _dxForRTL => dx;
|
||||
|
||||
@override
|
||||
double get _dxForLTR => dx;
|
||||
|
||||
/// The distance fraction in the vertical direction.
|
||||
///
|
||||
|
@ -292,9 +252,6 @@ class FractionalOffset extends FractionalOffsetGeometry {
|
|||
@override
|
||||
double get _dy => dy;
|
||||
|
||||
@override
|
||||
double get _start => 0.0;
|
||||
|
||||
/// The top left corner.
|
||||
static const FractionalOffset topLeft = const FractionalOffset(0.0, 0.0);
|
||||
|
||||
|
@ -420,6 +377,32 @@ class FractionalOffset extends FractionalOffsetGeometry {
|
|||
|
||||
@override
|
||||
FractionalOffset resolve(TextDirection direction) => this;
|
||||
|
||||
static String _stringify(double dx, double dy) {
|
||||
if (dx == 0.0 && dy == 0.0)
|
||||
return 'FractionalOffset.topLeft';
|
||||
if (dx == 0.5 && dy == 0.0)
|
||||
return 'FractionalOffset.topCenter';
|
||||
if (dx == 1.0 && dy == 0.0)
|
||||
return 'FractionalOffset.topRight';
|
||||
if (dx == 0.0 && dy == 0.5)
|
||||
return 'FractionalOffset.centerLeft';
|
||||
if (dx == 0.5 && dy == 0.5)
|
||||
return 'FractionalOffset.center';
|
||||
if (dx == 1.0 && dy == 0.5)
|
||||
return 'FractionalOffset.centerRight';
|
||||
if (dx == 0.0 && dy == 1.0)
|
||||
return 'FractionalOffset.bottomLeft';
|
||||
if (dx == 0.5 && dy == 1.0)
|
||||
return 'FractionalOffset.bottomCenter';
|
||||
if (dx == 1.0 && dy == 1.0)
|
||||
return 'FractionalOffset.bottomRight';
|
||||
return 'FractionalOffset(${dx.toStringAsFixed(1)}, '
|
||||
'${dy.toStringAsFixed(1)})';
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => _stringify(dx, dy);
|
||||
}
|
||||
|
||||
/// An offset that's expressed as a fraction of a [Size], but whose horizontal
|
||||
|
@ -455,7 +438,10 @@ class FractionalOffsetDirectional extends FractionalOffsetGeometry {
|
|||
final double start;
|
||||
|
||||
@override
|
||||
double get _start => start;
|
||||
double get _dxForRTL => 1.0 - start;
|
||||
|
||||
@override
|
||||
double get _dxForLTR => start;
|
||||
|
||||
/// The distance fraction in the vertical direction.
|
||||
///
|
||||
|
@ -471,9 +457,6 @@ class FractionalOffsetDirectional extends FractionalOffsetGeometry {
|
|||
@override
|
||||
double get _dy => dy;
|
||||
|
||||
@override
|
||||
double get _dx => 0.0;
|
||||
|
||||
/// The top corner on the "start" side.
|
||||
static const FractionalOffsetDirectional topStart = const FractionalOffsetDirectional(0.0, 0.0);
|
||||
|
||||
|
@ -578,84 +561,89 @@ class FractionalOffsetDirectional extends FractionalOffsetGeometry {
|
|||
}
|
||||
|
||||
@override
|
||||
FractionalOffset resolve(TextDirection direction) {
|
||||
assert(direction != null);
|
||||
switch (direction) {
|
||||
case TextDirection.ltr:
|
||||
return new FractionalOffset(start, dy);
|
||||
case TextDirection.rtl:
|
||||
return new FractionalOffset(1.0 - start, dy);
|
||||
}
|
||||
return null;
|
||||
String toString() {
|
||||
assert(start != 0.5);
|
||||
if (start == 0.0 && dy == 0.0)
|
||||
return 'FractionalOffsetDirectional.topStart';
|
||||
if (start == 1.0 && dy == 0.0)
|
||||
return 'FractionalOffsetDirectional.topEnd';
|
||||
if (start == 0.0 && dy == 0.5)
|
||||
return 'FractionalOffsetDirectional.centerStart';
|
||||
if (start == 1.0 && dy == 0.5)
|
||||
return 'FractionalOffsetDirectional.centerEnd';
|
||||
if (start == 0.0 && dy == 1.0)
|
||||
return 'FractionalOffsetDirectional.bottomStart';
|
||||
if (start == 1.0 && dy == 1.0)
|
||||
return 'FractionalOffsetDirectional.bottomEnd';
|
||||
return 'FractionalOffsetDirectional(${start.toStringAsFixed(1)}, '
|
||||
'${dy.toStringAsFixed(1)})';
|
||||
}
|
||||
}
|
||||
|
||||
class _MixedFractionalOffset extends FractionalOffsetGeometry {
|
||||
const _MixedFractionalOffset(this._dx, this._start, this._dy);
|
||||
class _SchrodingersFractionalOffset extends FractionalOffsetGeometry {
|
||||
const _SchrodingersFractionalOffset(this._dxForRTL, this._dxForLTR, this._dy);
|
||||
|
||||
@override
|
||||
final double _dx;
|
||||
final double _dxForRTL;
|
||||
|
||||
@override
|
||||
final double _start;
|
||||
final double _dxForLTR;
|
||||
|
||||
@override
|
||||
final double _dy;
|
||||
|
||||
@override
|
||||
_MixedFractionalOffset operator -() {
|
||||
return new _MixedFractionalOffset(
|
||||
-_dx,
|
||||
-_start,
|
||||
_SchrodingersFractionalOffset operator -() {
|
||||
return new _SchrodingersFractionalOffset(
|
||||
-_dxForRTL,
|
||||
-_dxForLTR,
|
||||
-_dy,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
_MixedFractionalOffset operator *(double other) {
|
||||
return new _MixedFractionalOffset(
|
||||
_dx * other,
|
||||
_start * other,
|
||||
_SchrodingersFractionalOffset operator *(double other) {
|
||||
return new _SchrodingersFractionalOffset(
|
||||
_dxForRTL * other,
|
||||
_dxForLTR * other,
|
||||
_dy * other,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
_MixedFractionalOffset operator /(double other) {
|
||||
return new _MixedFractionalOffset(
|
||||
_dx / other,
|
||||
_start / other,
|
||||
_SchrodingersFractionalOffset operator /(double other) {
|
||||
return new _SchrodingersFractionalOffset(
|
||||
_dxForRTL / other,
|
||||
_dxForLTR / other,
|
||||
_dy / other,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
_MixedFractionalOffset operator ~/(double other) {
|
||||
return new _MixedFractionalOffset(
|
||||
(_dx ~/ other).toDouble(),
|
||||
(_start ~/ other).toDouble(),
|
||||
_SchrodingersFractionalOffset operator ~/(double other) {
|
||||
return new _SchrodingersFractionalOffset(
|
||||
(_dxForRTL ~/ other).toDouble(),
|
||||
(_dxForLTR ~/ other).toDouble(),
|
||||
(_dy ~/ other).toDouble(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
_MixedFractionalOffset operator %(double other) {
|
||||
return new _MixedFractionalOffset(
|
||||
_dx % other,
|
||||
_start % other,
|
||||
_SchrodingersFractionalOffset operator %(double other) {
|
||||
return new _SchrodingersFractionalOffset(
|
||||
_dxForRTL % other,
|
||||
_dxForLTR % other,
|
||||
_dy % other,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
FractionalOffset resolve(TextDirection direction) {
|
||||
assert(direction != null);
|
||||
switch (direction) {
|
||||
case TextDirection.ltr:
|
||||
return new FractionalOffset(_start + _dx, _dy);
|
||||
case TextDirection.rtl:
|
||||
return new FractionalOffset((1.0 - _start) + _dx, _dy);
|
||||
}
|
||||
return null;
|
||||
String toString() {
|
||||
if (_dxForRTL == _dxForLTR)
|
||||
return FractionalOffset._stringify(_dxForRTL, _dy);
|
||||
|
||||
return '${FractionalOffset._stringify(_dxForRTL, _dy)} in RTL'
|
||||
' or '
|
||||
'${FractionalOffset._stringify(_dxForLTR, _dy)} in LTR';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,11 +11,16 @@ import 'package:flutter/painting.dart';
|
|||
/// appropriate for rectangles.
|
||||
///
|
||||
/// See [Tween] for a discussion on how to use interpolation objects.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [FractionalOffsetGeometryTween], which interpolates between two
|
||||
/// [FractionalOffsetGeometry] objects.
|
||||
class FractionalOffsetTween extends Tween<FractionalOffset> {
|
||||
/// Creates a fractional offset tween.
|
||||
///
|
||||
/// The [begin] and [end] properties may be null; the null value
|
||||
/// is treated as meaning the top left corner.
|
||||
/// is treated as meaning the center.
|
||||
FractionalOffsetTween({ FractionalOffset begin, FractionalOffset end })
|
||||
: super(begin: begin, end: end);
|
||||
|
||||
|
@ -23,3 +28,29 @@ class FractionalOffsetTween extends Tween<FractionalOffset> {
|
|||
@override
|
||||
FractionalOffset lerp(double t) => FractionalOffset.lerp(begin, end, t);
|
||||
}
|
||||
|
||||
/// An interpolation between two [FractionalOffsetGeometry].
|
||||
///
|
||||
/// This class specializes the interpolation of [Tween<FractionalOffsetGeometry>]
|
||||
/// to be appropriate for rectangles.
|
||||
///
|
||||
/// See [Tween] for a discussion on how to use interpolation objects.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [FractionalOffsetTween], which interpolates between two
|
||||
/// [FractionalOffset] objects.
|
||||
class FractionalOffsetGeometryTween extends Tween<FractionalOffsetGeometry> {
|
||||
/// Creates a fractional offset geometry tween.
|
||||
///
|
||||
/// The [begin] and [end] properties may be null; the null value
|
||||
/// is treated as meaning the center.
|
||||
FractionalOffsetGeometryTween({
|
||||
FractionalOffsetGeometry begin,
|
||||
FractionalOffsetGeometry end,
|
||||
}) : super(begin: begin, end: end);
|
||||
|
||||
/// Returns the value this variable has at the given animation clock value.
|
||||
@override
|
||||
FractionalOffsetGeometry lerp(double t) => FractionalOffsetGeometry.lerp(begin, end, t);
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ export 'package:flutter/rendering.dart' show
|
|||
FlowDelegate,
|
||||
FlowPaintingContext,
|
||||
FractionalOffsetTween,
|
||||
FractionalOffsetGeometryTween,
|
||||
HitTestBehavior,
|
||||
LayerLink,
|
||||
MainAxisAlignment,
|
||||
|
|
|
@ -61,6 +61,11 @@ class DecorationTween extends Tween<Decoration> {
|
|||
/// [EdgeInsets.lerp].
|
||||
///
|
||||
/// See [Tween] for a discussion on how to use interpolation objects.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [EdgeInsetsGeometryTween], which interpolates between two
|
||||
/// [EdgeInsetsGeometry] objects.
|
||||
class EdgeInsetsTween extends Tween<EdgeInsets> {
|
||||
/// Creates an [EdgeInsets] tween.
|
||||
///
|
||||
|
@ -73,6 +78,28 @@ class EdgeInsetsTween extends Tween<EdgeInsets> {
|
|||
EdgeInsets lerp(double t) => EdgeInsets.lerp(begin, end, t);
|
||||
}
|
||||
|
||||
/// An interpolation between two [EdgeInsetsGeometry]s.
|
||||
///
|
||||
/// This class specializes the interpolation of [Tween<EdgeInsetsGeometry>] to
|
||||
/// use [EdgeInsetsGeometry.lerp].
|
||||
///
|
||||
/// See [Tween] for a discussion on how to use interpolation objects.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [EdgeInsetsTween], which interpolates between two [EdgeInsets] objects.
|
||||
class EdgeInsetsGeometryTween extends Tween<EdgeInsetsGeometry> {
|
||||
/// Creates an [EdgeInsetsGeometry] tween.
|
||||
///
|
||||
/// The [begin] and [end] properties may be null; the null value
|
||||
/// is treated as an [EdgeInsetsGeometry] with no inset.
|
||||
EdgeInsetsGeometryTween({ EdgeInsetsGeometry begin, EdgeInsetsGeometry end }) : super(begin: begin, end: end);
|
||||
|
||||
/// Returns the value this variable has at the given animation clock value.
|
||||
@override
|
||||
EdgeInsetsGeometry lerp(double t) => EdgeInsetsGeometry.lerp(begin, end, t);
|
||||
}
|
||||
|
||||
/// An interpolation between two [BorderRadius]s.
|
||||
///
|
||||
/// This class specializes the interpolation of [Tween<BorderRadius>] to use
|
||||
|
@ -358,11 +385,11 @@ class AnimatedContainer extends ImplicitlyAnimatedWidget {
|
|||
/// constraints are unbounded, then the child will be shrink-wrapped instead.
|
||||
///
|
||||
/// Ignored if [child] is null.
|
||||
final FractionalOffset alignment;
|
||||
final FractionalOffsetGeometry alignment;
|
||||
|
||||
/// Empty space to inscribe inside the [decoration]. The [child], if any, is
|
||||
/// placed inside this padding.
|
||||
final EdgeInsets padding;
|
||||
final EdgeInsetsGeometry padding;
|
||||
|
||||
/// The decoration to paint behind the [child].
|
||||
///
|
||||
|
@ -383,7 +410,7 @@ class AnimatedContainer extends ImplicitlyAnimatedWidget {
|
|||
final BoxConstraints constraints;
|
||||
|
||||
/// Empty space to surround the [decoration] and [child].
|
||||
final EdgeInsets margin;
|
||||
final EdgeInsetsGeometry margin;
|
||||
|
||||
/// The transformation matrix to apply before painting the container.
|
||||
final Matrix4 transform;
|
||||
|
@ -394,33 +421,33 @@ class AnimatedContainer extends ImplicitlyAnimatedWidget {
|
|||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder description) {
|
||||
super.debugFillProperties(description);
|
||||
description.add(new DiagnosticsProperty<FractionalOffset>('alignment', alignment, showName: false, defaultValue: null));
|
||||
description.add(new DiagnosticsProperty<EdgeInsets>('padding', padding, defaultValue: null));
|
||||
description.add(new DiagnosticsProperty<FractionalOffsetGeometry>('alignment', alignment, showName: false, defaultValue: null));
|
||||
description.add(new DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding, defaultValue: null));
|
||||
description.add(new DiagnosticsProperty<Decoration>('bg', decoration, defaultValue: null));
|
||||
description.add(new DiagnosticsProperty<Decoration>('fg', foregroundDecoration, defaultValue: null));
|
||||
description.add(new DiagnosticsProperty<BoxConstraints>('constraints', constraints, defaultValue: null, showName: false));
|
||||
description.add(new DiagnosticsProperty<EdgeInsets>('margin', margin, defaultValue: null));
|
||||
description.add(new DiagnosticsProperty<EdgeInsetsGeometry>('margin', margin, defaultValue: null));
|
||||
description.add(new ObjectFlagProperty<Matrix4>.has('transform', transform));
|
||||
}
|
||||
}
|
||||
|
||||
class _AnimatedContainerState extends AnimatedWidgetBaseState<AnimatedContainer> {
|
||||
FractionalOffsetTween _alignment;
|
||||
EdgeInsetsTween _padding;
|
||||
FractionalOffsetGeometryTween _alignment;
|
||||
EdgeInsetsGeometryTween _padding;
|
||||
DecorationTween _decoration;
|
||||
DecorationTween _foregroundDecoration;
|
||||
BoxConstraintsTween _constraints;
|
||||
EdgeInsetsTween _margin;
|
||||
EdgeInsetsGeometryTween _margin;
|
||||
Matrix4Tween _transform;
|
||||
|
||||
@override
|
||||
void forEachTween(TweenVisitor<dynamic> visitor) {
|
||||
_alignment = visitor(_alignment, widget.alignment, (dynamic value) => new FractionalOffsetTween(begin: value));
|
||||
_padding = visitor(_padding, widget.padding, (dynamic value) => new EdgeInsetsTween(begin: value));
|
||||
_alignment = visitor(_alignment, widget.alignment, (dynamic value) => new FractionalOffsetGeometryTween(begin: value));
|
||||
_padding = visitor(_padding, widget.padding, (dynamic value) => new EdgeInsetsGeometryTween(begin: value));
|
||||
_decoration = visitor(_decoration, widget.decoration, (dynamic value) => new DecorationTween(begin: value));
|
||||
_foregroundDecoration = visitor(_foregroundDecoration, widget.foregroundDecoration, (dynamic value) => new DecorationTween(begin: value));
|
||||
_constraints = visitor(_constraints, widget.constraints, (dynamic value) => new BoxConstraintsTween(begin: value));
|
||||
_margin = visitor(_margin, widget.margin, (dynamic value) => new EdgeInsetsTween(begin: value));
|
||||
_margin = visitor(_margin, widget.margin, (dynamic value) => new EdgeInsetsGeometryTween(begin: value));
|
||||
_transform = visitor(_transform, widget.transform, (dynamic value) => new Matrix4Tween(begin: value));
|
||||
}
|
||||
|
||||
|
@ -441,12 +468,12 @@ class _AnimatedContainerState extends AnimatedWidgetBaseState<AnimatedContainer>
|
|||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder description) {
|
||||
super.debugFillProperties(description);
|
||||
description.add(new DiagnosticsProperty<FractionalOffsetTween>('alignment', _alignment, showName: false, defaultValue: null));
|
||||
description.add(new DiagnosticsProperty<EdgeInsetsTween>('padding', _padding, defaultValue: null));
|
||||
description.add(new DiagnosticsProperty<FractionalOffsetGeometryTween>('alignment', _alignment, showName: false, defaultValue: null));
|
||||
description.add(new DiagnosticsProperty<EdgeInsetsGeometryTween>('padding', _padding, defaultValue: null));
|
||||
description.add(new DiagnosticsProperty<DecorationTween>('bg', _decoration, defaultValue: null));
|
||||
description.add(new DiagnosticsProperty<DecorationTween>('fg', _foregroundDecoration, defaultValue: null));
|
||||
description.add(new DiagnosticsProperty<BoxConstraintsTween>('constraints', _constraints, showName: false, defaultValue: null));
|
||||
description.add(new DiagnosticsProperty<EdgeInsetsTween>('margin', _margin, defaultValue: null));
|
||||
description.add(new DiagnosticsProperty<EdgeInsetsGeometryTween>('margin', _margin, defaultValue: null));
|
||||
description.add(new ObjectFlagProperty<Matrix4Tween>.has('transform', _transform));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,15 +73,15 @@ void main() {
|
|||
|
||||
expect(FractionalOffsetGeometry.lerp(directional1, directional2, 0.5), const FractionalOffsetDirectional(0.125 + (2.0 - 0.125) / 2.0, 0.625 + (3.0 - 0.625) / 2.0));
|
||||
expect(FractionalOffsetGeometry.lerp(directional2, directional2, 0.5), directional2);
|
||||
expect(FractionalOffsetGeometry.lerp(directional1, normal2, 0.5).resolve(TextDirection.ltr), const FractionalOffset(1.0 + 1.0 / 16.0, 0.625 + (3.0 - 0.625) / 2.0));
|
||||
expect(FractionalOffsetGeometry.lerp(directional1, normal2, 0.5).resolve(TextDirection.rtl), const FractionalOffset(1.0 + 15.0 / 16.0, 0.625 + (3.0 - 0.625) / 2.0));
|
||||
expect(FractionalOffsetGeometry.lerp(directional1, normal2, 0.5).resolve(TextDirection.ltr), const FractionalOffset(1.0 + 1.0 / 16.0, 0.625 + (3.0 - 0.625) / 2.0));
|
||||
expect(FractionalOffsetGeometry.lerp(directional1, normal2, 0.5).resolve(TextDirection.rtl), new FractionalOffset(lerpDouble(0.875, 2.0, 0.5), 0.625 + (3.0 - 0.625) / 2.0));
|
||||
expect(FractionalOffsetGeometry.lerp(directional1, mixed1, 0.5).resolve(TextDirection.ltr), new FractionalOffset(1.0 / 32.0 + 2.5 / 16.0, lerpDouble(0.625, 0.5625 + 0.6875, 0.5)));
|
||||
expect(FractionalOffsetGeometry.lerp(directional1, mixed1, 0.5).resolve(TextDirection.rtl), new FractionalOffset(1.0 / 32.0 + 1.0 - 2.5 / 16.0, lerpDouble(0.625, 0.5625 + 0.6875, 0.5)));
|
||||
expect(FractionalOffsetGeometry.lerp(mixed1, mixed2, 0.5).resolve(TextDirection.ltr), new FractionalOffset(3.0 + 5.0 / 8.0, lerpDouble(0.5625 + 0.6875, 6.0, 0.5)));
|
||||
expect(FractionalOffsetGeometry.lerp(mixed1, mixed2, 0.5).resolve(TextDirection.rtl), new FractionalOffset(2.0 - 41.0 / 16.0, lerpDouble(0.5625 + 0.6875, 6.0, 0.5)));
|
||||
expect(FractionalOffsetGeometry.lerp(normal1, normal2, 0.5), const FractionalOffset(0.25 + (2.0 - 0.25) / 2.0, 0.875 + (3.0 - 0.875) / 2.0));
|
||||
expect(FractionalOffsetGeometry.lerp(normal1, mixed1, 0.5).resolve(TextDirection.ltr), new FractionalOffset(lerpDouble(0.25, 0.0625, 0.5) + lerpDouble(0.0, 0.1875, 0.5), lerpDouble(0.875, 0.5625 + 0.6875, 0.5)));
|
||||
expect(FractionalOffsetGeometry.lerp(normal1, mixed1, 0.5).resolve(TextDirection.rtl), new FractionalOffset(lerpDouble(0.25, 0.0625, 0.5) + 1.0 - lerpDouble(0.0, 0.1875, 0.5), lerpDouble(0.875, 0.5625 + 0.6875, 0.5)));
|
||||
expect(FractionalOffsetGeometry.lerp(normal1, mixed1, 0.5).resolve(TextDirection.rtl), new FractionalOffset(lerpDouble(0.25, 0.0625 + 0.8125, 0.5), lerpDouble(0.875, 0.5625 + 0.6875, 0.5)));
|
||||
expect(FractionalOffsetGeometry.lerp(null, mixed1, 0.5).resolve(TextDirection.ltr), FractionalOffsetGeometry.lerp(FractionalOffset.center, mixed1, 0.5).resolve(TextDirection.ltr));
|
||||
expect(FractionalOffsetGeometry.lerp(mixed2, null, 0.25).resolve(TextDirection.ltr), FractionalOffsetGeometry.lerp(FractionalOffset.center, mixed2, 0.75).resolve(TextDirection.ltr));
|
||||
expect(FractionalOffsetGeometry.lerp(directional1, null, 1.0), FractionalOffsetDirectional.center);
|
||||
|
@ -98,6 +98,35 @@ void main() {
|
|||
expect(FractionalOffsetGeometry.lerp(mixed1, mixed2, 0.25), mixed3);
|
||||
});
|
||||
|
||||
test('lerp commutes with resolve', () {
|
||||
final List<FractionalOffsetGeometry> offsets = <FractionalOffsetGeometry>[
|
||||
const FractionalOffset(-1.0, 0.65),
|
||||
const FractionalOffsetDirectional(-1.0, 0.45),
|
||||
const FractionalOffsetDirectional(0.125, 0.625),
|
||||
const FractionalOffset(0.25, 0.875),
|
||||
const FractionalOffset(0.0625, 0.5625).add(const FractionalOffsetDirectional(0.1875, 0.6875)),
|
||||
const FractionalOffsetDirectional(2.0, 3.0),
|
||||
const FractionalOffset(2.0, 3.0),
|
||||
const FractionalOffset(2.0, 3.0).add(const FractionalOffsetDirectional(5.0, 3.0)),
|
||||
const FractionalOffset(10.0, 20.0).add(const FractionalOffsetDirectional(30.0, 50.0)),
|
||||
const FractionalOffset(70.0, 110.0).add(const FractionalOffsetDirectional(130.0, 170.0)),
|
||||
const FractionalOffset(25.0, 42.5).add(const FractionalOffsetDirectional(55.0, 80.0)),
|
||||
];
|
||||
|
||||
final List<double> times = <double>[ 0.0, 0.25, 0.5, 0.75, 1.0 ];
|
||||
|
||||
for (TextDirection direction in TextDirection.values) {
|
||||
for (FractionalOffsetGeometry a in offsets) {
|
||||
for (FractionalOffsetGeometry b in offsets) {
|
||||
for (double t in times) {
|
||||
expect(FractionalOffsetGeometry.lerp(a, b, t).resolve(direction),
|
||||
FractionalOffset.lerp(a.resolve(direction), b.resolve(direction), t));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('FractionalOffsetGeometry add/subtract', () {
|
||||
final FractionalOffsetGeometry directional = const FractionalOffsetDirectional(1.0, 2.0);
|
||||
final FractionalOffsetGeometry normal = const FractionalOffset(3.0, 5.0);
|
||||
|
@ -136,10 +165,10 @@ void main() {
|
|||
test('FractionalOffsetGeometry toString', () {
|
||||
expect(const FractionalOffset(1.0001, 2.0001).toString(), 'FractionalOffset(1.0, 2.0)');
|
||||
expect(const FractionalOffset(0.0, 0.0).toString(), 'FractionalOffset.topLeft');
|
||||
expect(const FractionalOffset(0.0, 1.0).add(const FractionalOffsetDirectional(1.0, 0.0)).toString(), 'FractionalOffsetDirectional.bottomEnd');
|
||||
expect(const FractionalOffset(0.0, 1.0).add(const FractionalOffsetDirectional(1.0, 0.0)).toString(), 'FractionalOffset.bottomLeft in RTL or FractionalOffset.bottomRight in LTR');
|
||||
expect(const FractionalOffset(0.0001, 0.0001).toString(), 'FractionalOffset(0.0, 0.0)');
|
||||
expect(const FractionalOffset(0.0, 0.0).toString(), 'FractionalOffset.topLeft');
|
||||
expect(const FractionalOffsetDirectional(0.0, 0.0).toString(), 'FractionalOffsetDirectional.topStart');
|
||||
expect(const FractionalOffset(1.0, 1.0).add(const FractionalOffsetDirectional(1.0, 1.0)).toString(), 'FractionalOffset(1.0, 2.0) + FractionalOffsetDirectional(1.0, 0.0)');
|
||||
expect(const FractionalOffset(1.0, 1.0).add(const FractionalOffsetDirectional(1.0, 1.0)).toString(), 'FractionalOffset(1.0, 2.0) in RTL or FractionalOffset(2.0, 2.0) in LTR');
|
||||
});
|
||||
}
|
||||
|
|
|
@ -139,6 +139,90 @@ void main() {
|
|||
expect(tester.binding.transientCallbackCount, 0);
|
||||
});
|
||||
|
||||
testWidgets('AnimatedContainer padding visual-to-directional animation', (WidgetTester tester) async {
|
||||
final Key target = new UniqueKey();
|
||||
|
||||
await tester.pumpWidget(
|
||||
new Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: new AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
padding: const EdgeInsets.only(right: 50.0),
|
||||
child: new SizedBox.expand(key: target),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(tester.getSize(find.byKey(target)), const Size(750.0, 600.0));
|
||||
expect(tester.getTopRight(find.byKey(target)), const Offset(750.0, 0.0));
|
||||
|
||||
await tester.pumpWidget(
|
||||
new Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: new AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
padding: const EdgeInsetsDirectional.only(start: 100.0),
|
||||
child: new SizedBox.expand(key: target),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(tester.getSize(find.byKey(target)), const Size(750.0, 600.0));
|
||||
expect(tester.getTopRight(find.byKey(target)), const Offset(750.0, 0.0));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
expect(tester.getSize(find.byKey(target)), const Size(725.0, 600.0));
|
||||
expect(tester.getTopRight(find.byKey(target)), const Offset(725.0, 0.0));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
|
||||
expect(tester.getSize(find.byKey(target)), const Size(700.0, 600.0));
|
||||
expect(tester.getTopRight(find.byKey(target)), const Offset(700.0, 0.0));
|
||||
});
|
||||
|
||||
testWidgets('AnimatedContainer alignment visual-to-directional animation', (WidgetTester tester) async {
|
||||
final Key target = new UniqueKey();
|
||||
|
||||
await tester.pumpWidget(
|
||||
new Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: new AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
alignment: FractionalOffset.topRight,
|
||||
child: new SizedBox(key: target, width: 100.0, height: 200.0),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(tester.getSize(find.byKey(target)), const Size(100.0, 200.0));
|
||||
expect(tester.getTopRight(find.byKey(target)), const Offset(800.0, 0.0));
|
||||
|
||||
await tester.pumpWidget(
|
||||
new Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: new AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
alignment: FractionalOffsetDirectional.bottomStart,
|
||||
child: new SizedBox(key: target, width: 100.0, height: 200.0),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(tester.getSize(find.byKey(target)), const Size(100.0, 200.0));
|
||||
expect(tester.getTopRight(find.byKey(target)), const Offset(800.0, 0.0));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
expect(tester.getSize(find.byKey(target)), const Size(100.0, 200.0));
|
||||
expect(tester.getTopRight(find.byKey(target)), const Offset(800.0, 200.0));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
|
||||
expect(tester.getSize(find.byKey(target)), const Size(100.0, 200.0));
|
||||
expect(tester.getTopRight(find.byKey(target)), const Offset(800.0, 400.0));
|
||||
});
|
||||
|
||||
testWidgets('Animation rerun', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
new Center(
|
||||
|
|
Loading…
Reference in a new issue