Let CupertinoTabScaffold's tab be programmatically selectable (#16040)
* Let CupertinoTabScaffold's tab be programmatically selectable * Re-use the tab bar's index instead * review
This commit is contained in:
parent
68728e9f98
commit
b397406561
@ -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),
|
||||
);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user