BottomNavigationBar RTL (#13167)
This commit is contained in:
parent
9afc853f75
commit
4c9013b71b
@ -399,7 +399,8 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr
|
|||||||
if (widget.onTap != null)
|
if (widget.onTap != null)
|
||||||
widget.onTap(i);
|
widget.onTap(i);
|
||||||
},
|
},
|
||||||
colorTween: colorTween),
|
colorTween: colorTween,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -415,7 +416,8 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr
|
|||||||
if (widget.onTap != null)
|
if (widget.onTap != null)
|
||||||
widget.onTap(i);
|
widget.onTap(i);
|
||||||
},
|
},
|
||||||
flex: _evaluateFlex(_animations[i])),
|
flex: _evaluateFlex(_animations[i]),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -435,6 +437,7 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
assert(debugCheckHasDirectionality(context));
|
||||||
Color backgroundColor;
|
Color backgroundColor;
|
||||||
switch (widget.type) {
|
switch (widget.type) {
|
||||||
case BottomNavigationBarType.fixed:
|
case BottomNavigationBarType.fixed:
|
||||||
@ -459,6 +462,7 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr
|
|||||||
child: new CustomPaint(
|
child: new CustomPaint(
|
||||||
painter: new _RadialPainter(
|
painter: new _RadialPainter(
|
||||||
circles: _circles.toList(),
|
circles: _circles.toList(),
|
||||||
|
textDirection: Directionality.of(context),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -501,7 +505,7 @@ class _Circle {
|
|||||||
AnimationController controller;
|
AnimationController controller;
|
||||||
CurvedAnimation animation;
|
CurvedAnimation animation;
|
||||||
|
|
||||||
double get horizontalOffset {
|
double get horizontalLeadingOffset {
|
||||||
double weightSum(Iterable<Animation<double>> animations) {
|
double weightSum(Iterable<Animation<double>> animations) {
|
||||||
// We're adding flex values instead of animation values to produce correct
|
// We're adding flex values instead of animation values to produce correct
|
||||||
// ratios.
|
// ratios.
|
||||||
@ -509,11 +513,11 @@ class _Circle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final double allWeights = weightSum(state._animations);
|
final double allWeights = weightSum(state._animations);
|
||||||
// These weights sum to the left edge of the indexed item.
|
// These weights sum to the start edge of the indexed item.
|
||||||
final double leftWeights = weightSum(state._animations.sublist(0, index));
|
final double leadingWeights = weightSum(state._animations.sublist(0, index));
|
||||||
|
|
||||||
// Add half of its flex value in order to get to the center.
|
// Add half of its flex value in order to get to the center.
|
||||||
return (leftWeights + state._evaluateFlex(state._animations[index]) / 2.0) / allWeights;
|
return (leadingWeights + state._evaluateFlex(state._animations[index]) / 2.0) / allWeights;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
@ -524,10 +528,13 @@ class _Circle {
|
|||||||
// Paints the animating color splash circles.
|
// Paints the animating color splash circles.
|
||||||
class _RadialPainter extends CustomPainter {
|
class _RadialPainter extends CustomPainter {
|
||||||
_RadialPainter({
|
_RadialPainter({
|
||||||
this.circles,
|
@required this.circles,
|
||||||
});
|
@required this.textDirection,
|
||||||
|
}) : assert(circles != null),
|
||||||
|
assert(textDirection != null);
|
||||||
|
|
||||||
final List<_Circle> circles;
|
final List<_Circle> circles;
|
||||||
|
final TextDirection textDirection;
|
||||||
|
|
||||||
// Computes the maximum radius attainable such that at least one of the
|
// Computes the maximum radius attainable such that at least one of the
|
||||||
// bounding rectangle's corners touches the edge of the circle. Drawing a
|
// bounding rectangle's corners touches the edge of the circle. Drawing a
|
||||||
@ -541,6 +548,8 @@ class _RadialPainter extends CustomPainter {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool shouldRepaint(_RadialPainter oldPainter) {
|
bool shouldRepaint(_RadialPainter oldPainter) {
|
||||||
|
if (textDirection != oldPainter.textDirection)
|
||||||
|
return true;
|
||||||
if (circles == oldPainter.circles)
|
if (circles == oldPainter.circles)
|
||||||
return false;
|
return false;
|
||||||
if (circles.length != oldPainter.circles.length)
|
if (circles.length != oldPainter.circles.length)
|
||||||
@ -557,10 +566,16 @@ class _RadialPainter extends CustomPainter {
|
|||||||
final Paint paint = new Paint()..color = circle.color;
|
final Paint paint = new Paint()..color = circle.color;
|
||||||
final Rect rect = new Rect.fromLTWH(0.0, 0.0, size.width, size.height);
|
final Rect rect = new Rect.fromLTWH(0.0, 0.0, size.width, size.height);
|
||||||
canvas.clipRect(rect);
|
canvas.clipRect(rect);
|
||||||
final Offset center = new Offset(
|
double leftFraction;
|
||||||
circle.horizontalOffset * size.width,
|
switch (textDirection) {
|
||||||
size.height / 2.0,
|
case TextDirection.rtl:
|
||||||
);
|
leftFraction = 1.0 - circle.horizontalLeadingOffset;
|
||||||
|
break;
|
||||||
|
case TextDirection.ltr:
|
||||||
|
leftFraction = circle.horizontalLeadingOffset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
final Offset center = new Offset(leftFraction * size.width, size.height / 2.0);
|
||||||
final Tween<double> radiusTween = new Tween<double>(
|
final Tween<double> radiusTween = new Tween<double>(
|
||||||
begin: 0.0,
|
begin: 0.0,
|
||||||
end: _maxRadius(center, size),
|
end: _maxRadius(center, size),
|
||||||
|
@ -2,9 +2,12 @@
|
|||||||
// 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/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
import '../rendering/mock_canvas.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets('BottomNavigationBar callback test', (WidgetTester tester) async {
|
testWidgets('BottomNavigationBar callback test', (WidgetTester tester) async {
|
||||||
int mutatedIndex;
|
int mutatedIndex;
|
||||||
@ -392,4 +395,78 @@ void main() {
|
|||||||
final RenderBox itemBoxB = tester.renderObject(find.text(longTextB.data));
|
final RenderBox itemBoxB = tester.renderObject(find.text(longTextB.data));
|
||||||
expect(itemBoxB.size, equals(const Size(400.0, 14.0)));
|
expect(itemBoxB.size, equals(const Size(400.0, 14.0)));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('BottomNavigationBar paints circles', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
boilerplate(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
bottomNavigationBar: new BottomNavigationBar(
|
||||||
|
items: <BottomNavigationBarItem>[
|
||||||
|
const BottomNavigationBarItem(
|
||||||
|
title: const Text('A'),
|
||||||
|
icon: const Icon(Icons.ac_unit),
|
||||||
|
),
|
||||||
|
const BottomNavigationBarItem(
|
||||||
|
title: const Text('B'),
|
||||||
|
icon: const Icon(Icons.battery_alert),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final RenderBox box = tester.renderObject(find.byType(BottomNavigationBar));
|
||||||
|
expect(box, isNot(paints..circle()));
|
||||||
|
|
||||||
|
await tester.tap(find.text('A'));
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump(const Duration(milliseconds: 20));
|
||||||
|
expect(box, paints..circle(x: 200.0));
|
||||||
|
|
||||||
|
await tester.tap(find.text('B'));
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump(const Duration(milliseconds: 20));
|
||||||
|
expect(box, paints..circle(x: 200.0)..circle(x: 600.0));
|
||||||
|
|
||||||
|
// Now we flip the directionality and verify that the circles switch positions.
|
||||||
|
await tester.pumpWidget(
|
||||||
|
boilerplate(
|
||||||
|
textDirection: TextDirection.rtl,
|
||||||
|
bottomNavigationBar: new BottomNavigationBar(
|
||||||
|
items: <BottomNavigationBarItem>[
|
||||||
|
const BottomNavigationBarItem(
|
||||||
|
title: const Text('A'),
|
||||||
|
icon: const Icon(Icons.ac_unit),
|
||||||
|
),
|
||||||
|
const BottomNavigationBarItem(
|
||||||
|
title: const Text('B'),
|
||||||
|
icon: const Icon(Icons.battery_alert),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(box, paints..circle(x: 600.0)..circle(x: 200.0));
|
||||||
|
|
||||||
|
await tester.tap(find.text('A'));
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump(const Duration(milliseconds: 20));
|
||||||
|
expect(box, paints..circle(x: 600.0)..circle(x: 200.0)..circle(x: 600.0));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget boilerplate({ Widget bottomNavigationBar, @required TextDirection textDirection }) {
|
||||||
|
assert(textDirection != null);
|
||||||
|
return new Directionality(
|
||||||
|
textDirection: textDirection,
|
||||||
|
child: new MediaQuery(
|
||||||
|
data: const MediaQueryData(),
|
||||||
|
child: new Material(
|
||||||
|
child: new Scaffold(
|
||||||
|
bottomNavigationBar: bottomNavigationBar,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user