| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'package:flutter/material.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| |
| const Color kSelectedColor = Color(0xFF00FF00); |
| const Color kUnselectedColor = Colors.transparent; |
| |
| Widget buildFrame(TabController tabController, { Color? color, Color? selectedColor, double indicatorSize = 12.0, BorderStyle? borderStyle }) { |
| return Localizations( |
| locale: const Locale('en', 'US'), |
| delegates: const <LocalizationsDelegate<dynamic>>[ |
| DefaultMaterialLocalizations.delegate, |
| DefaultWidgetsLocalizations.delegate, |
| ], |
| child: Directionality( |
| textDirection: TextDirection.ltr, |
| child: Theme( |
| data: ThemeData(colorScheme: const ColorScheme.light().copyWith(secondary: kSelectedColor)), |
| child: SizedBox.expand( |
| child: Center( |
| child: SizedBox( |
| width: 400.0, |
| height: 400.0, |
| child: Column( |
| children: <Widget>[ |
| TabPageSelector( |
| controller: tabController, |
| color: color, |
| selectedColor: selectedColor, |
| indicatorSize: indicatorSize, |
| borderStyle: borderStyle, |
| ), |
| Flexible( |
| child: TabBarView( |
| controller: tabController, |
| children: const <Widget>[ |
| Center(child: Text('0')), |
| Center(child: Text('1')), |
| Center(child: Text('2')), |
| ], |
| ), |
| ), |
| ], |
| ), |
| ), |
| ), |
| ), |
| ), |
| ), |
| ); |
| } |
| |
| List<Color> indicatorColors(WidgetTester tester) { |
| final Iterable<TabPageSelectorIndicator> indicators = tester.widgetList( |
| find.descendant( |
| of: find.byType(TabPageSelector), |
| matching: find.byType(TabPageSelectorIndicator), |
| ), |
| ); |
| return indicators.map<Color>((TabPageSelectorIndicator indicator) => indicator.backgroundColor).toList(); |
| } |
| |
| void main() { |
| testWidgets('PageSelector responds correctly to setting the TabController index', (WidgetTester tester) async { |
| final TabController tabController = TabController( |
| vsync: const TestVSync(), |
| length: 3, |
| ); |
| await tester.pumpWidget(buildFrame(tabController)); |
| |
| expect(tabController.index, 0); |
| expect(indicatorColors(tester), const <Color>[kSelectedColor, kUnselectedColor, kUnselectedColor]); |
| |
| tabController.index = 1; |
| await tester.pump(); |
| expect(tabController.index, 1); |
| expect(indicatorColors(tester), const <Color>[kUnselectedColor, kSelectedColor, kUnselectedColor]); |
| |
| tabController.index = 2; |
| await tester.pump(); |
| expect(tabController.index, 2); |
| expect(indicatorColors(tester), const <Color>[kUnselectedColor, kUnselectedColor, kSelectedColor]); |
| }); |
| |
| testWidgets('PageSelector responds correctly to TabController.animateTo()', (WidgetTester tester) async { |
| final TabController tabController = TabController( |
| vsync: const TestVSync(), |
| length: 3, |
| ); |
| await tester.pumpWidget(buildFrame(tabController)); |
| |
| expect(tabController.index, 0); |
| expect(indicatorColors(tester), const <Color>[kSelectedColor, kUnselectedColor, kUnselectedColor]); |
| |
| tabController.animateTo(1, duration: const Duration(milliseconds: 200)); |
| await tester.pump(); |
| // Verify that indicator 0's color is becoming increasingly transparent, |
| // and indicator 1's color is becoming increasingly opaque during the |
| // 200ms animation. Indicator 2 remains transparent throughout. |
| await tester.pump(const Duration(milliseconds: 10)); |
| List<Color> colors = indicatorColors(tester); |
| expect(colors[0].alpha, greaterThan(colors[1].alpha)); |
| 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], kUnselectedColor); |
| await tester.pumpAndSettle(); |
| expect(tabController.index, 1); |
| expect(indicatorColors(tester), const <Color>[kUnselectedColor, kSelectedColor, kUnselectedColor]); |
| |
| tabController.animateTo(2, duration: const Duration(milliseconds: 200)); |
| await tester.pump(); |
| // Same animation test as above for indicators 1 and 2. |
| await tester.pump(const Duration(milliseconds: 10)); |
| colors = indicatorColors(tester); |
| expect(colors[1].alpha, greaterThan(colors[2].alpha)); |
| 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], kUnselectedColor); |
| await tester.pumpAndSettle(); |
| expect(tabController.index, 2); |
| expect(indicatorColors(tester), const <Color>[kUnselectedColor, kUnselectedColor, kSelectedColor]); |
| }); |
| |
| testWidgets('PageSelector responds correctly to TabBarView drags', (WidgetTester tester) async { |
| final TabController tabController = TabController( |
| vsync: const TestVSync(), |
| initialIndex: 1, |
| length: 3, |
| ); |
| await tester.pumpWidget(buildFrame(tabController)); |
| |
| expect(tabController.index, 1); |
| expect(indicatorColors(tester), const <Color>[kUnselectedColor, kSelectedColor, kUnselectedColor]); |
| |
| final TestGesture gesture = await tester.startGesture(const Offset(200.0, 200.0)); |
| |
| // Drag to the left moving the selection towards indicator 2. Indicator 2's |
| // opacity should increase and Indicator 1's opacity should decrease. |
| await gesture.moveBy(const Offset(-100.0, 0.0)); |
| await tester.pumpAndSettle(); |
| List<Color> colors = indicatorColors(tester); |
| expect(colors[1].alpha, greaterThan(colors[2].alpha)); |
| 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>[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. |
| await gesture.moveBy(const Offset(100.0, 0.0)); |
| await tester.pumpAndSettle(); |
| colors = indicatorColors(tester); |
| expect(colors[1].alpha, greaterThan(colors[0].alpha)); |
| 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>[kUnselectedColor, kSelectedColor, kUnselectedColor]); |
| |
| // Completing the gesture doesn't change anything |
| await gesture.up(); |
| await tester.pumpAndSettle(); |
| colors = indicatorColors(tester); |
| 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>[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>[kUnselectedColor, kSelectedColor, kUnselectedColor]); |
| |
| }); |
| |
| testWidgets('PageSelector indicatorColors', (WidgetTester tester) async { |
| const Color kRed = Color(0xFFFF0000); |
| const Color kBlue = Color(0xFF0000FF); |
| |
| final TabController tabController = 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 = 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 (final Element indicatorElement in indicatorElements) { |
| expect(indicatorElement.size, const Size(24.0, 24.0)); |
| } |
| |
| expect(tester.getSize(find.byType(TabPageSelector)).height, 24.0); |
| }); |
| |
| testWidgets('PageSelector circle border', (WidgetTester tester) async { |
| final TabController tabController = TabController( |
| vsync: const TestVSync(), |
| initialIndex: 1, |
| length: 3, |
| ); |
| |
| Iterable<TabPageSelectorIndicator> indicators; |
| |
| // Default border |
| await tester.pumpWidget(buildFrame(tabController)); |
| indicators = tester.widgetList( |
| find.descendant( |
| of: find.byType(TabPageSelector), |
| matching: find.byType(TabPageSelectorIndicator), |
| ), |
| ); |
| for (final TabPageSelectorIndicator indicator in indicators) { |
| expect(indicator.borderStyle, BorderStyle.solid); |
| } |
| |
| // No border |
| await tester.pumpWidget(buildFrame(tabController, borderStyle: BorderStyle.none)); |
| indicators = tester.widgetList( |
| find.descendant( |
| of: find.byType(TabPageSelector), |
| matching: find.byType(TabPageSelectorIndicator), |
| ), |
| ); |
| for (final TabPageSelectorIndicator indicator in indicators) { |
| expect(indicator.borderStyle, BorderStyle.none); |
| } |
| |
| // Solid border |
| await tester.pumpWidget(buildFrame(tabController, borderStyle: BorderStyle.solid)); |
| indicators = tester.widgetList( |
| find.descendant( |
| of: find.byType(TabPageSelector), |
| matching: find.byType(TabPageSelectorIndicator), |
| ), |
| ); |
| for (final TabPageSelectorIndicator indicator in indicators) { |
| expect(indicator.borderStyle, BorderStyle.solid); |
| } |
| }); |
| } |