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,
|
this.iconSize: 30.0,
|
||||||
}) : assert(items != null),
|
}) : assert(items != null),
|
||||||
assert(items.length >= 2),
|
assert(items.length >= 2),
|
||||||
|
assert(currentIndex != null),
|
||||||
assert(0 <= currentIndex && currentIndex < items.length),
|
assert(0 <= currentIndex && currentIndex < items.length),
|
||||||
assert(iconSize != null),
|
assert(iconSize != null),
|
||||||
super(key: key);
|
super(key: key);
|
||||||
|
|
||||||
/// The interactive items laid out within the bottom navigation bar.
|
/// The interactive items laid out within the bottom navigation bar.
|
||||||
|
///
|
||||||
|
/// Must not be null.
|
||||||
final List<BottomNavigationBarItem> items;
|
final List<BottomNavigationBarItem> items;
|
||||||
|
|
||||||
/// The callback that is called when a item is tapped.
|
/// The callback that is called when a item is tapped.
|
||||||
@ -62,6 +65,8 @@ class CupertinoTabBar extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
final ValueChanged<int> onTap;
|
final ValueChanged<int> onTap;
|
||||||
|
|
||||||
/// The index into [items] of the current active item.
|
/// The index into [items] of the current active item.
|
||||||
|
///
|
||||||
|
/// Must not be null.
|
||||||
final int currentIndex;
|
final int currentIndex;
|
||||||
|
|
||||||
/// The background color of the tab bar. If it contains transparency, the
|
/// 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
|
/// This value is used to to configure the [IconTheme] for the navigation
|
||||||
/// bar. When a [BottomNavigationBarItem.icon] widget is not an [Icon] the widget
|
/// bar. When a [BottomNavigationBarItem.icon] widget is not an [Icon] the widget
|
||||||
/// should configure itself to match the icon theme's size and color.
|
/// should configure itself to match the icon theme's size and color.
|
||||||
|
///
|
||||||
|
/// Must not be null.
|
||||||
final double iconSize;
|
final double iconSize;
|
||||||
|
|
||||||
/// True if the tab bar's background color has no transparency.
|
/// True if the tab bar's background color has no transparency.
|
||||||
|
@ -83,7 +83,10 @@ import 'bottom_tab_bar.dart';
|
|||||||
class CupertinoTabScaffold extends StatefulWidget {
|
class CupertinoTabScaffold extends StatefulWidget {
|
||||||
/// Creates a layout for applications with a tab bar at the bottom.
|
/// 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({
|
const CupertinoTabScaffold({
|
||||||
Key key,
|
Key key,
|
||||||
@required this.tabBar,
|
@required this.tabBar,
|
||||||
@ -96,16 +99,20 @@ class CupertinoTabScaffold extends StatefulWidget {
|
|||||||
/// that lets the user switch between different tabs in the main content area
|
/// that lets the user switch between different tabs in the main content area
|
||||||
/// when present.
|
/// when present.
|
||||||
///
|
///
|
||||||
/// When provided, [CupertinoTabBar.currentIndex] will be ignored and will
|
/// Setting and changing [CupertinoTabBar.currentIndex] programmatically will
|
||||||
/// be managed by the [CupertinoTabScaffold] to show the currently selected page
|
/// change the currently selected tab item in the [tabBar] as well as change
|
||||||
/// as the active item index. If [CupertinoTabBar.onTap] is provided, it will
|
/// the currently focused tab from the [tabBuilder].
|
||||||
/// still be called. [CupertinoTabScaffold] automatically also listen to the
|
|
||||||
|
/// 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`
|
/// [CupertinoTabBar]'s `onTap` to change the [CupertinoTabBar]'s `currentIndex`
|
||||||
/// and change the actively displayed tab in [CupertinoTabScaffold]'s own
|
/// and change the actively displayed tab in [CupertinoTabScaffold]'s own
|
||||||
/// main content area.
|
/// main content area.
|
||||||
///
|
///
|
||||||
/// If translucent, the main content may slide behind it.
|
/// If translucent, the main content may slide behind it.
|
||||||
/// Otherwise, the main content's bottom margin will be offset by its height.
|
/// Otherwise, the main content's bottom margin will be offset by its height.
|
||||||
|
///
|
||||||
|
/// Must not be null.
|
||||||
final CupertinoTabBar tabBar;
|
final CupertinoTabBar tabBar;
|
||||||
|
|
||||||
/// An [IndexedWidgetBuilder] that's called when tabs become active.
|
/// 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
|
/// In that case, the child's [BuildContext]'s [MediaQuery] will have a
|
||||||
/// bottom padding indicating the area of obstructing overlap from the
|
/// bottom padding indicating the area of obstructing overlap from the
|
||||||
/// [tabBar].
|
/// [tabBar].
|
||||||
|
///
|
||||||
|
/// Must not be null.
|
||||||
final IndexedWidgetBuilder tabBuilder;
|
final IndexedWidgetBuilder tabBuilder;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -128,7 +137,21 @@ class CupertinoTabScaffold extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _CupertinoTabScaffoldState extends State<CupertinoTabScaffold> {
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -237,9 +237,64 @@ void main() {
|
|||||||
1,
|
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(
|
return new CupertinoTabBar(
|
||||||
items: const <BottomNavigationBarItem>[
|
items: const <BottomNavigationBarItem>[
|
||||||
const BottomNavigationBarItem(
|
const BottomNavigationBarItem(
|
||||||
@ -252,6 +307,7 @@ CupertinoTabBar _buildTabBar() {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
backgroundColor: CupertinoColors.white,
|
backgroundColor: CupertinoColors.white,
|
||||||
|
currentIndex: selectedTab,
|
||||||
onTap: (int newTab) => selectedTabs.add(newTab),
|
onTap: (int newTab) => selectedTabs.add(newTab),
|
||||||
);
|
);
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user