Unverified Commit b3974065 authored by xster's avatar xster Committed by GitHub

Let CupertinoTabScaffold's tab be programmatically selectable (#16040)

* Let CupertinoTabScaffold's tab be programmatically selectable

* Re-use the tab bar's index instead

* review
parent 68728e9f
......@@ -47,11 +47,14 @@ class CupertinoTabBar extends StatelessWidget implements PreferredSizeWidget {
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 @@ class CupertinoTabBar extends StatelessWidget implements PreferredSizeWidget {
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 @@ class CupertinoTabBar extends StatelessWidget implements PreferredSizeWidget {
/// 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.
......
......@@ -83,7 +83,10 @@ import 'bottom_tab_bar.dart';
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 @@ class CupertinoTabScaffold extends StatefulWidget {
/// 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 @@ class CupertinoTabScaffold extends StatefulWidget {
/// 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 CupertinoTabScaffold extends StatefulWidget {
}
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) {
......
......@@ -237,9 +237,64 @@ void main() {
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 @@ CupertinoTabBar _buildTabBar() {
),
],
backgroundColor: CupertinoColors.white,
currentIndex: selectedTab,
onTap: (int newTab) => selectedTabs.add(newTab),
);
}
\ No newline at end of file
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