Commit 6494cd1f authored by Hans Muller's avatar Hans Muller

Make TabBarSelection a parameterized type

parent 81b70675
...@@ -9,16 +9,15 @@ import 'widget_demo.dart'; ...@@ -9,16 +9,15 @@ import 'widget_demo.dart';
final List<String> _iconNames = <String>["event", "home", "android", "alarm", "face", "language"]; final List<String> _iconNames = <String>["event", "home", "android", "alarm", "face", "language"];
Widget _buildTabBarSelection(_, Widget child) { Widget _buildTabBarSelection(_, Widget child) {
return new TabBarSelection( return new TabBarSelection<String>(values: _iconNames, child: child);
maxIndex: _iconNames.length - 1,
child: child
);
} }
Widget _buildTabBar(_) { Widget _buildTabBar(_) {
return new TabBar( return new TabBar<String>(
isScrollable: true, isScrollable: true,
labels: _iconNames.map((String iconName) => new TabLabel(text: iconName, icon: "action/$iconName")).toList() labels: new Map.fromIterable(
_iconNames,
value: (String iconName) => new TabLabel(text: iconName, icon: "action/$iconName"))
); );
} }
......
...@@ -161,17 +161,15 @@ class StockHomeState extends State<StockHome> { ...@@ -161,17 +161,15 @@ class StockHomeState extends State<StockHome> {
onPressed: _handleMenuShow onPressed: _handleMenuShow
) )
], ],
tabBar: new TabBar( tabBar: new TabBar<StockHomeTab>(
labels: <TabLabel>[ labels: <StockHomeTab, TabLabel>{
new TabLabel(text: StockStrings.of(context).market()), StockHomeTab.market: new TabLabel(text: StockStrings.of(context).market()),
new TabLabel(text: StockStrings.of(context).portfolio()) StockHomeTab.portfolio: new TabLabel(text: StockStrings.of(context).portfolio())
] }
) )
); );
} }
int selectedTabIndex = 0;
Iterable<Stock> _getStockList(Iterable<String> symbols) { Iterable<Stock> _getStockList(Iterable<String> symbols) {
return symbols.map((String symbol) => config.stocks[symbol]) return symbols.map((String symbol) => config.stocks[symbol])
.where((Stock stock) => stock != null); .where((Stock stock) => stock != null);
...@@ -266,8 +264,8 @@ class StockHomeState extends State<StockHome> { ...@@ -266,8 +264,8 @@ class StockHomeState extends State<StockHome> {
} }
Widget build(BuildContext context) { Widget build(BuildContext context) {
return new TabBarSelection( return new TabBarSelection<StockHomeTab>(
maxIndex: 1, values: <StockHomeTab>[StockHomeTab.market, StockHomeTab.portfolio],
child: new Scaffold( child: new Scaffold(
key: _scaffoldKey, key: _scaffoldKey,
toolBar: _isSearching ? buildSearchBar() : buildToolBar(), toolBar: _isSearching ? buildSearchBar() : buildToolBar(),
......
...@@ -387,81 +387,115 @@ abstract class TabBarSelectionPerformanceListener { ...@@ -387,81 +387,115 @@ abstract class TabBarSelectionPerformanceListener {
void handleSelectionDeactivate(); void handleSelectionDeactivate();
} }
class TabBarSelection extends StatefulComponent { class TabBarSelection<T> extends StatefulComponent {
TabBarSelection({ TabBarSelection({
Key key, Key key,
this.index, this.value,
this.maxIndex, this.values,
this.onChanged, this.onChanged,
this.child this.child
}) : super(key: key) { }) : super(key: key) {
assert(values != null && values.length > 0);
assert(new Set<T>.from(values).length == values.length);
assert(value == null ? true : values.where((T e) => e == value).length == 1);
assert(child != null); assert(child != null);
assert(maxIndex != null);
assert((index != null) ? index >= 0 && index <= maxIndex : true);
} }
final int index; final T value;
final int maxIndex; List<T> values;
final ValueChanged<T> onChanged;
final Widget child; final Widget child;
final ValueChanged<int> onChanged;
TabBarSelectionState createState() => new TabBarSelectionState(); TabBarSelectionState createState() => new TabBarSelectionState<T>();
static TabBarSelectionState of(BuildContext context) { static TabBarSelectionState of(BuildContext context) {
return context.ancestorStateOfType(TabBarSelectionState); TabBarSelectionState result = null;
context.visitAncestorElements((ancestor) {
if (ancestor is StatefulComponentElement && ancestor.state is TabBarSelectionState) {
result = ancestor.state;
return false;
}
return true;
});
return result;
} }
} }
class TabBarSelectionState extends State<TabBarSelection> { class TabBarSelectionState<T> extends State<TabBarSelection<T>> {
PerformanceView get performance => _performance.view; PerformanceView get performance => _performance.view;
// Both the TabBar and TabBarView classes access _performance because they // Both the TabBar and TabBarView classes access _performance because they
// alternately drive selection progress between tabs. // alternately drive selection progress between tabs.
final _performance = new Performance(duration: _kTabBarScroll, progress: 1.0); final _performance = new Performance(duration: _kTabBarScroll, progress: 1.0);
final Map<T, int> _valueToIndex = new Map<T, int>();
void _initValueToIndex() {
_valueToIndex.clear();
int index = 0;
for(T value in values)
_valueToIndex[value] = index++;
}
void initState() { void initState() {
super.initState(); super.initState();
_index = config.index ?? PageStorage.of(context)?.readState(context) ?? 0; _value = config.value ?? PageStorage.of(context)?.readState(context) ?? values.first;
_previousValue = _value;
_initValueToIndex();
}
void didUpdateConfig(TabBarSelection oldConfig) {
super.didUpdateConfig(oldConfig);
if (values != oldConfig.values)
_initValueToIndex();
} }
void dispose() { void dispose() {
_performance.stop(); _performance.stop();
PageStorage.of(context)?.writeState(context, _index); PageStorage.of(context)?.writeState(context, _value);
super.dispose(); super.dispose();
} }
bool _indexIsChanging = false; List<T> get values => config.values;
bool get indexIsChanging => _indexIsChanging;
T get previousValue => _previousValue;
T _previousValue;
bool _valueIsChanging = false;
bool get valueIsChanging => _valueIsChanging;
int get index => _index; int indexOf(T tabValue) => _valueToIndex[tabValue];
int _index; int get index => _valueToIndex[value];
void set index(int value) { int get previousIndex => indexOf(_previousValue);
if (value == _index)
T get value => _value;
T _value;
void set value(T newValue) {
if (newValue == _value)
return; return;
if (!_indexIsChanging) if (!_valueIsChanging)
_previousIndex = _index; _previousValue = _value;
_index = value; _value = newValue;
_indexIsChanging = true; _valueIsChanging = true;
// If the selected index change was triggered by a drag gesture, the current // If the selected value change was triggered by a drag gesture, the current
// value of _performance.progress will reflect where the gesture ended. While // value of _performance.progress will reflect where the gesture ended. While
// the drag was underway progress indicates where the indicator and TabBarView // the drag was underway progress indicates where the indicator and TabBarView
// scrollPosition are vis the indices of the two tabs adjacent to the selected // scrollPosition are vis the indices of the two tabs adjacent to the selected
// one. So 0.5 means the drag didn't move at all, 0.0 means the drag extended // one. So 0.5 means the drag didn't move at all, 0.0 means the drag extended
// to the beginning of the tab on the left and 1.0 likewise for the tab on the // to the beginning of the tab on the left and 1.0 likewise for the tab on the
// right. That is unless the selected index was 0 or maxIndex. In those cases // right. That is unless the index of the selected value was 0 or values.length - 1.
// progress just moves between the selected tab and the adjacent one. // In those cases progress just moves between the selected tab and the adjacent
// Convert progress to reflect the fact that we're now moving between (just) // one. Convert progress to reflect the fact that we're now moving between (just)
// the previous and current selection index. // the previous and current selection index.
double progress; double progress;
if (_performance.status == PerformanceStatus.completed) if (_performance.status == PerformanceStatus.completed)
progress = 0.0; progress = 0.0;
else if (_previousIndex == 0) else if (_previousValue == values.first)
progress = _performance.progress; progress = _performance.progress;
else if (_previousIndex == config.maxIndex) else if (_previousValue == values.last)
progress = 1.0 - _performance.progress; progress = 1.0 - _performance.progress;
else if (_previousIndex < _index) else if (previousIndex < index)
progress = (_performance.progress - 0.5) * 2.0; progress = (_performance.progress - 0.5) * 2.0;
else else
progress = 1.0 - _performance.progress * 2.0; progress = 1.0 - _performance.progress * 2.0;
...@@ -471,15 +505,12 @@ class TabBarSelectionState extends State<TabBarSelection> { ...@@ -471,15 +505,12 @@ class TabBarSelectionState extends State<TabBarSelection> {
..forward().then((_) { ..forward().then((_) {
if (_performance.progress == 1.0) { if (_performance.progress == 1.0) {
if (config.onChanged != null) if (config.onChanged != null)
config.onChanged(_index); config.onChanged(_value);
_indexIsChanging = false; _valueIsChanging = false;
} }
}); });
} }
int get previousIndex => _previousIndex;
int _previousIndex = 0;
final List<TabBarSelectionPerformanceListener> _performanceListeners = <TabBarSelectionPerformanceListener>[]; final List<TabBarSelectionPerformanceListener> _performanceListeners = <TabBarSelectionPerformanceListener>[];
void registerPerformanceListener(TabBarSelectionPerformanceListener listener) { void registerPerformanceListener(TabBarSelectionPerformanceListener listener) {
...@@ -509,7 +540,6 @@ class TabBarSelectionState extends State<TabBarSelection> { ...@@ -509,7 +540,6 @@ class TabBarSelectionState extends State<TabBarSelection> {
} }
} }
/// Displays a horizontal row of tabs, one per label. If isScrollable is /// Displays a horizontal row of tabs, one per label. If isScrollable is
/// true then each tab is as wide as needed for its label and the entire /// true then each tab is as wide as needed for its label and the entire
/// [TabBar] is scrollable. Otherwise each tab gets an equal share of the /// [TabBar] is scrollable. Otherwise each tab gets an equal share of the
...@@ -517,34 +547,40 @@ class TabBarSelectionState extends State<TabBarSelection> { ...@@ -517,34 +547,40 @@ class TabBarSelectionState extends State<TabBarSelection> {
/// built to enable saving and monitoring the selected tab. /// built to enable saving and monitoring the selected tab.
/// ///
/// Tabs must always have an ancestor Material object. /// Tabs must always have an ancestor Material object.
class TabBar extends Scrollable { class TabBar<T> extends Scrollable {
TabBar({ TabBar({
Key key, Key key,
this.labels, this.labels,
this.isScrollable: false this.isScrollable: false
}) : super(key: key, scrollDirection: ScrollDirection.horizontal) { }) : super(key: key, scrollDirection: ScrollDirection.horizontal);
assert(labels != null);
assert(labels.length > 1);
}
final Iterable<TabLabel> labels; final Map<T, TabLabel> labels;
final bool isScrollable; final bool isScrollable;
_TabBarState createState() => new _TabBarState(); _TabBarState createState() => new _TabBarState();
} }
class _TabBarState extends ScrollableState<TabBar> implements TabBarSelectionPerformanceListener { class _TabBarState<T> extends ScrollableState<TabBar<T>> implements TabBarSelectionPerformanceListener {
TabBarSelectionState _selection; TabBarSelectionState _selection;
bool _indexIsChanging = false; bool _valueIsChanging = false;
int get _tabCount => config.labels.length; void _initSelection(TabBarSelectionState<T> selection) {
_selection?.unregisterPerformanceListener(this);
_selection = selection;
_selection?.registerPerformanceListener(this);
}
void initState() { void initState() {
super.initState(); super.initState();
scrollBehavior.isScrollable = config.isScrollable; scrollBehavior.isScrollable = config.isScrollable;
_selection = TabBarSelection.of(context); _initSelection(TabBarSelection.of(context));
_selection?.registerPerformanceListener(this); }
void didUpdateConfig(TabBar oldConfig) {
super.didUpdateConfig(oldConfig);
if (!config.isScrollable)
scrollTo(0.0);
} }
void dispose() { void dispose() {
...@@ -557,20 +593,20 @@ class _TabBarState extends ScrollableState<TabBar> implements TabBarSelectionPer ...@@ -557,20 +593,20 @@ class _TabBarState extends ScrollableState<TabBar> implements TabBarSelectionPer
} }
void handleStatusChange(PerformanceStatus status) { void handleStatusChange(PerformanceStatus status) {
if (_tabCount == 0) if (config.labels.length == 0)
return; return;
if (_indexIsChanging && status == PerformanceStatus.completed) { if (_valueIsChanging && status == PerformanceStatus.completed) {
_indexIsChanging = false; _valueIsChanging = false;
double progress = 0.5; double progress = 0.5;
if (_selection.index == 0) if (_selection.index == 0)
progress = 0.0; progress = 0.0;
else if (_selection.index == _tabCount - 1) else if (_selection.index == config.labels.length - 1)
progress = 1.0; progress = 1.0;
setState(() { setState(() {
_indicatorRect _indicatorRect
..begin = _tabIndicatorRect(math.max(0, _selection.index - 1)) ..begin = _tabIndicatorRect(math.max(0, _selection.index - 1))
..end = _tabIndicatorRect(math.min(_tabCount - 1, _selection.index + 1)) ..end = _tabIndicatorRect(math.min(config.labels.length - 1, _selection.index + 1))
..curve = null ..curve = null
..setProgress(progress, AnimationDirection.forward); ..setProgress(progress, AnimationDirection.forward);
}); });
...@@ -578,17 +614,17 @@ class _TabBarState extends ScrollableState<TabBar> implements TabBarSelectionPer ...@@ -578,17 +614,17 @@ class _TabBarState extends ScrollableState<TabBar> implements TabBarSelectionPer
} }
void handleProgressChange() { void handleProgressChange() {
if (_tabCount == 0 || _selection == null) if (config.labels.length == 0 || _selection == null)
return; return;
if (!_indexIsChanging && _selection.indexIsChanging) { if (!_valueIsChanging && _selection.valueIsChanging) {
if (config.isScrollable) if (config.isScrollable)
scrollTo(_centeredTabScrollOffset(_selection.index), duration: _kTabBarScroll); scrollTo(_centeredTabScrollOffset(_selection.index), duration: _kTabBarScroll);
_indicatorRect _indicatorRect
..begin = _indicatorRect.value ?? _tabIndicatorRect(_selection.previousIndex) ..begin = _indicatorRect.value ?? _tabIndicatorRect(_selection.previousIndex)
..end = _tabIndicatorRect(_selection.index) ..end = _tabIndicatorRect(_selection.index)
..curve = Curves.ease; ..curve = Curves.ease;
_indexIsChanging = true; _valueIsChanging = true;
} }
Rect oldRect = _indicatorRect.value; Rect oldRect = _indicatorRect.value;
_indicatorRect.setProgress(_selection.performance.progress, AnimationDirection.forward); _indicatorRect.setProgress(_selection.performance.progress, AnimationDirection.forward);
...@@ -620,12 +656,6 @@ class _TabBarState extends ScrollableState<TabBar> implements TabBarSelectionPer ...@@ -620,12 +656,6 @@ class _TabBarState extends ScrollableState<TabBar> implements TabBarSelectionPer
return new Rect.fromLTRB(r.left, r.bottom, r.right, r.bottom + _kTabIndicatorHeight); return new Rect.fromLTRB(r.left, r.bottom, r.right, r.bottom + _kTabIndicatorHeight);
} }
void didUpdateConfig(TabBar oldConfig) {
super.didUpdateConfig(oldConfig);
if (!config.isScrollable)
scrollTo(0.0);
}
ScrollBehavior createScrollBehavior() => new _TabsScrollBehavior(); ScrollBehavior createScrollBehavior() => new _TabsScrollBehavior();
_TabsScrollBehavior get scrollBehavior => super.scrollBehavior; _TabsScrollBehavior get scrollBehavior => super.scrollBehavior;
...@@ -639,7 +669,7 @@ class _TabBarState extends ScrollableState<TabBar> implements TabBarSelectionPer ...@@ -639,7 +669,7 @@ class _TabBarState extends ScrollableState<TabBar> implements TabBarSelectionPer
void _handleTabSelected(int tabIndex) { void _handleTabSelected(int tabIndex) {
if (_selection != null && tabIndex != _selection.index) if (_selection != null && tabIndex != _selection.index)
setState(() { setState(() {
_selection.index = tabIndex; _selection.value = _selection.values[tabIndex];
}); });
} }
...@@ -649,7 +679,7 @@ class _TabBarState extends ScrollableState<TabBar> implements TabBarSelectionPer ...@@ -649,7 +679,7 @@ class _TabBarState extends ScrollableState<TabBar> implements TabBarSelectionPer
final bool isSelectedTab = tabIndex == _selection.index; final bool isSelectedTab = tabIndex == _selection.index;
final bool isPreviouslySelectedTab = tabIndex == _selection.previousIndex; final bool isPreviouslySelectedTab = tabIndex == _selection.previousIndex;
labelColor = isSelectedTab ? selectedColor : color; labelColor = isSelectedTab ? selectedColor : color;
if (_selection.indexIsChanging) { if (_selection.valueIsChanging) {
if (isSelectedTab) if (isSelectedTab)
labelColor = Color.lerp(color, selectedColor, _selection.performance.progress); labelColor = Color.lerp(color, selectedColor, _selection.performance.progress);
else if (isPreviouslySelectedTab) else if (isPreviouslySelectedTab)
...@@ -686,14 +716,11 @@ class _TabBarState extends ScrollableState<TabBar> implements TabBarSelectionPer ...@@ -686,14 +716,11 @@ class _TabBarState extends ScrollableState<TabBar> implements TabBarSelectionPer
} }
Widget buildContent(BuildContext context) { Widget buildContent(BuildContext context) {
TabBarSelectionState oldSelection = _selection; TabBarSelectionState<T> newSelection = TabBarSelection.of(context);
_selection = TabBarSelection.of(context); if (_selection != newSelection)
if (oldSelection != _selection) { _initSelection(newSelection);
oldSelection?.registerPerformanceListener(this);
_selection?.registerPerformanceListener(this);
}
assert(config.labels != null && config.labels.isNotEmpty); assert(config.labels.isNotEmpty);
assert(Material.of(context) != null); assert(Material.of(context) != null);
ThemeData themeData = Theme.of(context); ThemeData themeData = Theme.of(context);
...@@ -708,7 +735,7 @@ class _TabBarState extends ScrollableState<TabBar> implements TabBarSelectionPer ...@@ -708,7 +735,7 @@ class _TabBarState extends ScrollableState<TabBar> implements TabBarSelectionPer
List<Widget> tabs = <Widget>[]; List<Widget> tabs = <Widget>[];
bool textAndIcons = false; bool textAndIcons = false;
int tabIndex = 0; int tabIndex = 0;
for (TabLabel label in config.labels) { for (TabLabel label in config.labels.values) {
tabs.add(_toTab(label, tabIndex++, textStyle.color, indicatorColor)); tabs.add(_toTab(label, tabIndex++, textStyle.color, indicatorColor));
if (label.text != null && label.icon != null) if (label.text != null && label.icon != null)
textAndIcons = true; textAndIcons = true;
...@@ -780,15 +807,19 @@ class _TabBarViewState<T> extends PageableListState<T, TabBarView<T>> implements ...@@ -780,15 +807,19 @@ class _TabBarViewState<T> extends PageableListState<T, TabBarView<T>> implements
} }
void initState() { void _initSelection(TabBarSelectionState<T> selection) {
super.initState(); _selection = selection;
_selection = TabBarSelection.of(context);
if (_selection != null) { if (_selection != null) {
_selection.registerPerformanceListener(this); _selection.registerPerformanceListener(this);
_initItemIndicesAndScrollPosition(); _initItemIndicesAndScrollPosition();
} }
} }
void initState() {
super.initState();
_initSelection(TabBarSelection.of(context));
}
void dispose() { void dispose() {
_selection?.unregisterPerformanceListener(this); _selection?.unregisterPerformanceListener(this);
super.dispose(); super.dispose();
...@@ -817,7 +848,7 @@ class _TabBarViewState<T> extends PageableListState<T, TabBarView<T>> implements ...@@ -817,7 +848,7 @@ class _TabBarViewState<T> extends PageableListState<T, TabBarView<T>> implements
} }
void handleProgressChange() { void handleProgressChange() {
if (_selection == null || !_selection.indexIsChanging) if (_selection == null || !_selection.valueIsChanging)
return; return;
// The TabBar is driving the TabBarSelection performance. // The TabBar is driving the TabBarSelection performance.
...@@ -851,7 +882,7 @@ class _TabBarViewState<T> extends PageableListState<T, TabBarView<T>> implements ...@@ -851,7 +882,7 @@ class _TabBarViewState<T> extends PageableListState<T, TabBarView<T>> implements
int get itemCount => _itemIndices.length; int get itemCount => _itemIndices.length;
void dispatchOnScroll() { void dispatchOnScroll() {
if (_selection == null || _selection.indexIsChanging) if (_selection == null || _selection.valueIsChanging)
return; return;
// This class is driving the TabBarSelection's performance. // This class is driving the TabBarSelection's performance.
...@@ -864,36 +895,31 @@ class _TabBarViewState<T> extends PageableListState<T, TabBarView<T>> implements ...@@ -864,36 +895,31 @@ class _TabBarViewState<T> extends PageableListState<T, TabBarView<T>> implements
} }
Future fling(Offset scrollVelocity) { Future fling(Offset scrollVelocity) {
// TODO(hansmuller): should not short-circuit in this case. if (_selection == null || _selection.valueIsChanging)
if (_selection == null || _selection.indexIsChanging)
return new Future.value(); return new Future.value();
if (scrollVelocity.dx.abs() > _kMinFlingVelocity) { if (scrollVelocity.dx.abs() > _kMinFlingVelocity) {
final int selectionDelta = scrollVelocity.dx > 0 ? -1 : 1; final int selectionDelta = scrollVelocity.dx > 0 ? -1 : 1;
_selection.index = (_selection.index + selectionDelta).clamp(0, _tabCount - 1); _selection.value = _selection.values[(_selection.index + selectionDelta).clamp(0, _tabCount - 1)];
return new Future.value(); return new Future.value();
} }
final int selectionIndex = _selection.index; final int selectionIndex = _selection.index;
final int settleIndex = snapScrollOffset(scrollOffset).toInt(); final int settleIndex = snapScrollOffset(scrollOffset).toInt();
if (selectionIndex > 0 && settleIndex != 1) { if (selectionIndex > 0 && settleIndex != 1) {
_selection.index += settleIndex == 2 ? 1 : -1; _selection.value = _selection.values[selectionIndex + (settleIndex == 2 ? 1 : -1)];
return new Future.value(); return new Future.value();
} else if (selectionIndex == 0 && settleIndex == 1) { } else if (selectionIndex == 0 && settleIndex == 1) {
_selection.index = 1; _selection.value = _selection.values[1];
return new Future.value(); return new Future.value();
} }
return settleScrollOffset(); return settleScrollOffset();
} }
List<Widget> buildItems(BuildContext context, int start, int count) { List<Widget> buildItems(BuildContext context, int start, int count) {
TabBarSelectionState oldSelection = _selection; TabBarSelectionState<T> newSelection = TabBarSelection.of(context);
_selection = TabBarSelection.of(context); if (_selection != newSelection)
if (oldSelection != _selection) { _initSelection(newSelection);
oldSelection?.unregisterPerformanceListener(this);
_selection?.registerPerformanceListener(this);
}
return _itemIndices return _itemIndices
.skip(start) .skip(start)
.take(count) .take(count)
......
...@@ -7,13 +7,13 @@ import 'package:flutter/material.dart'; ...@@ -7,13 +7,13 @@ import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
Widget buildFrame({ List<String> tabs, bool isScrollable: false }) { Widget buildFrame({ List<String> tabs, String value, bool isScrollable: false }) {
return new Material( return new Material(
child: new TabBarSelection( child: new TabBarSelection<String>(
index: 2, value: value,
maxIndex: tabs.length - 1, values: tabs,
child: new TabBar( child: new TabBar<String>(
labels: tabs.map((String tab) => new TabLabel(text: tab)).toList(), labels: new Map<String, TabLabel>.fromIterable(tabs, value: (String tab) => new TabLabel(text: tab)),
isScrollable: isScrollable isScrollable: isScrollable
) )
) )
...@@ -25,28 +25,48 @@ void main() { ...@@ -25,28 +25,48 @@ void main() {
testWidgets((WidgetTester tester) { testWidgets((WidgetTester tester) {
List<String> tabs = <String>['A', 'B', 'C']; List<String> tabs = <String>['A', 'B', 'C'];
tester.pumpWidget(buildFrame(tabs: tabs, isScrollable: false)); tester.pumpWidget(buildFrame(tabs: tabs, value: 'C', isScrollable: false));
TabBarSelectionState selection = tester.findStateOfType(TabBarSelectionState); TabBarSelectionState<String> selection = TabBarSelection.of(tester.findText('A'));
expect(selection, isNotNull); expect(selection, isNotNull);
expect(selection.indexOf('A'), equals(0));
expect(selection.indexOf('B'), equals(1));
expect(selection.indexOf('C'), equals(2));
expect(tester.findText('A'), isNotNull); expect(tester.findText('A'), isNotNull);
expect(tester.findText('B'), isNotNull); expect(tester.findText('B'), isNotNull);
expect(tester.findText('C'), isNotNull); expect(tester.findText('C'), isNotNull);
expect(selection.index, equals(2)); expect(selection.index, equals(2));
expect(selection.previousIndex, equals(2));
expect(selection.value, equals('C'));
expect(selection.previousValue, equals('C'));
tester.pumpWidget(buildFrame(tabs: tabs, isScrollable: false)); tester.pumpWidget(buildFrame(tabs: tabs, value: 'C' ,isScrollable: false));
tester.tap(tester.findText('B')); tester.tap(tester.findText('B'));
tester.pump(); tester.pump();
expect(selection.valueIsChanging, true);
tester.pump(const Duration(seconds: 1)); // finish the animation
expect(selection.valueIsChanging, false);
expect(selection.value, equals('B'));
expect(selection.previousValue, equals('C'));
expect(selection.index, equals(1)); expect(selection.index, equals(1));
expect(selection.previousIndex, equals(2));
tester.pumpWidget(buildFrame(tabs: tabs, isScrollable: false)); tester.pumpWidget(buildFrame(tabs: tabs, value: 'C', isScrollable: false));
tester.tap(tester.findText('C')); tester.tap(tester.findText('C'));
tester.pump(); tester.pump();
tester.pump(const Duration(seconds: 1));
expect(selection.value, equals('C'));
expect(selection.previousValue, equals('B'));
expect(selection.index, equals(2)); expect(selection.index, equals(2));
expect(selection.previousIndex, equals(1));
tester.pumpWidget(buildFrame(tabs: tabs, isScrollable: false)); tester.pumpWidget(buildFrame(tabs: tabs, value: 'C', isScrollable: false));
tester.tap(tester.findText('A')); tester.tap(tester.findText('A'));
tester.pump(); tester.pump();
tester.pump(const Duration(seconds: 1));
expect(selection.value, equals('A'));
expect(selection.previousValue, equals('C'));
expect(selection.index, equals(0)); expect(selection.index, equals(0));
expect(selection.previousIndex, equals(2));
}); });
}); });
...@@ -54,28 +74,28 @@ void main() { ...@@ -54,28 +74,28 @@ void main() {
testWidgets((WidgetTester tester) { testWidgets((WidgetTester tester) {
List<String> tabs = <String>['A', 'B', 'C']; List<String> tabs = <String>['A', 'B', 'C'];
tester.pumpWidget(buildFrame(tabs: tabs, isScrollable: true)); tester.pumpWidget(buildFrame(tabs: tabs, value: 'C', isScrollable: true));
TabBarSelectionState selection = tester.findStateOfType(TabBarSelectionState); TabBarSelectionState<String> selection = TabBarSelection.of(tester.findText('A'));
expect(selection, isNotNull); expect(selection, isNotNull);
expect(tester.findText('A'), isNotNull); expect(tester.findText('A'), isNotNull);
expect(tester.findText('B'), isNotNull); expect(tester.findText('B'), isNotNull);
expect(tester.findText('C'), isNotNull); expect(tester.findText('C'), isNotNull);
expect(selection.index, equals(2)); expect(selection.value, equals('C'));
tester.pumpWidget(buildFrame(tabs: tabs, isScrollable: true)); tester.pumpWidget(buildFrame(tabs: tabs, value: 'C', isScrollable: true));
tester.tap(tester.findText('B')); tester.tap(tester.findText('B'));
tester.pump(); tester.pump();
expect(selection.index, equals(1)); expect(selection.value, equals('B'));
tester.pumpWidget(buildFrame(tabs: tabs, isScrollable: true)); tester.pumpWidget(buildFrame(tabs: tabs, value: 'C', isScrollable: true));
tester.tap(tester.findText('C')); tester.tap(tester.findText('C'));
tester.pump(); tester.pump();
expect(selection.index, equals(2)); expect(selection.value, equals('C'));
tester.pumpWidget(buildFrame(tabs: tabs, isScrollable: true)); tester.pumpWidget(buildFrame(tabs: tabs, value: 'C', isScrollable: true));
tester.tap(tester.findText('A')); tester.tap(tester.findText('A'));
tester.pump(); tester.pump();
expect(selection.index, equals(0)); expect(selection.value, equals('A'));
}); });
}); });
} }
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