Let CupertinoTabScaffold's tab be programmatically selectable (#16040)
* Let CupertinoTabScaffold's tab be programmatically selectable
* Re-use the tab bar's index instead
* review
diff --git a/packages/flutter/lib/src/cupertino/bottom_tab_bar.dart b/packages/flutter/lib/src/cupertino/bottom_tab_bar.dart
index 9eec803..cf12016 100644
--- a/packages/flutter/lib/src/cupertino/bottom_tab_bar.dart
+++ b/packages/flutter/lib/src/cupertino/bottom_tab_bar.dart
@@ -47,11 +47,14 @@
this.iconSize: 30.0,
}) : assert(items != null),
assert(items.length >= 2),
+ assert(currentIndex != null),
assert(0 <= currentIndex && currentIndex < items.length),
assert(iconSize != null),
super(key: key);
/// The interactive items laid out within the bottom navigation bar.
+ ///
+ /// Must not be null.
final List<BottomNavigationBarItem> items;
/// The callback that is called when a item is tapped.
@@ -62,6 +65,8 @@
final ValueChanged<int> onTap;
/// The index into [items] of the current active item.
+ ///
+ /// Must not be null.
final int currentIndex;
/// The background color of the tab bar. If it contains transparency, the
@@ -82,6 +87,8 @@
/// This value is used to to configure the [IconTheme] for the navigation
/// bar. When a [BottomNavigationBarItem.icon] widget is not an [Icon] the widget
/// should configure itself to match the icon theme's size and color.
+ ///
+ /// Must not be null.
final double iconSize;
/// True if the tab bar's background color has no transparency.
diff --git a/packages/flutter/lib/src/cupertino/tab_scaffold.dart b/packages/flutter/lib/src/cupertino/tab_scaffold.dart
index 3b7bb60..3238108 100644
--- a/packages/flutter/lib/src/cupertino/tab_scaffold.dart
+++ b/packages/flutter/lib/src/cupertino/tab_scaffold.dart
@@ -83,7 +83,10 @@
class CupertinoTabScaffold extends StatefulWidget {
/// Creates a layout for applications with a tab bar at the bottom.
///
- /// The [tabBar] and [tabBuilder] arguments must not be null.
+ /// The [tabBar], [tabBuilder] and [currentTabIndex] arguments must not be null.
+ ///
+ /// The [currentTabIndex] argument can be used to programmatically change the
+ /// currently selected tab.
const CupertinoTabScaffold({
Key key,
@required this.tabBar,
@@ -96,16 +99,20 @@
/// that lets the user switch between different tabs in the main content area
/// when present.
///
- /// When provided, [CupertinoTabBar.currentIndex] will be ignored and will
- /// be managed by the [CupertinoTabScaffold] to show the currently selected page
- /// as the active item index. If [CupertinoTabBar.onTap] is provided, it will
- /// still be called. [CupertinoTabScaffold] automatically also listen to the
+ /// Setting and changing [CupertinoTabBar.currentIndex] programmatically will
+ /// change the currently selected tab item in the [tabBar] as well as change
+ /// the currently focused tab from the [tabBuilder].
+
+ /// If [CupertinoTabBar.onTap] is provided, it will still be called.
+ /// [CupertinoTabScaffold] automatically also listen to the
/// [CupertinoTabBar]'s `onTap` to change the [CupertinoTabBar]'s `currentIndex`
/// and change the actively displayed tab in [CupertinoTabScaffold]'s own
/// main content area.
///
/// If translucent, the main content may slide behind it.
/// Otherwise, the main content's bottom margin will be offset by its height.
+ ///
+ /// Must not be null.
final CupertinoTabBar tabBar;
/// An [IndexedWidgetBuilder] that's called when tabs become active.
@@ -121,6 +128,8 @@
/// In that case, the child's [BuildContext]'s [MediaQuery] will have a
/// bottom padding indicating the area of obstructing overlap from the
/// [tabBar].
+ ///
+ /// Must not be null.
final IndexedWidgetBuilder tabBuilder;
@override
@@ -128,7 +137,21 @@
}
class _CupertinoTabScaffoldState extends State<CupertinoTabScaffold> {
- int _currentPage = 0;
+ int _currentPage;
+
+ @override
+ void initState() {
+ super.initState();
+ _currentPage = widget.tabBar.currentIndex;
+ }
+
+ @override
+ void didUpdateWidget(CupertinoTabScaffold oldWidget) {
+ super.didUpdateWidget(oldWidget);
+ if (widget.tabBar.currentIndex != oldWidget.tabBar.currentIndex) {
+ _currentPage = widget.tabBar.currentIndex;
+ }
+ }
@override
Widget build(BuildContext context) {
diff --git a/packages/flutter/test/cupertino/tab_scaffold_test.dart b/packages/flutter/test/cupertino/tab_scaffold_test.dart
index 7e62b4c..5ef397a 100644
--- a/packages/flutter/test/cupertino/tab_scaffold_test.dart
+++ b/packages/flutter/test/cupertino/tab_scaffold_test.dart
@@ -237,9 +237,64 @@
1,
);
});
+
+ testWidgets('Programmatic tab switching', (WidgetTester tester) async {
+ final List<int> tabsPainted = <int>[];
+
+ await tester.pumpWidget(
+ new WidgetsApp(
+ color: const Color(0xFFFFFFFF),
+ builder: (BuildContext context, Widget child) {
+ return new CupertinoTabScaffold(
+ tabBar: _buildTabBar(),
+ tabBuilder: (BuildContext context, int index) {
+ return new CustomPaint(
+ child: new Text('Page ${index + 1}'),
+ painter: new TestCallbackPainter(
+ onPaint: () { tabsPainted.add(index); }
+ )
+ );
+ },
+ );
+ },
+ ),
+ );
+
+ expect(tabsPainted, <int>[0]);
+
+ await tester.pumpWidget(
+ new WidgetsApp(
+ color: const Color(0xFFFFFFFF),
+ builder: (BuildContext context, Widget child) {
+ return new CupertinoTabScaffold(
+ tabBar: _buildTabBar(selectedTab: 1), // Programmatically change the tab now.
+ tabBuilder: (BuildContext context, int index) {
+ return new CustomPaint(
+ child: new Text('Page ${index + 1}'),
+ painter: new TestCallbackPainter(
+ onPaint: () { tabsPainted.add(index); }
+ )
+ );
+ },
+ );
+ },
+ ),
+ );
+
+ expect(tabsPainted, <int>[0, 1]);
+ // onTap is not called when changing tabs programmatically.
+ expect(selectedTabs, isEmpty);
+
+ // Can still tap out of the programmatically selected tab.
+ await tester.tap(find.text('Tab 1'));
+ await tester.pump();
+
+ expect(tabsPainted, <int>[0, 1, 0]);
+ expect(selectedTabs, <int>[0]);
+ });
}
-CupertinoTabBar _buildTabBar() {
+CupertinoTabBar _buildTabBar({ int selectedTab: 0 }) {
return new CupertinoTabBar(
items: const <BottomNavigationBarItem>[
const BottomNavigationBarItem(
@@ -252,6 +307,7 @@
),
],
backgroundColor: CupertinoColors.white,
+ currentIndex: selectedTab,
onTap: (int newTab) => selectedTabs.add(newTab),
);
}
\ No newline at end of file