blob: 5ef397a3b76edcac042b920ba41f41c548c50cd4 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import '../painting/mocks_for_image_cache.dart';
import '../rendering/rendering_tester.dart';
List<int> selectedTabs;
void main() {
setUp(() {
selectedTabs = <int>[];
});
testWidgets('Tab switching', (WidgetTester tester) async {
final List<int> tabsPainted = <int>[];
await tester.pumpWidget(
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
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]);
RichText tab1 = tester.widget(find.descendant(
of: find.text('Tab 1'),
matching: find.byType(RichText),
));
expect(tab1.text.style.color, CupertinoColors.activeBlue);
RichText tab2 = tester.widget(find.descendant(
of: find.text('Tab 2'),
matching: find.byType(RichText),
));
expect(tab2.text.style.color, CupertinoColors.inactiveGray);
await tester.tap(find.text('Tab 2'));
await tester.pump();
expect(tabsPainted, <int>[0, 1]);
tab1 = tester.widget(find.descendant(
of: find.text('Tab 1'),
matching: find.byType(RichText),
));
expect(tab1.text.style.color, CupertinoColors.inactiveGray);
tab2 = tester.widget(find.descendant(
of: find.text('Tab 2'),
matching: find.byType(RichText),
));
expect(tab2.text.style.color, CupertinoColors.activeBlue);
await tester.tap(find.text('Tab 1'));
await tester.pump();
expect(tabsPainted, <int>[0, 1, 0]);
// CupertinoTabBar's onTap callbacks are passed on.
expect(selectedTabs, <int>[1, 0]);
});
testWidgets('Tabs are lazy built and moved offstage when inactive', (WidgetTester tester) async {
final List<int> tabsBuilt = <int>[];
await tester.pumpWidget(
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return new CupertinoTabScaffold(
tabBar: _buildTabBar(),
tabBuilder: (BuildContext context, int index) {
tabsBuilt.add(index);
return new Text('Page ${index + 1}');
},
);
},
);
},
),
);
expect(tabsBuilt, <int>[0]);
expect(find.text('Page 1'), findsOneWidget);
expect(find.text('Page 2'), findsNothing);
await tester.tap(find.text('Tab 2'));
await tester.pump();
// Both tabs are built but only one is onstage.
expect(tabsBuilt, <int>[0, 0, 1]);
expect(find.text('Page 1', skipOffstage: false), isOffstage);
expect(find.text('Page 2'), findsOneWidget);
await tester.tap(find.text('Tab 1'));
await tester.pump();
expect(tabsBuilt, <int>[0, 0, 1, 0, 1]);
expect(find.text('Page 1'), findsOneWidget);
expect(find.text('Page 2', skipOffstage: false), isOffstage);
});
testWidgets('Last tab gets focus', (WidgetTester tester) async {
// 2 nodes for 2 tabs
final List<FocusNode> focusNodes = <FocusNode>[new FocusNode(), new FocusNode()];
await tester.pumpWidget(
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return new Material(
child: new CupertinoTabScaffold(
tabBar: _buildTabBar(),
tabBuilder: (BuildContext context, int index) {
return new TextField(
focusNode: focusNodes[index],
autofocus: true,
);
},
),
);
},
);
},
),
);
expect(focusNodes[0].hasFocus, isTrue);
await tester.tap(find.text('Tab 2'));
await tester.pump();
expect(focusNodes[0].hasFocus, isFalse);
expect(focusNodes[1].hasFocus, isTrue);
await tester.tap(find.text('Tab 1'));
await tester.pump();
expect(focusNodes[0].hasFocus, isTrue);
expect(focusNodes[1].hasFocus, isFalse);
});
testWidgets('Do not affect focus order in the route', (WidgetTester tester) async {
final List<FocusNode> focusNodes = <FocusNode>[
new FocusNode(), new FocusNode(), new FocusNode(), new FocusNode(),
];
await tester.pumpWidget(
new WidgetsApp(
color: const Color(0xFFFFFFFF),
onGenerateRoute: (RouteSettings settings) {
return new CupertinoPageRoute<void>(
settings: settings,
builder: (BuildContext context) {
return new Material(
child: new CupertinoTabScaffold(
tabBar: _buildTabBar(),
tabBuilder: (BuildContext context, int index) {
return new Column(
children: <Widget>[
new TextField(
focusNode: focusNodes[index * 2],
decoration: const InputDecoration(
hintText: 'TextField 1',
),
),
new TextField(
focusNode: focusNodes[index * 2 + 1],
decoration: const InputDecoration(
hintText: 'TextField 2',
),
),
],
);
},
),
);
},
);
},
),
);
expect(
focusNodes.any((FocusNode node) => node.hasFocus),
isFalse,
);
await tester.tap(find.widgetWithText(TextField, 'TextField 2'));
expect(
focusNodes.indexOf(focusNodes.singleWhere((FocusNode node) => node.hasFocus)),
1,
);
await tester.tap(find.text('Tab 2'));
await tester.pump();
await tester.tap(find.widgetWithText(TextField, 'TextField 1'));
expect(
focusNodes.indexOf(focusNodes.singleWhere((FocusNode node) => node.hasFocus)),
2,
);
await tester.tap(find.text('Tab 1'));
await tester.pump();
// Upon going back to tab 1, the item it tab 1 that previously had the focus
// (TextField 2) gets it back.
expect(
focusNodes.indexOf(focusNodes.singleWhere((FocusNode node) => node.hasFocus)),
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({ int selectedTab: 0 }) {
return new CupertinoTabBar(
items: const <BottomNavigationBarItem>[
const BottomNavigationBarItem(
icon: const ImageIcon(const TestImageProvider(24, 24)),
title: const Text('Tab 1'),
),
const BottomNavigationBarItem(
icon: const ImageIcon(const TestImageProvider(24, 24)),
title: const Text('Tab 2'),
),
],
backgroundColor: CupertinoColors.white,
currentIndex: selectedTab,
onTap: (int newTab) => selectedTabs.add(newTab),
);
}