mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
TabPageSelector colors and indicatorSize (#10665)
This commit is contained in:
parent
ede575a92e
commit
7ada46677b
@ -961,12 +961,19 @@ class _TabBarViewState extends State<TabBarView> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Displays a single 12x12 circle with the specified border and background colors.
|
||||
/// Displays a single circle with the specified border and background colors.
|
||||
///
|
||||
/// Used by [TabPageSelector] to indicate the selected page.
|
||||
class TabPageSelectorIndicator extends StatelessWidget {
|
||||
/// Creates an indicator used by [TabPageSelector].
|
||||
const TabPageSelectorIndicator({ Key key, this.backgroundColor, this.borderColor }) : super(key: key);
|
||||
///
|
||||
/// The [backgroundColor], [borderColor], and [size] parameters cannot be null.
|
||||
const TabPageSelectorIndicator({
|
||||
Key key,
|
||||
@required this.backgroundColor,
|
||||
@required this.borderColor,
|
||||
@required this.size,
|
||||
}) : assert(backgroundColor != null), assert(borderColor != null), assert(size != null), super(key: key);
|
||||
|
||||
/// The indicator circle's background color.
|
||||
final Color backgroundColor;
|
||||
@ -974,11 +981,14 @@ class TabPageSelectorIndicator extends StatelessWidget {
|
||||
/// The indicator circle's border color.
|
||||
final Color borderColor;
|
||||
|
||||
/// The indicator circle's diameter.
|
||||
final double size;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new Container(
|
||||
width: 12.0,
|
||||
height: 12.0,
|
||||
width: size,
|
||||
height: size,
|
||||
margin: const EdgeInsets.all(4.0),
|
||||
decoration: new BoxDecoration(
|
||||
color: backgroundColor,
|
||||
@ -996,7 +1006,13 @@ class TabPageSelectorIndicator extends StatelessWidget {
|
||||
/// ancestor.
|
||||
class TabPageSelector extends StatelessWidget {
|
||||
/// Creates a compact widget that indicates which tab has been selected.
|
||||
const TabPageSelector({ Key key, this.controller }) : super(key: key);
|
||||
const TabPageSelector({
|
||||
Key key,
|
||||
this.controller,
|
||||
this.indicatorSize: 12.0,
|
||||
this.color,
|
||||
this.selectedColor,
|
||||
}) : assert(indicatorSize != null && indicatorSize > 0.0), super(key: key);
|
||||
|
||||
/// This widget's selection and animation state.
|
||||
///
|
||||
@ -1004,47 +1020,64 @@ class TabPageSelector extends StatelessWidget {
|
||||
/// will be used.
|
||||
final TabController controller;
|
||||
|
||||
/// The indicator circle's diameter (the default value is 12.0).
|
||||
final double indicatorSize;
|
||||
|
||||
/// The indicator cicle's fill color for unselected pages.
|
||||
///
|
||||
/// If this parameter is null then the indicator is filled with [Colors.transparent].
|
||||
final Color color;
|
||||
|
||||
/// The indicator cicle's fill color for selected pages and border color
|
||||
/// for all indicator circles.
|
||||
///
|
||||
/// If this parameter is null then the indicator is filled with the theme's
|
||||
/// accent color, [ThemeData.accentColor].
|
||||
final Color selectedColor;
|
||||
|
||||
Widget _buildTabIndicator(
|
||||
int tabIndex,
|
||||
TabController tabController,
|
||||
ColorTween selectedColor,
|
||||
ColorTween previousColor,
|
||||
ColorTween selectedColorTween,
|
||||
ColorTween previousColorTween,
|
||||
) {
|
||||
Color background;
|
||||
if (tabController.indexIsChanging) {
|
||||
// The selection's animation is animating from previousValue to value.
|
||||
final double t = 1.0 - _indexChangeProgress(tabController);
|
||||
if (tabController.index == tabIndex)
|
||||
background = selectedColor.lerp(t);
|
||||
background = selectedColorTween.lerp(t);
|
||||
else if (tabController.previousIndex == tabIndex)
|
||||
background = previousColor.lerp(t);
|
||||
background = previousColorTween.lerp(t);
|
||||
else
|
||||
background = selectedColor.begin;
|
||||
background = selectedColorTween.begin;
|
||||
} else {
|
||||
// The selection's offset reflects how far the TabBarView has
|
||||
/// been dragged to the left (-1.0 to 0.0) or the right (0.0 to 1.0).
|
||||
final double offset = tabController.offset;
|
||||
if (tabController.index == tabIndex) {
|
||||
background = selectedColor.lerp(1.0 - offset.abs());
|
||||
background = selectedColorTween.lerp(1.0 - offset.abs());
|
||||
} else if (tabController.index == tabIndex - 1 && offset > 0.0) {
|
||||
background = selectedColor.lerp(offset);
|
||||
background = selectedColorTween.lerp(offset);
|
||||
} else if (tabController.index == tabIndex + 1 && offset < 0.0) {
|
||||
background = selectedColor.lerp(-offset);
|
||||
background = selectedColorTween.lerp(-offset);
|
||||
} else {
|
||||
background = selectedColor.begin;
|
||||
background = selectedColorTween.begin;
|
||||
}
|
||||
}
|
||||
return new TabPageSelectorIndicator(
|
||||
backgroundColor: background,
|
||||
borderColor: selectedColor.end,
|
||||
borderColor: selectedColorTween.end,
|
||||
size: indicatorSize,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Color color = Theme.of(context).accentColor;
|
||||
final ColorTween selectedColor = new ColorTween(begin: Colors.transparent, end: color);
|
||||
final ColorTween previousColor = new ColorTween(begin: color, end: Colors.transparent);
|
||||
final Color fixColor = color ?? Colors.transparent;
|
||||
final Color fixSelectedColor = selectedColor ?? Theme.of(context).accentColor;
|
||||
final ColorTween selectedColorTween = new ColorTween(begin: fixColor, end: fixSelectedColor);
|
||||
final ColorTween previousColorTween = new ColorTween(begin: fixSelectedColor, end: fixColor);
|
||||
final TabController tabController = controller ?? DefaultTabController.of(context);
|
||||
assert(() {
|
||||
if (tabController == null) {
|
||||
@ -1070,7 +1103,7 @@ class TabPageSelector extends StatelessWidget {
|
||||
child: new Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: new List<Widget>.generate(tabController.length, (int tabIndex) {
|
||||
return _buildTabIndicator(tabIndex, tabController, selectedColor, previousColor);
|
||||
return _buildTabIndicator(tabIndex, tabController, selectedColorTween, previousColorTween);
|
||||
}).toList(),
|
||||
),
|
||||
);
|
||||
|
||||
@ -5,12 +5,12 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
const Color selectedColor = const Color(0xFF00FF00);
|
||||
const Color unselectedColor = Colors.transparent;
|
||||
const Color kSelectedColor = const Color(0xFF00FF00);
|
||||
const Color kUnselectedColor = Colors.transparent;
|
||||
|
||||
Widget buildFrame(TabController tabController) {
|
||||
Widget buildFrame(TabController tabController, { Color color, Color selectedColor, double indicatorSize: 12.0 }) {
|
||||
return new Theme(
|
||||
data: new ThemeData(accentColor: selectedColor),
|
||||
data: new ThemeData(accentColor: kSelectedColor),
|
||||
child: new SizedBox.expand(
|
||||
child: new Center(
|
||||
child: new SizedBox(
|
||||
@ -18,7 +18,12 @@ Widget buildFrame(TabController tabController) {
|
||||
height: 400.0,
|
||||
child: new Column(
|
||||
children: <Widget>[
|
||||
new TabPageSelector(controller: tabController),
|
||||
new TabPageSelector(
|
||||
controller: tabController,
|
||||
color: color,
|
||||
selectedColor: selectedColor,
|
||||
indicatorSize: indicatorSize,
|
||||
),
|
||||
new Flexible(
|
||||
child: new TabBarView(
|
||||
controller: tabController,
|
||||
@ -56,17 +61,17 @@ void main() {
|
||||
await tester.pumpWidget(buildFrame(tabController));
|
||||
|
||||
expect(tabController.index, 0);
|
||||
expect(indicatorColors(tester), const <Color>[selectedColor, unselectedColor, unselectedColor]);
|
||||
expect(indicatorColors(tester), const <Color>[kSelectedColor, kUnselectedColor, kUnselectedColor]);
|
||||
|
||||
tabController.index = 1;
|
||||
await tester.pump();
|
||||
expect(tabController.index, 1);
|
||||
expect(indicatorColors(tester), const <Color>[unselectedColor, selectedColor, unselectedColor]);
|
||||
expect(indicatorColors(tester), const <Color>[kUnselectedColor, kSelectedColor, kUnselectedColor]);
|
||||
|
||||
tabController.index = 2;
|
||||
await tester.pump();
|
||||
expect(tabController.index, 2);
|
||||
expect(indicatorColors(tester), const <Color>[unselectedColor, unselectedColor, selectedColor]);
|
||||
expect(indicatorColors(tester), const <Color>[kUnselectedColor, kUnselectedColor, kSelectedColor]);
|
||||
});
|
||||
|
||||
testWidgets('PageSelector responds correctly to TabController.animateTo()', (WidgetTester tester) async {
|
||||
@ -77,7 +82,7 @@ void main() {
|
||||
await tester.pumpWidget(buildFrame(tabController));
|
||||
|
||||
expect(tabController.index, 0);
|
||||
expect(indicatorColors(tester), const <Color>[selectedColor, unselectedColor, unselectedColor]);
|
||||
expect(indicatorColors(tester), const <Color>[kSelectedColor, kUnselectedColor, kUnselectedColor]);
|
||||
|
||||
tabController.animateTo(1, duration: const Duration(milliseconds: 200));
|
||||
await tester.pump();
|
||||
@ -87,14 +92,14 @@ void main() {
|
||||
await tester.pump(const Duration(milliseconds: 10));
|
||||
List<Color> colors = indicatorColors(tester);
|
||||
expect(colors[0].alpha, greaterThan(colors[1].alpha));
|
||||
expect(colors[2], unselectedColor);
|
||||
expect(colors[2], kUnselectedColor);
|
||||
await tester.pump(const Duration(milliseconds: 175));
|
||||
colors = indicatorColors(tester);
|
||||
expect(colors[0].alpha, lessThan(colors[1].alpha));
|
||||
expect(colors[2], unselectedColor);
|
||||
expect(colors[2], kUnselectedColor);
|
||||
await tester.pumpAndSettle();
|
||||
expect(tabController.index, 1);
|
||||
expect(indicatorColors(tester), const <Color>[unselectedColor, selectedColor, unselectedColor]);
|
||||
expect(indicatorColors(tester), const <Color>[kUnselectedColor, kSelectedColor, kUnselectedColor]);
|
||||
|
||||
tabController.animateTo(2, duration: const Duration(milliseconds: 200));
|
||||
await tester.pump();
|
||||
@ -102,14 +107,14 @@ void main() {
|
||||
await tester.pump(const Duration(milliseconds: 10));
|
||||
colors = indicatorColors(tester);
|
||||
expect(colors[1].alpha, greaterThan(colors[2].alpha));
|
||||
expect(colors[0], unselectedColor);
|
||||
expect(colors[0], kUnselectedColor);
|
||||
await tester.pump(const Duration(milliseconds: 175));
|
||||
colors = indicatorColors(tester);
|
||||
expect(colors[1].alpha, lessThan(colors[2].alpha));
|
||||
expect(colors[0], unselectedColor);
|
||||
expect(colors[0], kUnselectedColor);
|
||||
await tester.pumpAndSettle();
|
||||
expect(tabController.index, 2);
|
||||
expect(indicatorColors(tester), const <Color>[unselectedColor, unselectedColor, selectedColor]);
|
||||
expect(indicatorColors(tester), const <Color>[kUnselectedColor, kUnselectedColor, kSelectedColor]);
|
||||
});
|
||||
|
||||
testWidgets('PageSelector responds correctly to TabBarView drags', (WidgetTester tester) async {
|
||||
@ -121,7 +126,7 @@ void main() {
|
||||
await tester.pumpWidget(buildFrame(tabController));
|
||||
|
||||
expect(tabController.index, 1);
|
||||
expect(indicatorColors(tester), const <Color>[unselectedColor, selectedColor, unselectedColor]);
|
||||
expect(indicatorColors(tester), const <Color>[kUnselectedColor, kSelectedColor, kUnselectedColor]);
|
||||
|
||||
final TestGesture gesture = await tester.startGesture(const Offset(200.0, 200.0));
|
||||
|
||||
@ -131,13 +136,13 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
List<Color> colors = indicatorColors(tester);
|
||||
expect(colors[1].alpha, greaterThan(colors[2].alpha));
|
||||
expect(colors[0], unselectedColor);
|
||||
expect(colors[0], kUnselectedColor);
|
||||
|
||||
// Drag back to where we started.
|
||||
await gesture.moveBy(const Offset(100.0, 0.0));
|
||||
await tester.pumpAndSettle();
|
||||
colors = indicatorColors(tester);
|
||||
expect(indicatorColors(tester), const <Color>[unselectedColor, selectedColor, unselectedColor]);
|
||||
expect(indicatorColors(tester), const <Color>[kUnselectedColor, kSelectedColor, kUnselectedColor]);
|
||||
|
||||
// Drag to the left moving the selection towards indicator 0. Indicator 0's
|
||||
// opacity should increase and Indicator 1's opacity should decrease.
|
||||
@ -145,30 +150,69 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
colors = indicatorColors(tester);
|
||||
expect(colors[1].alpha, greaterThan(colors[0].alpha));
|
||||
expect(colors[2], unselectedColor);
|
||||
expect(colors[2], kUnselectedColor);
|
||||
|
||||
// Drag back to where we started.
|
||||
await gesture.moveBy(const Offset(-100.0, 0.0));
|
||||
await tester.pumpAndSettle();
|
||||
colors = indicatorColors(tester);
|
||||
expect(indicatorColors(tester), const <Color>[unselectedColor, selectedColor, unselectedColor]);
|
||||
expect(indicatorColors(tester), const <Color>[kUnselectedColor, kSelectedColor, kUnselectedColor]);
|
||||
|
||||
// Completing the gesture doesn't change anything
|
||||
await gesture.up();
|
||||
await tester.pumpAndSettle();
|
||||
colors = indicatorColors(tester);
|
||||
expect(indicatorColors(tester), const <Color>[unselectedColor, selectedColor, unselectedColor]);
|
||||
expect(indicatorColors(tester), const <Color>[kUnselectedColor, kSelectedColor, kUnselectedColor]);
|
||||
|
||||
// Fling to the left, selects indicator 2
|
||||
await tester.fling(find.byType(TabBarView), const Offset(-100.0, 0.0), 1000.0);
|
||||
await tester.pumpAndSettle();
|
||||
expect(indicatorColors(tester), const <Color>[unselectedColor, unselectedColor, selectedColor]);
|
||||
expect(indicatorColors(tester), const <Color>[kUnselectedColor, kUnselectedColor, kSelectedColor]);
|
||||
|
||||
// Fling to the right, selects indicator 1
|
||||
await tester.fling(find.byType(TabBarView), const Offset(100.0, 0.0), 1000.0);
|
||||
await tester.pumpAndSettle();
|
||||
expect(indicatorColors(tester), const <Color>[unselectedColor, selectedColor, unselectedColor]);
|
||||
expect(indicatorColors(tester), const <Color>[kUnselectedColor, kSelectedColor, kUnselectedColor]);
|
||||
|
||||
});
|
||||
|
||||
testWidgets('PageSelector indicatorColors', (WidgetTester tester) async {
|
||||
const Color kRed = const Color(0xFFFF0000);
|
||||
const Color kBlue = const Color(0xFF0000FF);
|
||||
|
||||
final TabController tabController = new TabController(
|
||||
vsync: const TestVSync(),
|
||||
initialIndex: 1,
|
||||
length: 3,
|
||||
);
|
||||
await tester.pumpWidget(buildFrame(tabController, color: kRed, selectedColor: kBlue));
|
||||
|
||||
expect(tabController.index, 1);
|
||||
expect(indicatorColors(tester), const <Color>[kRed, kBlue, kRed]);
|
||||
|
||||
tabController.index = 0;
|
||||
await tester.pumpAndSettle();
|
||||
expect(indicatorColors(tester), const <Color>[kBlue, kRed, kRed]);
|
||||
});
|
||||
|
||||
testWidgets('PageSelector indicatorSize', (WidgetTester tester) async {
|
||||
final TabController tabController = new TabController(
|
||||
vsync: const TestVSync(),
|
||||
initialIndex: 1,
|
||||
length: 3,
|
||||
);
|
||||
await tester.pumpWidget(buildFrame(tabController, indicatorSize: 16.0));
|
||||
|
||||
final Iterable<Element> indicatorElements = find.descendant(
|
||||
of: find.byType(TabPageSelector),
|
||||
matching: find.byType(TabPageSelectorIndicator),
|
||||
).evaluate();
|
||||
|
||||
// Indicators get an 8 pixel margin, 16 + 8 = 24.
|
||||
for (Element indicatorElement in indicatorElements)
|
||||
expect(indicatorElement.size, const Size(24.0, 24.0));
|
||||
|
||||
expect(tester.getSize(find.byType(TabPageSelector)).height, 24.0);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user