mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

Should fix #22226. Code introduced in #20890 caused a regression that broke color flooding animations in a BottomNavigationBar that has BottomNavigationBarType.shifting. The original issue (#19653) dealt with background color changes not occurring until another tab was selected. The result is that the background color instantly changes whenever the state changes and when the widget changes, instead of allowing a new widget to animate the background color change.
877 lines
28 KiB
Dart
877 lines
28 KiB
Dart
// Copyright 2016 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 'dart:io';
|
|
import 'dart:ui';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
import '../rendering/mock_canvas.dart';
|
|
import '../widgets/semantics_tester.dart';
|
|
|
|
void main() {
|
|
testWidgets('BottomNavigationBar callback test', (WidgetTester tester) async {
|
|
int mutatedIndex;
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Scaffold(
|
|
bottomNavigationBar: BottomNavigationBar(
|
|
items: const <BottomNavigationBarItem>[
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.ac_unit),
|
|
title: Text('AC')
|
|
),
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.access_alarm),
|
|
title: Text('Alarm')
|
|
)
|
|
],
|
|
onTap: (int index) {
|
|
mutatedIndex = index;
|
|
}
|
|
)
|
|
)
|
|
)
|
|
);
|
|
|
|
await tester.tap(find.text('Alarm'));
|
|
|
|
expect(mutatedIndex, 1);
|
|
});
|
|
|
|
testWidgets('BottomNavigationBar content test', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Scaffold(
|
|
bottomNavigationBar: BottomNavigationBar(
|
|
items: const <BottomNavigationBarItem>[
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.ac_unit),
|
|
title: Text('AC')
|
|
),
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.access_alarm),
|
|
title: Text('Alarm')
|
|
)
|
|
]
|
|
)
|
|
)
|
|
)
|
|
);
|
|
|
|
final RenderBox box = tester.renderObject(find.byType(BottomNavigationBar));
|
|
expect(box.size.height, kBottomNavigationBarHeight);
|
|
expect(find.text('AC'), findsOneWidget);
|
|
expect(find.text('Alarm'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('BottomNavigationBar adds bottom padding to height', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: MediaQuery(
|
|
data: const MediaQueryData(padding: EdgeInsets.only(bottom: 40.0)),
|
|
child: Scaffold(
|
|
bottomNavigationBar: BottomNavigationBar(
|
|
items: const <BottomNavigationBarItem>[
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.ac_unit),
|
|
title: Text('AC')
|
|
),
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.access_alarm),
|
|
title: Text('Alarm')
|
|
)
|
|
]
|
|
)
|
|
)
|
|
)
|
|
)
|
|
);
|
|
|
|
const double labelBottomMargin = 8.0; // _kBottomMargin in implementation.
|
|
const double additionalPadding = 40.0 - labelBottomMargin;
|
|
const double expectedHeight = kBottomNavigationBarHeight + additionalPadding;
|
|
expect(tester.getSize(find.byType(BottomNavigationBar)).height, expectedHeight);
|
|
});
|
|
|
|
testWidgets('BottomNavigationBar action size test', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Scaffold(
|
|
bottomNavigationBar: BottomNavigationBar(
|
|
type: BottomNavigationBarType.shifting,
|
|
items: const <BottomNavigationBarItem>[
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.ac_unit),
|
|
title: Text('AC')
|
|
),
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.access_alarm),
|
|
title: Text('Alarm')
|
|
)
|
|
]
|
|
)
|
|
)
|
|
)
|
|
);
|
|
|
|
Iterable<RenderBox> actions = tester.renderObjectList(find.byType(InkResponse));
|
|
expect(actions.length, 2);
|
|
expect(actions.elementAt(0).size.width, 480.0);
|
|
expect(actions.elementAt(1).size.width, 320.0);
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Scaffold(
|
|
bottomNavigationBar: BottomNavigationBar(
|
|
currentIndex: 1,
|
|
type: BottomNavigationBarType.shifting,
|
|
items: const <BottomNavigationBarItem>[
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.ac_unit),
|
|
title: Text('AC')
|
|
),
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.access_alarm),
|
|
title: Text('Alarm')
|
|
)
|
|
]
|
|
)
|
|
)
|
|
)
|
|
);
|
|
|
|
await tester.pump(const Duration(milliseconds: 200));
|
|
|
|
actions = tester.renderObjectList(find.byType(InkResponse));
|
|
expect(actions.length, 2);
|
|
expect(actions.elementAt(0).size.width, 320.0);
|
|
expect(actions.elementAt(1).size.width, 480.0);
|
|
});
|
|
|
|
testWidgets('BottomNavigationBar multiple taps test', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Scaffold(
|
|
bottomNavigationBar: BottomNavigationBar(
|
|
type: BottomNavigationBarType.shifting,
|
|
items: const <BottomNavigationBarItem>[
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.ac_unit),
|
|
title: Text('AC')
|
|
),
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.access_alarm),
|
|
title: Text('Alarm')
|
|
),
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.access_time),
|
|
title: Text('Time')
|
|
),
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.add),
|
|
title: Text('Add')
|
|
)
|
|
]
|
|
)
|
|
)
|
|
)
|
|
);
|
|
|
|
// We want to make sure that the last label does not get displaced,
|
|
// irrespective of how many taps happen on the first N - 1 labels and how
|
|
// they grow.
|
|
|
|
Iterable<RenderBox> actions = tester.renderObjectList(find.byType(InkResponse));
|
|
final Offset originalOrigin = actions.elementAt(3).localToGlobal(Offset.zero);
|
|
|
|
await tester.tap(find.text('AC'));
|
|
await tester.pump();
|
|
await tester.pump(const Duration(milliseconds: 100));
|
|
|
|
actions = tester.renderObjectList(find.byType(InkResponse));
|
|
expect(actions.elementAt(3).localToGlobal(Offset.zero), equals(originalOrigin));
|
|
|
|
await tester.tap(find.text('Alarm'));
|
|
await tester.pump();
|
|
await tester.pump(const Duration(milliseconds: 100));
|
|
|
|
actions = tester.renderObjectList(find.byType(InkResponse));
|
|
expect(actions.elementAt(3).localToGlobal(Offset.zero), equals(originalOrigin));
|
|
|
|
await tester.tap(find.text('Time'));
|
|
await tester.pump();
|
|
await tester.pump(const Duration(milliseconds: 100));
|
|
|
|
actions = tester.renderObjectList(find.byType(InkResponse));
|
|
expect(actions.elementAt(3).localToGlobal(Offset.zero), equals(originalOrigin));
|
|
});
|
|
|
|
testWidgets('BottomNavigationBar inherits shadowed app theme for shifting navbar', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(brightness: Brightness.light),
|
|
home: Theme(
|
|
data: ThemeData(brightness: Brightness.dark),
|
|
child: Scaffold(
|
|
bottomNavigationBar: BottomNavigationBar(
|
|
type: BottomNavigationBarType.shifting,
|
|
items: const <BottomNavigationBarItem>[
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.ac_unit),
|
|
title: Text('AC')
|
|
),
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.access_alarm),
|
|
title: Text('Alarm')
|
|
),
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.access_time),
|
|
title: Text('Time')
|
|
),
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.add),
|
|
title: Text('Add')
|
|
)
|
|
]
|
|
)
|
|
)
|
|
)
|
|
)
|
|
);
|
|
|
|
await tester.tap(find.text('Alarm'));
|
|
await tester.pump(const Duration(seconds: 1));
|
|
expect(Theme.of(tester.element(find.text('Alarm'))).brightness, equals(Brightness.dark));
|
|
});
|
|
|
|
testWidgets('BottomNavigationBar inherits shadowed app theme for fixed navbar', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(brightness: Brightness.light),
|
|
home: Theme(
|
|
data: ThemeData(brightness: Brightness.dark),
|
|
child: Scaffold(
|
|
bottomNavigationBar: BottomNavigationBar(
|
|
type: BottomNavigationBarType.fixed,
|
|
items: const <BottomNavigationBarItem>[
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.ac_unit),
|
|
title: Text('AC')
|
|
),
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.access_alarm),
|
|
title: Text('Alarm')
|
|
),
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.access_time),
|
|
title: Text('Time')
|
|
),
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.add),
|
|
title: Text('Add')
|
|
)
|
|
]
|
|
)
|
|
)
|
|
)
|
|
)
|
|
);
|
|
|
|
await tester.tap(find.text('Alarm'));
|
|
await tester.pump(const Duration(seconds: 1));
|
|
expect(Theme.of(tester.element(find.text('Alarm'))).brightness, equals(Brightness.dark));
|
|
});
|
|
|
|
testWidgets('BottomNavigationBar iconSize test', (WidgetTester tester) async {
|
|
double builderIconSize;
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Scaffold(
|
|
bottomNavigationBar: BottomNavigationBar(
|
|
iconSize: 12.0,
|
|
items: <BottomNavigationBarItem>[
|
|
const BottomNavigationBarItem(
|
|
title: Text('A'),
|
|
icon: Icon(Icons.ac_unit),
|
|
),
|
|
BottomNavigationBarItem(
|
|
title: const Text('B'),
|
|
icon: Builder(
|
|
builder: (BuildContext context) {
|
|
builderIconSize = IconTheme.of(context).size;
|
|
return SizedBox(
|
|
width: builderIconSize,
|
|
height: builderIconSize,
|
|
);
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final RenderBox box = tester.renderObject(find.byType(Icon));
|
|
expect(box.size.width, equals(12.0));
|
|
expect(box.size.height, equals(12.0));
|
|
expect(builderIconSize, 12.0);
|
|
});
|
|
|
|
|
|
testWidgets('BottomNavigationBar responds to textScaleFactor', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Scaffold(
|
|
bottomNavigationBar: BottomNavigationBar(
|
|
type: BottomNavigationBarType.fixed,
|
|
items: const <BottomNavigationBarItem>[
|
|
BottomNavigationBarItem(
|
|
title: Text('A'),
|
|
icon: Icon(Icons.ac_unit),
|
|
),
|
|
BottomNavigationBarItem(
|
|
title: Text('B'),
|
|
icon: Icon(Icons.battery_alert),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final RenderBox defaultBox = tester.renderObject(find.byType(BottomNavigationBar));
|
|
expect(defaultBox.size.height, equals(kBottomNavigationBarHeight));
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Scaffold(
|
|
bottomNavigationBar: BottomNavigationBar(
|
|
type: BottomNavigationBarType.shifting,
|
|
items: const <BottomNavigationBarItem>[
|
|
BottomNavigationBarItem(
|
|
title: Text('A'),
|
|
icon: Icon(Icons.ac_unit),
|
|
),
|
|
BottomNavigationBarItem(
|
|
title: Text('B'),
|
|
icon: Icon(Icons.battery_alert),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final RenderBox shiftingBox = tester.renderObject(find.byType(BottomNavigationBar));
|
|
expect(shiftingBox.size.height, equals(kBottomNavigationBarHeight));
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: MediaQuery(
|
|
data: const MediaQueryData(textScaleFactor: 2.0),
|
|
child: Scaffold(
|
|
bottomNavigationBar: BottomNavigationBar(
|
|
items: const <BottomNavigationBarItem>[
|
|
BottomNavigationBarItem(
|
|
title: Text('A'),
|
|
icon: Icon(Icons.ac_unit),
|
|
),
|
|
BottomNavigationBarItem(
|
|
title: Text('B'),
|
|
icon: Icon(Icons.battery_alert),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final RenderBox box = tester.renderObject(find.byType(BottomNavigationBar));
|
|
expect(box.size.height, equals(68.0));
|
|
});
|
|
|
|
testWidgets('BottomNavigationBar limits width of tiles with long titles', (WidgetTester tester) async {
|
|
final Text longTextA = Text(''.padLeft(100, 'A'));
|
|
final Text longTextB = Text(''.padLeft(100, 'B'));
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Scaffold(
|
|
bottomNavigationBar: BottomNavigationBar(
|
|
items: <BottomNavigationBarItem>[
|
|
BottomNavigationBarItem(
|
|
title: longTextA,
|
|
icon: const Icon(Icons.ac_unit),
|
|
),
|
|
BottomNavigationBarItem(
|
|
title: longTextB,
|
|
icon: const Icon(Icons.battery_alert),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final RenderBox box = tester.renderObject(find.byType(BottomNavigationBar));
|
|
expect(box.size.height, equals(kBottomNavigationBarHeight));
|
|
|
|
final RenderBox itemBoxA = tester.renderObject(find.text(longTextA.data));
|
|
expect(itemBoxA.size, equals(const Size(400.0, 14.0)));
|
|
final RenderBox itemBoxB = tester.renderObject(find.text(longTextB.data));
|
|
expect(itemBoxB.size, equals(const Size(400.0, 14.0)));
|
|
});
|
|
|
|
testWidgets('BottomNavigationBar paints circles', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
textDirection: TextDirection.ltr,
|
|
bottomNavigationBar: BottomNavigationBar(
|
|
items: const <BottomNavigationBarItem>[
|
|
BottomNavigationBarItem(
|
|
title: Text('A'),
|
|
icon: Icon(Icons.ac_unit),
|
|
),
|
|
BottomNavigationBarItem(
|
|
title: Text('B'),
|
|
icon: Icon(Icons.battery_alert),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
final RenderBox box = tester.renderObject(find.byType(BottomNavigationBar));
|
|
expect(box, isNot(paints..circle()));
|
|
|
|
await tester.tap(find.text('A'));
|
|
await tester.pump();
|
|
await tester.pump(const Duration(milliseconds: 20));
|
|
expect(box, paints..circle(x: 200.0));
|
|
|
|
await tester.tap(find.text('B'));
|
|
await tester.pump();
|
|
await tester.pump(const Duration(milliseconds: 20));
|
|
expect(box, paints..circle(x: 200.0)..translate(x: 400.0)..circle(x: 200.0));
|
|
|
|
// Now we flip the directionality and verify that the circles switch positions.
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
textDirection: TextDirection.rtl,
|
|
bottomNavigationBar: BottomNavigationBar(
|
|
items: const <BottomNavigationBarItem>[
|
|
BottomNavigationBarItem(
|
|
title: Text('A'),
|
|
icon: Icon(Icons.ac_unit),
|
|
),
|
|
BottomNavigationBarItem(
|
|
title: Text('B'),
|
|
icon: Icon(Icons.battery_alert),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(box, paints..translate()..save()..translate(x: 400.0)..circle(x: 200.0)..restore()..circle(x: 200.0));
|
|
|
|
await tester.tap(find.text('A'));
|
|
await tester.pump();
|
|
await tester.pump(const Duration(milliseconds: 20));
|
|
expect(
|
|
box,
|
|
paints
|
|
..translate(x: 0.0, y: 0.0)
|
|
..save()
|
|
..translate(x: 400.0)
|
|
..circle(x: 200.0)
|
|
..restore()
|
|
..circle(x: 200.0)
|
|
..translate(x: 400.0)
|
|
..circle(x: 200.0)
|
|
);
|
|
});
|
|
|
|
testWidgets('BottomNavigationBar inactiveIcon shown', (WidgetTester tester) async {
|
|
const Key filled = Key('filled');
|
|
const Key stroked = Key('stroked');
|
|
int selectedItem = 0;
|
|
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
textDirection: TextDirection.ltr,
|
|
bottomNavigationBar: BottomNavigationBar(
|
|
currentIndex: selectedItem,
|
|
items: const <BottomNavigationBarItem>[
|
|
BottomNavigationBarItem(
|
|
activeIcon: Icon(Icons.favorite, key: filled),
|
|
icon: Icon(Icons.favorite_border, key: stroked),
|
|
title: Text('Favorite'),
|
|
),
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.access_alarm),
|
|
title: Text('Alarm'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(find.byKey(filled), findsOneWidget);
|
|
expect(find.byKey(stroked), findsNothing);
|
|
selectedItem = 1;
|
|
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
textDirection: TextDirection.ltr,
|
|
bottomNavigationBar: BottomNavigationBar(
|
|
currentIndex: selectedItem,
|
|
items: const <BottomNavigationBarItem>[
|
|
BottomNavigationBarItem(
|
|
activeIcon: Icon(Icons.favorite, key: filled),
|
|
icon: Icon(Icons.favorite_border, key: stroked),
|
|
title: Text('Favorite'),
|
|
),
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.access_alarm),
|
|
title: Text('Alarm'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(find.byKey(filled), findsNothing);
|
|
expect(find.byKey(stroked), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('BottomNavigationBar.fixed semantics', (WidgetTester tester) async {
|
|
final SemanticsTester semantics = SemanticsTester(tester);
|
|
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
textDirection: TextDirection.ltr,
|
|
bottomNavigationBar: BottomNavigationBar(
|
|
items: const <BottomNavigationBarItem>[
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.ac_unit),
|
|
title: Text('AC'),
|
|
),
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.access_alarm),
|
|
title: Text('Alarm'),
|
|
),
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.hot_tub),
|
|
title: Text('Hot Tub'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
final TestSemantics expected = TestSemantics.root(
|
|
children: <TestSemantics>[
|
|
TestSemantics(
|
|
children: <TestSemantics>[
|
|
TestSemantics(
|
|
children: <TestSemantics>[
|
|
TestSemantics(
|
|
flags: <SemanticsFlag>[
|
|
SemanticsFlag.isSelected,
|
|
SemanticsFlag.isHeader,
|
|
],
|
|
actions: <SemanticsAction>[SemanticsAction.tap],
|
|
label: 'AC\nTab 1 of 3',
|
|
textDirection: TextDirection.ltr,
|
|
),
|
|
TestSemantics(
|
|
flags: <SemanticsFlag>[
|
|
SemanticsFlag.isHeader,
|
|
],
|
|
actions: <SemanticsAction>[SemanticsAction.tap],
|
|
label: 'Alarm\nTab 2 of 3',
|
|
textDirection: TextDirection.ltr,
|
|
),
|
|
TestSemantics(
|
|
flags: <SemanticsFlag>[
|
|
SemanticsFlag.isHeader,
|
|
],
|
|
actions: <SemanticsAction>[SemanticsAction.tap],
|
|
label: 'Hot Tub\nTab 3 of 3',
|
|
textDirection: TextDirection.ltr,
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
],
|
|
);
|
|
expect(semantics, hasSemantics(expected, ignoreId: true, ignoreTransform: true, ignoreRect: true));
|
|
|
|
semantics.dispose();
|
|
});
|
|
|
|
testWidgets('BottomNavigationBar.shifting semantics', (WidgetTester tester) async {
|
|
final SemanticsTester semantics = SemanticsTester(tester);
|
|
|
|
await tester.pumpWidget(
|
|
boilerplate(
|
|
textDirection: TextDirection.ltr,
|
|
bottomNavigationBar: BottomNavigationBar(
|
|
type: BottomNavigationBarType.shifting,
|
|
items: const <BottomNavigationBarItem>[
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.ac_unit),
|
|
title: Text('AC'),
|
|
),
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.access_alarm),
|
|
title: Text('Alarm'),
|
|
),
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.hot_tub),
|
|
title: Text('Hot Tub'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
final TestSemantics expected = TestSemantics.root(
|
|
children: <TestSemantics>[
|
|
TestSemantics(
|
|
children: <TestSemantics>[
|
|
TestSemantics(
|
|
children: <TestSemantics>[
|
|
TestSemantics(
|
|
flags: <SemanticsFlag>[
|
|
SemanticsFlag.isSelected,
|
|
SemanticsFlag.isHeader,
|
|
],
|
|
actions: <SemanticsAction>[SemanticsAction.tap],
|
|
label: 'AC\nTab 1 of 3',
|
|
textDirection: TextDirection.ltr,
|
|
),
|
|
TestSemantics(
|
|
flags: <SemanticsFlag>[
|
|
SemanticsFlag.isHeader,
|
|
],
|
|
actions: <SemanticsAction>[SemanticsAction.tap],
|
|
label: 'Alarm\nTab 2 of 3',
|
|
textDirection: TextDirection.ltr,
|
|
),
|
|
TestSemantics(
|
|
flags: <SemanticsFlag>[
|
|
SemanticsFlag.isHeader,
|
|
],
|
|
actions: <SemanticsAction>[SemanticsAction.tap],
|
|
label: 'Hot Tub\nTab 3 of 3',
|
|
textDirection: TextDirection.ltr,
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
],
|
|
);
|
|
expect(semantics, hasSemantics(expected, ignoreId: true, ignoreTransform: true, ignoreRect: true));
|
|
|
|
semantics.dispose();
|
|
});
|
|
|
|
testWidgets('BottomNavigationBar handles items.length changes', (WidgetTester tester) async {
|
|
// Regression test for https://github.com/flutter/flutter/issues/10322
|
|
|
|
Widget buildFrame(int itemCount) {
|
|
return MaterialApp(
|
|
home: Scaffold(
|
|
bottomNavigationBar: BottomNavigationBar(
|
|
type: BottomNavigationBarType.fixed,
|
|
currentIndex: 0,
|
|
items: List<BottomNavigationBarItem>.generate(itemCount, (int itemIndex) {
|
|
return BottomNavigationBarItem(
|
|
icon: const Icon(Icons.android),
|
|
title: Text('item $itemIndex'),
|
|
);
|
|
}),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
await tester.pumpWidget(buildFrame(3));
|
|
expect(find.text('item 0'), findsOneWidget);
|
|
expect(find.text('item 1'), findsOneWidget);
|
|
expect(find.text('item 2'), findsOneWidget);
|
|
expect(find.text('item 3'), findsNothing);
|
|
|
|
await tester.pumpWidget(buildFrame(4));
|
|
expect(find.text('item 0'), findsOneWidget);
|
|
expect(find.text('item 1'), findsOneWidget);
|
|
expect(find.text('item 2'), findsOneWidget);
|
|
expect(find.text('item 3'), findsOneWidget);
|
|
|
|
await tester.pumpWidget(buildFrame(2));
|
|
expect(find.text('item 0'), findsOneWidget);
|
|
expect(find.text('item 1'), findsOneWidget);
|
|
expect(find.text('item 2'), findsNothing);
|
|
expect(find.text('item 3'), findsNothing);
|
|
});
|
|
|
|
testWidgets('BottomNavigationBar change backgroundColor test', (WidgetTester tester) async {
|
|
// Regression test for: https://github.com/flutter/flutter/issues/19653
|
|
|
|
Color _backgroundColor = Colors.red;
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return Scaffold(
|
|
body: Center(
|
|
child: RaisedButton(
|
|
child: const Text('green'),
|
|
onPressed: () {
|
|
setState(() {
|
|
_backgroundColor = Colors.green;
|
|
});
|
|
},
|
|
),
|
|
),
|
|
bottomNavigationBar: BottomNavigationBar(
|
|
type: BottomNavigationBarType.shifting,
|
|
items: <BottomNavigationBarItem>[
|
|
BottomNavigationBarItem(
|
|
title: const Text('Page 1'),
|
|
backgroundColor: _backgroundColor,
|
|
icon: const Icon(Icons.dashboard),
|
|
),
|
|
BottomNavigationBarItem(
|
|
title: const Text('Page 2'),
|
|
backgroundColor: _backgroundColor,
|
|
icon: const Icon(Icons.menu),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder backgroundMaterial = find.descendant(
|
|
of: find.byType(BottomNavigationBar),
|
|
matching: find.byWidgetPredicate((Widget w) {
|
|
if (w is Material)
|
|
return w.type == MaterialType.canvas;
|
|
return false;
|
|
}),
|
|
);
|
|
|
|
expect(_backgroundColor, Colors.red);
|
|
expect(tester.widget<Material>(backgroundMaterial).color, Colors.red);
|
|
await tester.tap(find.text('green'));
|
|
await tester.pumpAndSettle();
|
|
expect(_backgroundColor, Colors.green);
|
|
expect(tester.widget<Material>(backgroundMaterial).color, Colors.green);
|
|
});
|
|
|
|
testWidgets('BottomNavigationBar shifting backgroundColor with transition', (WidgetTester tester) async {
|
|
// Regression test for: https://github.com/flutter/flutter/issues/22226
|
|
|
|
int _currentIndex = 0;
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return Scaffold(
|
|
bottomNavigationBar: RepaintBoundary(
|
|
child: BottomNavigationBar(
|
|
type: BottomNavigationBarType.shifting,
|
|
currentIndex: _currentIndex,
|
|
onTap: (int index) {
|
|
setState(() {
|
|
_currentIndex = index;
|
|
});
|
|
},
|
|
items: const <BottomNavigationBarItem>[
|
|
BottomNavigationBarItem(
|
|
title: Text('Red'),
|
|
backgroundColor: Colors.red,
|
|
icon: Icon(Icons.dashboard),
|
|
),
|
|
BottomNavigationBarItem(
|
|
title: Text('Green'),
|
|
backgroundColor: Colors.green,
|
|
icon: Icon(Icons.menu),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.tap(find.text('Green'));
|
|
|
|
for (int pump = 0; pump < 8; pump++) {
|
|
await tester.pump(const Duration(milliseconds: 30));
|
|
await expectLater(
|
|
find.byType(BottomNavigationBar),
|
|
matchesGoldenFile('bottom_navigation_bar.shifting_transition.$pump.png'),
|
|
skip: !Platform.isLinux,
|
|
);
|
|
}
|
|
});
|
|
|
|
testWidgets('BottomNavigationBar item title should not be nullable',
|
|
(WidgetTester tester) async {
|
|
expect(() {
|
|
MaterialApp(
|
|
home: Scaffold(
|
|
bottomNavigationBar: BottomNavigationBar(
|
|
type: BottomNavigationBarType.shifting,
|
|
items: const <BottomNavigationBarItem>[
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.ac_unit),
|
|
title: Text('AC'),
|
|
),
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.access_alarm),
|
|
)
|
|
])));
|
|
}, throwsA(isInstanceOf<AssertionError>()));
|
|
});
|
|
}
|
|
|
|
Widget boilerplate({ Widget bottomNavigationBar, @required TextDirection textDirection }) {
|
|
assert(textDirection != null);
|
|
return Localizations(
|
|
locale: const Locale('en', 'US'),
|
|
delegates: const <LocalizationsDelegate<dynamic>>[
|
|
DefaultMaterialLocalizations.delegate,
|
|
DefaultWidgetsLocalizations.delegate,
|
|
],
|
|
child: Directionality(
|
|
textDirection: textDirection,
|
|
child: MediaQuery(
|
|
data: const MediaQueryData(),
|
|
child: Material(
|
|
child: Scaffold(
|
|
bottomNavigationBar: bottomNavigationBar,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|