Commit 4a18cbdf authored by Adam Barth's avatar Adam Barth Committed by GitHub

Add RTL support to progress indicators (#11840)

Fixes #11374
parent d4e52ccb
...@@ -73,12 +73,14 @@ class _LinearProgressIndicatorPainter extends CustomPainter { ...@@ -73,12 +73,14 @@ class _LinearProgressIndicatorPainter extends CustomPainter {
this.valueColor, this.valueColor,
this.value, this.value,
this.animationValue, this.animationValue,
}); @required this.textDirection,
}) : assert(textDirection != null);
final Color backgroundColor; final Color backgroundColor;
final Color valueColor; final Color valueColor;
final double value; final double value;
final double animationValue; final double animationValue;
final TextDirection textDirection;
@override @override
void paint(Canvas canvas, Size size) { void paint(Canvas canvas, Size size) {
...@@ -90,13 +92,35 @@ class _LinearProgressIndicatorPainter extends CustomPainter { ...@@ -90,13 +92,35 @@ class _LinearProgressIndicatorPainter extends CustomPainter {
paint.color = valueColor; paint.color = valueColor;
if (value != null) { if (value != null) {
final double width = value.clamp(0.0, 1.0) * size.width; final double width = value.clamp(0.0, 1.0) * size.width;
canvas.drawRect(Offset.zero & new Size(width, size.height), paint);
double left;
switch (textDirection) {
case TextDirection.rtl:
left = size.width - width;
break;
case TextDirection.ltr:
left = 0.0;
break;
}
canvas.drawRect(new Offset(left, 0.0) & new Size(width, size.height), paint);
} else { } else {
final double startX = size.width * (1.5 * animationValue - 0.5); final double startX = size.width * (1.5 * animationValue - 0.5);
final double endX = startX + 0.5 * size.width; final double endX = startX + 0.5 * size.width;
final double x = startX.clamp(0.0, size.width); final double x = startX.clamp(0.0, size.width);
final double width = endX.clamp(0.0, size.width) - x; final double width = endX.clamp(0.0, size.width) - x;
canvas.drawRect(new Offset(x, 0.0) & new Size(width, size.height), paint);
double left;
switch (textDirection) {
case TextDirection.rtl:
left = size.width - width - x;
break;
case TextDirection.ltr:
left = x;
break;
}
canvas.drawRect(new Offset(left, 0.0) & new Size(width, size.height), paint);
} }
} }
...@@ -105,7 +129,8 @@ class _LinearProgressIndicatorPainter extends CustomPainter { ...@@ -105,7 +129,8 @@ class _LinearProgressIndicatorPainter extends CustomPainter {
return oldPainter.backgroundColor != backgroundColor return oldPainter.backgroundColor != backgroundColor
|| oldPainter.valueColor != valueColor || oldPainter.valueColor != valueColor
|| oldPainter.value != value || oldPainter.value != value
|| oldPainter.animationValue != animationValue; || oldPainter.animationValue != animationValue
|| oldPainter.textDirection != textDirection;
} }
} }
...@@ -152,8 +177,20 @@ class _LinearProgressIndicatorState extends State<LinearProgressIndicator> with ...@@ -152,8 +177,20 @@ class _LinearProgressIndicatorState extends State<LinearProgressIndicator> with
_controller = new AnimationController( _controller = new AnimationController(
duration: const Duration(milliseconds: 1500), duration: const Duration(milliseconds: 1500),
vsync: this, vsync: this,
)..repeat(); );
_animation = new CurvedAnimation(parent: _controller, curve: Curves.fastOutSlowIn); _animation = new CurvedAnimation(parent: _controller, curve: Curves.fastOutSlowIn);
if (widget.value == null)
_controller.repeat();
}
@override
void didUpdateWidget(LinearProgressIndicator oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.value == null && !_controller.isAnimating)
_controller.repeat();
else if (widget.value != null && _controller.isAnimating)
_controller.stop();
} }
@override @override
...@@ -162,7 +199,7 @@ class _LinearProgressIndicatorState extends State<LinearProgressIndicator> with ...@@ -162,7 +199,7 @@ class _LinearProgressIndicatorState extends State<LinearProgressIndicator> with
super.dispose(); super.dispose();
} }
Widget _buildIndicator(BuildContext context, double animationValue) { Widget _buildIndicator(BuildContext context, double animationValue, TextDirection textDirection) {
return new Container( return new Container(
constraints: const BoxConstraints.tightFor( constraints: const BoxConstraints.tightFor(
width: double.INFINITY, width: double.INFINITY,
...@@ -174,6 +211,7 @@ class _LinearProgressIndicatorState extends State<LinearProgressIndicator> with ...@@ -174,6 +211,7 @@ class _LinearProgressIndicatorState extends State<LinearProgressIndicator> with
valueColor: widget._getValueColor(context), valueColor: widget._getValueColor(context),
value: widget.value, // may be null value: widget.value, // may be null
animationValue: animationValue, // ignored if widget.value is not null animationValue: animationValue, // ignored if widget.value is not null
textDirection: textDirection,
), ),
), ),
); );
...@@ -181,13 +219,15 @@ class _LinearProgressIndicatorState extends State<LinearProgressIndicator> with ...@@ -181,13 +219,15 @@ class _LinearProgressIndicatorState extends State<LinearProgressIndicator> with
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final TextDirection textDirection = Directionality.of(context);
if (widget.value != null) if (widget.value != null)
return _buildIndicator(context, _animation.value); return _buildIndicator(context, _animation.value, textDirection);
return new AnimatedBuilder( return new AnimatedBuilder(
animation: _animation, animation: _animation,
builder: (BuildContext context, Widget child) { builder: (BuildContext context, Widget child) {
return _buildIndicator(context, _animation.value); return _buildIndicator(context, _animation.value, textDirection);
}, },
); );
} }
......
...@@ -15,26 +15,134 @@ void main() { ...@@ -15,26 +15,134 @@ void main() {
testWidgets('LinearProgressIndicator(value: 0.0) can be constructed', (WidgetTester tester) async { testWidgets('LinearProgressIndicator(value: 0.0) can be constructed', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
const Center( const Directionality(
child: const SizedBox( textDirection: TextDirection.ltr,
width: 200.0, child: const Center(
child: const LinearProgressIndicator(value: 0.0) child: const SizedBox(
) width: 200.0,
) child: const LinearProgressIndicator(value: 0.0),
),
),
),
); );
}); });
testWidgets('LinearProgressIndicator(value: null) can be constructed', (WidgetTester tester) async { testWidgets('LinearProgressIndicator(value: null) can be constructed', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
const Center( const Directionality(
child: const SizedBox( textDirection: TextDirection.rtl,
width: 200.0, child: const Center(
child: const LinearProgressIndicator(value: null) child: const SizedBox(
) width: 200.0,
) child: const LinearProgressIndicator(value: null),
),
),
),
); );
}); });
testWidgets('LinearProgressIndicator paint (LTR)', (WidgetTester tester) async {
await tester.pumpWidget(
const Directionality(
textDirection: TextDirection.ltr,
child: const Center(
child: const SizedBox(
width: 200.0,
child: const LinearProgressIndicator(value: 0.25),
),
),
),
);
expect(
find.byType(LinearProgressIndicator),
paints
..rect(rect: new Rect.fromLTRB(0.0, 0.0, 200.0, 6.0))
..rect(rect: new Rect.fromLTRB(0.0, 0.0, 50.0, 6.0))
);
expect(tester.binding.transientCallbackCount, 0);
});
testWidgets('LinearProgressIndicator paint (RTL)', (WidgetTester tester) async {
await tester.pumpWidget(
const Directionality(
textDirection: TextDirection.rtl,
child: const Center(
child: const SizedBox(
width: 200.0,
child: const LinearProgressIndicator(value: 0.25),
),
),
),
);
expect(
find.byType(LinearProgressIndicator),
paints
..rect(rect: new Rect.fromLTRB(0.0, 0.0, 200.0, 6.0))
..rect(rect: new Rect.fromLTRB(150.0, 0.0, 200.0, 6.0))
);
expect(tester.binding.transientCallbackCount, 0);
});
testWidgets('LinearProgressIndicator indeterminate (LTR)', (WidgetTester tester) async {
await tester.pumpWidget(
const Directionality(
textDirection: TextDirection.ltr,
child: const Center(
child: const SizedBox(
width: 200.0,
child: const LinearProgressIndicator(),
),
),
),
);
await tester.pump(const Duration(milliseconds: 750));
final double animationValue = Curves.fastOutSlowIn.transform(0.5);
final double startX = 200.0 * (1.5 * animationValue - 0.5);
expect(
find.byType(LinearProgressIndicator),
paints
..rect(rect: new Rect.fromLTRB(0.0, 0.0, 200.0, 6.0))
..rect(rect: new Rect.fromLTRB(startX, 0.0, 200.0, 6.0))
);
expect(tester.binding.transientCallbackCount, 1);
});
testWidgets('LinearProgressIndicator paint (RTL)', (WidgetTester tester) async {
await tester.pumpWidget(
const Directionality(
textDirection: TextDirection.rtl,
child: const Center(
child: const SizedBox(
width: 200.0,
child: const LinearProgressIndicator(),
),
),
),
);
await tester.pump(const Duration(milliseconds: 750));
final double animationValue = Curves.fastOutSlowIn.transform(0.5);
final double startX = 200.0 * (1.5 * animationValue - 0.5);
expect(
find.byType(LinearProgressIndicator),
paints
..rect(rect: new Rect.fromLTRB(0.0, 0.0, 200.0, 6.0))
..rect(rect: new Rect.fromLTRB(0.0, 0.0, 200.0 - startX, 6.0))
);
expect(tester.binding.transientCallbackCount, 1);
});
testWidgets('CircularProgressIndicator(value: 0.0) can be constructed', (WidgetTester tester) async { testWidgets('CircularProgressIndicator(value: 0.0) can be constructed', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
const Center( const Center(
...@@ -52,9 +160,15 @@ void main() { ...@@ -52,9 +160,15 @@ void main() {
}); });
testWidgets('LinearProgressIndicator causes a repaint when it changes', (WidgetTester tester) async { testWidgets('LinearProgressIndicator causes a repaint when it changes', (WidgetTester tester) async {
await tester.pumpWidget(new ListView(children: <Widget>[const LinearProgressIndicator(value: 0.0)])); await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new ListView(children: <Widget>[const LinearProgressIndicator(value: 0.0)]),
));
final List<Layer> layers1 = tester.layers; final List<Layer> layers1 = tester.layers;
await tester.pumpWidget(new ListView(children: <Widget>[const LinearProgressIndicator(value: 0.5)])); await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new ListView(children: <Widget>[const LinearProgressIndicator(value: 0.5)])),
);
final List<Layer> layers2 = tester.layers; final List<Layer> layers2 = tester.layers;
expect(layers1, isNot(equals(layers2))); expect(layers1, isNot(equals(layers2)));
}); });
......
...@@ -9,7 +9,7 @@ void main() { ...@@ -9,7 +9,7 @@ void main() {
testWidgets('TickerMode', (WidgetTester tester) async { testWidgets('TickerMode', (WidgetTester tester) async {
final Widget widget = const TickerMode( final Widget widget = const TickerMode(
enabled: false, enabled: false,
child: const LinearProgressIndicator() child: const CircularProgressIndicator()
); );
expect(widget.toString, isNot(throwsException)); expect(widget.toString, isNot(throwsException));
...@@ -19,14 +19,14 @@ void main() { ...@@ -19,14 +19,14 @@ void main() {
await tester.pumpWidget(const TickerMode( await tester.pumpWidget(const TickerMode(
enabled: true, enabled: true,
child: const LinearProgressIndicator() child: const CircularProgressIndicator()
)); ));
expect(tester.binding.transientCallbackCount, 1); expect(tester.binding.transientCallbackCount, 1);
await tester.pumpWidget(const TickerMode( await tester.pumpWidget(const TickerMode(
enabled: false, enabled: false,
child: const LinearProgressIndicator() child: const CircularProgressIndicator()
)); ));
expect(tester.binding.transientCallbackCount, 0); expect(tester.binding.transientCallbackCount, 0);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment