341 lines
10 KiB
Dart
341 lines
10 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// @dart = 2.8
|
|
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
import '../../image_data.dart';
|
|
|
|
List<int> selectedTabs;
|
|
|
|
void main() {
|
|
setUp(() {
|
|
selectedTabs = <int>[];
|
|
});
|
|
|
|
testWidgets('Last tab gets focus', (WidgetTester tester) async {
|
|
// 2 nodes for 2 tabs
|
|
final List<FocusNode> focusNodes = <FocusNode>[FocusNode(), FocusNode()];
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Material(
|
|
child: CupertinoTabScaffold(
|
|
tabBar: _buildTabBar(),
|
|
tabBuilder: (BuildContext context, int index) {
|
|
return 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>[
|
|
FocusNode(), FocusNode(), FocusNode(), FocusNode(),
|
|
];
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Material(
|
|
child: CupertinoTabScaffold(
|
|
tabBar: _buildTabBar(),
|
|
tabBuilder: (BuildContext context, int index) {
|
|
return Column(
|
|
children: <Widget>[
|
|
TextField(
|
|
focusNode: focusNodes[index * 2],
|
|
decoration: const InputDecoration(
|
|
hintText: 'TextField 1',
|
|
),
|
|
),
|
|
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('Tab bar respects themes', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
CupertinoApp(
|
|
home: CupertinoTabScaffold(
|
|
tabBar: _buildTabBar(),
|
|
tabBuilder: (BuildContext context, int index) {
|
|
return const Placeholder();
|
|
},
|
|
),
|
|
),
|
|
);
|
|
|
|
BoxDecoration tabDecoration = tester.widget<DecoratedBox>(find.descendant(
|
|
of: find.byType(CupertinoTabBar),
|
|
matching: find.byType(DecoratedBox),
|
|
)).decoration as BoxDecoration;
|
|
|
|
expect(tabDecoration.color, isSameColorAs(const Color(0xF0F9F9F9))); // Inherited from theme.
|
|
|
|
await tester.tap(find.text('Tab 2'));
|
|
await tester.pump();
|
|
|
|
// Pump again but with dark theme.
|
|
await tester.pumpWidget(
|
|
CupertinoApp(
|
|
theme: const CupertinoThemeData(
|
|
brightness: Brightness.dark,
|
|
primaryColor: CupertinoColors.destructiveRed,
|
|
),
|
|
home: CupertinoTabScaffold(
|
|
tabBar: _buildTabBar(),
|
|
tabBuilder: (BuildContext context, int index) {
|
|
return const Placeholder();
|
|
},
|
|
),
|
|
),
|
|
);
|
|
|
|
tabDecoration = tester.widget<DecoratedBox>(find.descendant(
|
|
of: find.byType(CupertinoTabBar),
|
|
matching: find.byType(DecoratedBox),
|
|
)).decoration as BoxDecoration;
|
|
|
|
expect(tabDecoration.color, isSameColorAs(const Color(0xF01D1D1D)));
|
|
|
|
final RichText tab1 = tester.widget(find.descendant(
|
|
of: find.text('Tab 1'),
|
|
matching: find.byType(RichText),
|
|
));
|
|
// Tab 2 should still be selected after changing theme.
|
|
expect(tab1.text.style.color.value, 0xFF757575);
|
|
final RichText tab2 = tester.widget(find.descendant(
|
|
of: find.text('Tab 2'),
|
|
matching: find.byType(RichText),
|
|
));
|
|
expect(tab2.text.style.color.value, CupertinoColors.systemRed.darkColor.value);
|
|
});
|
|
|
|
testWidgets('dark mode background color', (WidgetTester tester) async {
|
|
const CupertinoDynamicColor backgroundColor = CupertinoDynamicColor.withBrightness(
|
|
color: Color(0xFF123456),
|
|
darkColor: Color(0xFF654321),
|
|
);
|
|
await tester.pumpWidget(
|
|
CupertinoApp(
|
|
theme: const CupertinoThemeData(brightness: Brightness.light),
|
|
home: CupertinoTabScaffold(
|
|
backgroundColor: backgroundColor,
|
|
tabBar: _buildTabBar(),
|
|
tabBuilder: (BuildContext context, int index) {
|
|
return const Placeholder();
|
|
},
|
|
),
|
|
),
|
|
);
|
|
|
|
// The DecoratedBox with the smallest depth is the DecoratedBox of the
|
|
// CupertinoTabScaffold.
|
|
BoxDecoration tabDecoration = tester.firstWidget<DecoratedBox>(
|
|
find.descendant(
|
|
of: find.byType(CupertinoTabScaffold),
|
|
matching: find.byType(DecoratedBox),
|
|
)
|
|
).decoration as BoxDecoration;
|
|
|
|
expect(tabDecoration.color.value, backgroundColor.color.value);
|
|
|
|
// Dark mode
|
|
await tester.pumpWidget(
|
|
CupertinoApp(
|
|
theme: const CupertinoThemeData(brightness: Brightness.dark),
|
|
home: CupertinoTabScaffold(
|
|
backgroundColor: backgroundColor,
|
|
tabBar: _buildTabBar(),
|
|
tabBuilder: (BuildContext context, int index) {
|
|
return const Placeholder();
|
|
},
|
|
),
|
|
),
|
|
);
|
|
|
|
tabDecoration = tester.firstWidget<DecoratedBox>(
|
|
find.descendant(
|
|
of: find.byType(CupertinoTabScaffold),
|
|
matching: find.byType(DecoratedBox),
|
|
)
|
|
).decoration as BoxDecoration;
|
|
|
|
expect(tabDecoration.color.value, backgroundColor.darkColor.value);
|
|
});
|
|
|
|
testWidgets('Does not lose state when focusing on text input', (WidgetTester tester) async {
|
|
// Regression testing for https://github.com/flutter/flutter/issues/28457.
|
|
|
|
await tester.pumpWidget(
|
|
MediaQuery(
|
|
data: const MediaQueryData(
|
|
viewInsets: EdgeInsets.only(bottom: 0),
|
|
),
|
|
child: MaterialApp(
|
|
home: Material(
|
|
child: CupertinoTabScaffold(
|
|
tabBar: _buildTabBar(),
|
|
tabBuilder: (BuildContext context, int index) {
|
|
return const TextField();
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final EditableTextState editableState = tester.state<EditableTextState>(find.byType(EditableText));
|
|
|
|
await tester.enterText(find.byType(TextField), "don't lose me");
|
|
|
|
await tester.pumpWidget(
|
|
MediaQuery(
|
|
data: const MediaQueryData(
|
|
viewInsets: EdgeInsets.only(bottom: 100),
|
|
),
|
|
child: MaterialApp(
|
|
home: Material(
|
|
child: CupertinoTabScaffold(
|
|
tabBar: _buildTabBar(),
|
|
tabBuilder: (BuildContext context, int index) {
|
|
return const TextField();
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
// The exact same state instance is still there.
|
|
expect(tester.state<EditableTextState>(find.byType(EditableText)), editableState);
|
|
expect(find.text("don't lose me"), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('textScaleFactor is set to 1.0', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Builder(builder: (BuildContext context) {
|
|
return MediaQuery(
|
|
data: MediaQuery.of(context).copyWith(textScaleFactor: 99),
|
|
child: CupertinoTabScaffold(
|
|
tabBar: CupertinoTabBar(
|
|
items: List<BottomNavigationBarItem>.generate(
|
|
10,
|
|
(int i) => BottomNavigationBarItem(icon: ImageIcon(MemoryImage(Uint8List.fromList(kTransparentImage))), title: Text('$i')),
|
|
),
|
|
),
|
|
tabBuilder: (BuildContext context, int index) => const Text('content'),
|
|
),
|
|
);
|
|
}),
|
|
),
|
|
);
|
|
|
|
final Iterable<RichText> barItems = tester.widgetList<RichText>(
|
|
find.descendant(
|
|
of: find.byType(CupertinoTabBar),
|
|
matching: find.byType(RichText),
|
|
),
|
|
);
|
|
|
|
final Iterable<RichText> contents = tester.widgetList<RichText>(
|
|
find.descendant(
|
|
of: find.text('content'),
|
|
matching: find.byType(RichText),
|
|
skipOffstage: false,
|
|
),
|
|
);
|
|
|
|
expect(barItems.length, greaterThan(0));
|
|
expect(barItems.any((RichText t) => t.textScaleFactor != 1), isFalse);
|
|
|
|
expect(contents.length, greaterThan(0));
|
|
expect(contents.any((RichText t) => t.textScaleFactor != 99), isFalse);
|
|
});
|
|
}
|
|
|
|
CupertinoTabBar _buildTabBar({ int selectedTab = 0 }) {
|
|
return CupertinoTabBar(
|
|
items: <BottomNavigationBarItem>[
|
|
BottomNavigationBarItem(
|
|
icon: ImageIcon(MemoryImage(Uint8List.fromList(kTransparentImage))),
|
|
title: const Text('Tab 1'),
|
|
),
|
|
BottomNavigationBarItem(
|
|
icon: ImageIcon(MemoryImage(Uint8List.fromList(kTransparentImage))),
|
|
title: const Text('Tab 2'),
|
|
),
|
|
],
|
|
currentIndex: selectedTab,
|
|
onTap: (int newTab) => selectedTabs.add(newTab),
|
|
);
|
|
}
|