2011 lines
69 KiB
Dart
2011 lines
69 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.
|
|
|
|
import 'dart:ui';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
import '../rendering/mock_canvas.dart';
|
|
|
|
void main() {
|
|
testWidgets('SearchBar defaults', (WidgetTester tester) async {
|
|
final ThemeData theme = ThemeData(useMaterial3: true);
|
|
final ColorScheme colorScheme = theme.colorScheme;
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: theme,
|
|
home: const Material(
|
|
child: SearchBar(
|
|
hintText: 'hint text',
|
|
)
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder searchBarMaterial = find.descendant(
|
|
of: find.byType(SearchBar),
|
|
matching: find.byType(Material),
|
|
);
|
|
|
|
final Material material = tester.widget<Material>(searchBarMaterial);
|
|
checkSearchBarDefaults(tester, colorScheme, material);
|
|
});
|
|
|
|
testWidgets('SearchBar respects controller property', (WidgetTester tester) async {
|
|
const String defaultText = 'default text';
|
|
final TextEditingController controller = TextEditingController(text: defaultText);
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Material(
|
|
child: SearchBar(
|
|
controller: controller,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(controller.value.text, defaultText);
|
|
expect(find.text(defaultText), findsOneWidget);
|
|
|
|
const String updatedText = 'updated text';
|
|
await tester.enterText(find.byType(SearchBar), updatedText);
|
|
expect(controller.value.text, updatedText);
|
|
expect(find.text(defaultText), findsNothing);
|
|
expect(find.text(updatedText), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('SearchBar respects focusNode property', (WidgetTester tester) async {
|
|
final FocusNode node = FocusNode();
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Material(
|
|
child: SearchBar(
|
|
focusNode: node,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(node.hasFocus, false);
|
|
|
|
node.requestFocus();
|
|
await tester.pump();
|
|
expect(node.hasFocus, true);
|
|
|
|
node.unfocus();
|
|
await tester.pump();
|
|
expect(node.hasFocus, false);
|
|
});
|
|
|
|
testWidgets('SearchBar has correct default layout and padding LTR', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Center(
|
|
child: SearchBar(
|
|
leading: IconButton(
|
|
icon: const Icon(Icons.search),
|
|
onPressed: () {},
|
|
),
|
|
trailing: <Widget>[
|
|
IconButton(
|
|
icon: const Icon(Icons.menu),
|
|
onPressed: () {},
|
|
)
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Rect barRect = tester.getRect(find.byType(SearchBar));
|
|
expect(barRect.size, const Size(800.0, 56.0));
|
|
expect(barRect, equals(const Rect.fromLTRB(0.0, 272.0, 800.0, 328.0)));
|
|
|
|
final Rect leadingIcon = tester.getRect(find.widgetWithIcon(IconButton, Icons.search));
|
|
// Default left padding is 8.0, and icon button has 8.0 padding, so in total the padding between
|
|
// the edge of the bar and the icon of the button is 16.0, which matches the spec.
|
|
expect(leadingIcon.left, equals(barRect.left + 8.0));
|
|
|
|
final Rect textField = tester.getRect(find.byType(TextField));
|
|
expect(textField.left, equals(leadingIcon.right + 8.0));
|
|
|
|
final Rect trailingIcon = tester.getRect(find.widgetWithIcon(IconButton, Icons.menu));
|
|
expect(trailingIcon.left, equals(textField.right + 8.0));
|
|
expect(trailingIcon.right, equals(barRect.right - 8.0));
|
|
});
|
|
|
|
testWidgets('SearchBar has correct default layout and padding - RTL', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Directionality(
|
|
textDirection: TextDirection.rtl,
|
|
child: Center(
|
|
child: SearchBar(
|
|
leading: IconButton(
|
|
icon: const Icon(Icons.search),
|
|
onPressed: () {},
|
|
),
|
|
trailing: <Widget>[
|
|
IconButton(
|
|
icon: const Icon(Icons.menu),
|
|
onPressed: () {},
|
|
)
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Rect barRect = tester.getRect(find.byType(SearchBar));
|
|
expect(barRect.size, const Size(800.0, 56.0));
|
|
expect(barRect, equals(const Rect.fromLTRB(0.0, 272.0, 800.0, 328.0)));
|
|
|
|
// The default padding is set to 8.0 so the distance between the icon of the button
|
|
// and the edge of the bar is 16.0, which matches the spec.
|
|
final Rect leadingIcon = tester.getRect(find.widgetWithIcon(IconButton, Icons.search));
|
|
expect(leadingIcon.right, equals(barRect.right - 8.0));
|
|
|
|
final Rect textField = tester.getRect(find.byType(TextField));
|
|
expect(textField.right, equals(leadingIcon.left - 8.0));
|
|
|
|
final Rect trailingIcon = tester.getRect(find.widgetWithIcon(IconButton, Icons.menu));
|
|
expect(trailingIcon.right, equals(textField.left - 8.0));
|
|
expect(trailingIcon.left, equals(barRect.left + 8.0));
|
|
});
|
|
|
|
testWidgets('SearchBar respects hintText property', (WidgetTester tester) async {
|
|
const String hintText = 'hint text';
|
|
await tester.pumpWidget(
|
|
const MaterialApp(
|
|
home: Material(
|
|
child: SearchBar(
|
|
hintText: hintText,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(find.text(hintText), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('SearchBar respects leading property', (WidgetTester tester) async {
|
|
final ThemeData theme = ThemeData();
|
|
final ColorScheme colorScheme = theme.colorScheme;
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Material(
|
|
child: SearchBar(
|
|
leading: IconButton(
|
|
icon: const Icon(Icons.search),
|
|
onPressed: () {},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(find.widgetWithIcon(IconButton, Icons.search), findsOneWidget);
|
|
final Color? iconColor = _iconStyle(tester, Icons.search)?.color;
|
|
expect(iconColor, colorScheme.onSurface); // Default icon color.
|
|
});
|
|
|
|
testWidgets('SearchBar respects trailing property', (WidgetTester tester) async {
|
|
final ThemeData theme = ThemeData();
|
|
final ColorScheme colorScheme = theme.colorScheme;
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Material(
|
|
child: SearchBar(
|
|
trailing: <Widget>[
|
|
IconButton(
|
|
icon: const Icon(Icons.menu),
|
|
onPressed: () {},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(find.widgetWithIcon(IconButton, Icons.menu), findsOneWidget);
|
|
final Color? iconColor = _iconStyle(tester, Icons.menu)?.color;
|
|
expect(iconColor, colorScheme.onSurfaceVariant); // Default icon color.
|
|
});
|
|
|
|
testWidgets('SearchBar respects onTap property', (WidgetTester tester) async {
|
|
int tapCount = 0;
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return Material(
|
|
child: SearchBar(
|
|
onTap: () {
|
|
setState(() {
|
|
tapCount++;
|
|
});
|
|
}
|
|
),
|
|
);
|
|
}
|
|
),
|
|
),
|
|
);
|
|
expect(tapCount, 0);
|
|
await tester.tap(find.byType(SearchBar));
|
|
expect(tapCount, 1);
|
|
await tester.tap(find.byType(SearchBar));
|
|
expect(tapCount, 2);
|
|
});
|
|
|
|
testWidgets('SearchBar respects onChanged property', (WidgetTester tester) async {
|
|
int changeCount = 0;
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return Material(
|
|
child: SearchBar(
|
|
onChanged: (_) {
|
|
setState(() {
|
|
changeCount++;
|
|
});
|
|
}
|
|
),
|
|
);
|
|
}
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(changeCount, 0);
|
|
await tester.enterText(find.byType(SearchBar), 'a');
|
|
expect(changeCount, 1);
|
|
await tester.enterText(find.byType(SearchBar), 'b');
|
|
expect(changeCount, 2);
|
|
});
|
|
|
|
testWidgets('SearchBar respects onSubmitted property', (WidgetTester tester) async {
|
|
String submittedQuery = '';
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Material(
|
|
child: SearchBar(
|
|
onSubmitted: (String text) {
|
|
submittedQuery = text;
|
|
},
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.enterText(find.byType(SearchBar), 'query');
|
|
await tester.testTextInput.receiveAction(TextInputAction.done);
|
|
|
|
expect(submittedQuery, equals('query'));
|
|
});
|
|
|
|
testWidgets('SearchBar respects constraints property', (WidgetTester tester) async {
|
|
const BoxConstraints constraints = BoxConstraints(maxWidth: 350.0, minHeight: 80);
|
|
await tester.pumpWidget(
|
|
const MaterialApp(
|
|
home: Center(
|
|
child: Material(
|
|
child: SearchBar(
|
|
constraints: constraints,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Rect barRect = tester.getRect(find.byType(SearchBar));
|
|
expect(barRect.size, const Size(350.0, 80.0));
|
|
});
|
|
|
|
testWidgets('SearchBar respects elevation property', (WidgetTester tester) async {
|
|
const double pressedElevation = 0.0;
|
|
const double hoveredElevation = 1.0;
|
|
const double focusedElevation = 2.0;
|
|
const double defaultElevation = 3.0;
|
|
double getElevation(Set<MaterialState> states) {
|
|
if (states.contains(MaterialState.pressed)) {
|
|
return pressedElevation;
|
|
}
|
|
if (states.contains(MaterialState.hovered)) {
|
|
return hoveredElevation;
|
|
}
|
|
if (states.contains(MaterialState.focused)) {
|
|
return focusedElevation;
|
|
}
|
|
return defaultElevation;
|
|
}
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Center(
|
|
child: Material(
|
|
child: SearchBar(
|
|
elevation: MaterialStateProperty.resolveWith<double>(getElevation),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder searchBarMaterial = find.descendant(
|
|
of: find.byType(SearchBar),
|
|
matching: find.byType(Material),
|
|
);
|
|
Material material = tester.widget<Material>(searchBarMaterial);
|
|
|
|
// On hovered.
|
|
final TestGesture gesture = await _pointGestureToSearchBar(tester);
|
|
await tester.pump();
|
|
material = tester.widget<Material>(searchBarMaterial);
|
|
expect(material.elevation, hoveredElevation);
|
|
|
|
// On pressed.
|
|
await gesture.down(tester.getCenter(find.byType(SearchBar)));
|
|
await tester.pump();
|
|
await gesture.removePointer();
|
|
|
|
material = tester.widget<Material>(searchBarMaterial);
|
|
expect(material.elevation, pressedElevation);
|
|
|
|
// On focused.
|
|
await tester.tap(find.byType(SearchBar));
|
|
await tester.pump();
|
|
material = tester.widget<Material>(searchBarMaterial);
|
|
expect(material.elevation, focusedElevation);
|
|
});
|
|
|
|
testWidgets('SearchBar respects backgroundColor property', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Center(
|
|
child: Material(
|
|
child: SearchBar(
|
|
backgroundColor: MaterialStateProperty.resolveWith<Color>(_getColor),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder searchBarMaterial = find.descendant(
|
|
of: find.byType(SearchBar),
|
|
matching: find.byType(Material),
|
|
);
|
|
Material material = tester.widget<Material>(searchBarMaterial);
|
|
|
|
// On hovered.
|
|
final TestGesture gesture = await _pointGestureToSearchBar(tester);
|
|
await tester.pump();
|
|
material = tester.widget<Material>(searchBarMaterial);
|
|
expect(material.color, hoveredColor);
|
|
|
|
// On pressed.
|
|
await gesture.down(tester.getCenter(find.byType(SearchBar)));
|
|
await tester.pump();
|
|
await gesture.removePointer();
|
|
|
|
material = tester.widget<Material>(searchBarMaterial);
|
|
expect(material.color, pressedColor);
|
|
|
|
// On focused.
|
|
await tester.tap(find.byType(SearchBar));
|
|
await tester.pump();
|
|
material = tester.widget<Material>(searchBarMaterial);
|
|
expect(material.color, focusedColor);
|
|
});
|
|
|
|
testWidgets('SearchBar respects shadowColor property', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Center(
|
|
child: Material(
|
|
child: SearchBar(
|
|
shadowColor: MaterialStateProperty.resolveWith<Color>(_getColor),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder searchBarMaterial = find.descendant(
|
|
of: find.byType(SearchBar),
|
|
matching: find.byType(Material),
|
|
);
|
|
Material material = tester.widget<Material>(searchBarMaterial);
|
|
|
|
// On hovered.
|
|
final TestGesture gesture = await _pointGestureToSearchBar(tester);
|
|
await tester.pump();
|
|
material = tester.widget<Material>(searchBarMaterial);
|
|
expect(material.shadowColor, hoveredColor);
|
|
|
|
// On pressed.
|
|
await gesture.down(tester.getCenter(find.byType(SearchBar)));
|
|
await tester.pump();
|
|
await gesture.removePointer();
|
|
|
|
material = tester.widget<Material>(searchBarMaterial);
|
|
expect(material.shadowColor, pressedColor);
|
|
|
|
// On focused.
|
|
await tester.tap(find.byType(SearchBar));
|
|
await tester.pump();
|
|
material = tester.widget<Material>(searchBarMaterial);
|
|
expect(material.shadowColor, focusedColor);
|
|
});
|
|
|
|
testWidgets('SearchBar respects surfaceTintColor property', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Center(
|
|
child: Material(
|
|
child: SearchBar(
|
|
surfaceTintColor: MaterialStateProperty.resolveWith<Color>(_getColor),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder searchBarMaterial = find.descendant(
|
|
of: find.byType(SearchBar),
|
|
matching: find.byType(Material),
|
|
);
|
|
Material material = tester.widget<Material>(searchBarMaterial);
|
|
|
|
// On hovered.
|
|
final TestGesture gesture = await _pointGestureToSearchBar(tester);
|
|
await tester.pump();
|
|
material = tester.widget<Material>(searchBarMaterial);
|
|
expect(material.surfaceTintColor, hoveredColor);
|
|
|
|
// On pressed.
|
|
await gesture.down(tester.getCenter(find.byType(SearchBar)));
|
|
await tester.pump();
|
|
await gesture.removePointer();
|
|
|
|
material = tester.widget<Material>(searchBarMaterial);
|
|
expect(material.surfaceTintColor, pressedColor);
|
|
|
|
// On focused.
|
|
await tester.tap(find.byType(SearchBar));
|
|
await tester.pump();
|
|
material = tester.widget<Material>(searchBarMaterial);
|
|
expect(material.surfaceTintColor, focusedColor);
|
|
});
|
|
|
|
testWidgets('SearchBar respects overlayColor property', (WidgetTester tester) async {
|
|
final FocusNode focusNode = FocusNode();
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Center(
|
|
child: Material(
|
|
child: SearchBar(
|
|
focusNode: focusNode,
|
|
overlayColor: MaterialStateProperty.resolveWith<Color>(_getColor),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
|
|
|
|
// On hovered.
|
|
final TestGesture gesture = await _pointGestureToSearchBar(tester);
|
|
await tester.pumpAndSettle();
|
|
expect(inkFeatures, paints..rect(color: hoveredColor.withOpacity(1.0)));
|
|
|
|
// On pressed.
|
|
await tester.pumpAndSettle();
|
|
await tester.startGesture(tester.getCenter(find.byType(SearchBar)));
|
|
await tester.pumpAndSettle();
|
|
inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
|
|
expect(inkFeatures, paints..rect()..rect(color: pressedColor.withOpacity(1.0)));
|
|
await gesture.removePointer();
|
|
|
|
// On focused.
|
|
await tester.pumpAndSettle();
|
|
focusNode.requestFocus();
|
|
await tester.pumpAndSettle();
|
|
inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
|
|
expect(inkFeatures, paints..rect()..rect(color: focusedColor.withOpacity(1.0)));
|
|
});
|
|
|
|
testWidgets('SearchBar respects side and shape properties', (WidgetTester tester) async {
|
|
const BorderSide pressedSide = BorderSide(width: 2.0);
|
|
const BorderSide hoveredSide = BorderSide(width: 3.0);
|
|
const BorderSide focusedSide = BorderSide(width: 4.0);
|
|
const BorderSide defaultSide = BorderSide(width: 5.0);
|
|
|
|
const OutlinedBorder pressedShape = RoundedRectangleBorder();
|
|
const OutlinedBorder hoveredShape = ContinuousRectangleBorder();
|
|
const OutlinedBorder focusedShape = CircleBorder();
|
|
const OutlinedBorder defaultShape = StadiumBorder();
|
|
BorderSide getSide(Set<MaterialState> states) {
|
|
if (states.contains(MaterialState.pressed)) {
|
|
return pressedSide;
|
|
}
|
|
if (states.contains(MaterialState.hovered)) {
|
|
return hoveredSide;
|
|
}
|
|
if (states.contains(MaterialState.focused)) {
|
|
return focusedSide;
|
|
}
|
|
return defaultSide;
|
|
}
|
|
OutlinedBorder getShape(Set<MaterialState> states) {
|
|
if (states.contains(MaterialState.pressed)) {
|
|
return pressedShape;
|
|
}
|
|
if (states.contains(MaterialState.hovered)) {
|
|
return hoveredShape;
|
|
}
|
|
if (states.contains(MaterialState.focused)) {
|
|
return focusedShape;
|
|
}
|
|
return defaultShape;
|
|
}
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Center(
|
|
child: Material(
|
|
child: SearchBar(
|
|
side: MaterialStateProperty.resolveWith<BorderSide>(getSide),
|
|
shape: MaterialStateProperty.resolveWith<OutlinedBorder>(getShape),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder searchBarMaterial = find.descendant(
|
|
of: find.byType(SearchBar),
|
|
matching: find.byType(Material),
|
|
);
|
|
Material material = tester.widget<Material>(searchBarMaterial);
|
|
|
|
// On hovered.
|
|
final TestGesture gesture = await _pointGestureToSearchBar(tester);
|
|
await tester.pump();
|
|
material = tester.widget<Material>(searchBarMaterial);
|
|
expect(material.shape, hoveredShape.copyWith(side: hoveredSide));
|
|
|
|
// On pressed.
|
|
await gesture.down(tester.getCenter(find.byType(SearchBar)));
|
|
await tester.pump();
|
|
await gesture.removePointer();
|
|
|
|
material = tester.widget<Material>(searchBarMaterial);
|
|
expect(material.shape, pressedShape.copyWith(side: pressedSide));
|
|
|
|
// On focused.
|
|
await tester.tap(find.byType(SearchBar));
|
|
await tester.pump();
|
|
material = tester.widget<Material>(searchBarMaterial);
|
|
expect(material.shape, focusedShape.copyWith(side: focusedSide));
|
|
});
|
|
|
|
testWidgets('SearchBar respects padding property', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
const MaterialApp(
|
|
home: Center(
|
|
child: Material(
|
|
child: SearchBar(
|
|
leading: Icon(Icons.search),
|
|
padding: MaterialStatePropertyAll<EdgeInsets>(EdgeInsets.all(16.0)),
|
|
trailing: <Widget>[
|
|
Icon(Icons.menu),
|
|
]
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Rect barRect = tester.getRect(find.byType(SearchBar));
|
|
final Rect leadingRect = tester.getRect(find.byIcon(Icons.search));
|
|
final Rect textFieldRect = tester.getRect(find.byType(TextField));
|
|
final Rect trailingRect = tester.getRect(find.byIcon(Icons.menu));
|
|
|
|
expect(barRect.left, leadingRect.left - 16.0);
|
|
expect(leadingRect.right, textFieldRect.left - 16.0);
|
|
expect(textFieldRect.right, trailingRect.left - 16.0);
|
|
expect(trailingRect.right, barRect.right - 16.0);
|
|
});
|
|
|
|
testWidgets('SearchBar respects hintStyle property', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Center(
|
|
child: Material(
|
|
child: SearchBar(
|
|
hintText: 'hint text',
|
|
hintStyle: MaterialStateProperty.resolveWith<TextStyle?>(_getTextStyle),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
// On hovered.
|
|
final TestGesture gesture = await _pointGestureToSearchBar(tester);
|
|
await tester.pump();
|
|
Text helperText = tester.widget(find.text('hint text'));
|
|
expect(helperText.style?.color, hoveredColor);
|
|
|
|
// On pressed.
|
|
await gesture.down(tester.getCenter(find.byType(SearchBar)));
|
|
await tester.pump();
|
|
helperText = tester.widget(find.text('hint text'));
|
|
expect(helperText.style?.color, pressedColor);
|
|
await gesture.removePointer();
|
|
|
|
// On focused.
|
|
await tester.tap(find.byType(SearchBar));
|
|
await tester.pump();
|
|
helperText = tester.widget(find.text('hint text'));
|
|
expect(helperText.style?.color, focusedColor);
|
|
});
|
|
|
|
testWidgets('SearchBar respects textStyle property', (WidgetTester tester) async {
|
|
final TextEditingController controller = TextEditingController(text: 'input text');
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Center(
|
|
child: Material(
|
|
child: SearchBar(
|
|
controller: controller,
|
|
textStyle: MaterialStateProperty.resolveWith<TextStyle?>(_getTextStyle),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
// On hovered.
|
|
final TestGesture gesture = await _pointGestureToSearchBar(tester);
|
|
await tester.pump();
|
|
EditableText inputText = tester.widget(find.text('input text'));
|
|
expect(inputText.style.color, hoveredColor);
|
|
|
|
// On pressed.
|
|
await gesture.down(tester.getCenter(find.byType(SearchBar)));
|
|
await tester.pump();
|
|
await gesture.removePointer();
|
|
inputText = tester.widget(find.text('input text'));
|
|
expect(inputText.style.color, pressedColor);
|
|
|
|
// On focused.
|
|
await tester.tap(find.byType(SearchBar));
|
|
await tester.pump();
|
|
inputText = tester.widget(find.text('input text'));
|
|
expect(inputText.style.color, focusedColor);
|
|
});
|
|
|
|
testWidgets('hintStyle can override textStyle for hintText', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Center(
|
|
child: Material(
|
|
child: SearchBar(
|
|
hintText: 'hint text',
|
|
hintStyle: MaterialStateProperty.resolveWith<TextStyle?>(_getTextStyle),
|
|
textStyle: const MaterialStatePropertyAll<TextStyle>(TextStyle(color: Colors.pink)),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
// On hovered.
|
|
final TestGesture gesture = await _pointGestureToSearchBar(tester);
|
|
await tester.pump();
|
|
Text helperText = tester.widget(find.text('hint text'));
|
|
expect(helperText.style?.color, hoveredColor);
|
|
|
|
// On pressed.
|
|
await gesture.down(tester.getCenter(find.byType(SearchBar)));
|
|
await tester.pump();
|
|
await gesture.removePointer();
|
|
helperText = tester.widget(find.text('hint text'));
|
|
expect(helperText.style?.color, pressedColor);
|
|
|
|
// On focused.
|
|
await tester.tap(find.byType(SearchBar));
|
|
await tester.pump();
|
|
helperText = tester.widget(find.text('hint text'));
|
|
expect(helperText.style?.color, focusedColor);
|
|
});
|
|
|
|
// Regression test for https://github.com/flutter/flutter/issues/127092.
|
|
testWidgets('The text is still centered when SearchBar text field is smaller than 48', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(useMaterial3: true),
|
|
home: const Center(
|
|
child: Material(
|
|
child: SearchBar(
|
|
constraints: BoxConstraints.tightFor(height: 35.0),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.enterText(find.byType(TextField), 'input text');
|
|
final Finder textContent = find.text('input text');
|
|
final double textCenterY = tester.getCenter(textContent).dy;
|
|
final Finder searchBar = find.byType(SearchBar);
|
|
final double searchBarCenterY = tester.getCenter(searchBar).dy;
|
|
expect(textCenterY, searchBarCenterY);
|
|
});
|
|
|
|
testWidgets('The search view defaults', (WidgetTester tester) async {
|
|
final ThemeData theme = ThemeData(useMaterial3: true);
|
|
final ColorScheme colorScheme = theme.colorScheme;
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: theme,
|
|
home: Scaffold(
|
|
body: Material(
|
|
child: Align(
|
|
alignment: Alignment.topLeft,
|
|
child: SearchAnchor(
|
|
viewHintText: 'hint text',
|
|
builder: (BuildContext context, SearchController controller) {
|
|
return const Icon(Icons.search);
|
|
},
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[];
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.tap(find.byIcon(Icons.search));
|
|
await tester.pumpAndSettle();
|
|
final Material material = getSearchViewMaterial(tester);
|
|
expect(material.elevation, 6.0);
|
|
expect(material.color, colorScheme.surface);
|
|
expect(material.surfaceTintColor, colorScheme.surfaceTint);
|
|
expect(material.clipBehavior, Clip.antiAlias);
|
|
|
|
final Finder findDivider = find.byType(Divider);
|
|
final Container dividerContainer = tester.widget<Container>(find.descendant(of: findDivider, matching: find.byType(Container)).first);
|
|
final BoxDecoration decoration = dividerContainer.decoration! as BoxDecoration;
|
|
expect(decoration.border!.bottom.color, colorScheme.outline);
|
|
|
|
// Default search view has a leading back button on the start of the header.
|
|
expect(find.widgetWithIcon(IconButton, Icons.arrow_back), findsOneWidget);
|
|
|
|
// Default search view has a trailing close button on the end of the header.
|
|
// It is used to clear the input in the text field.
|
|
expect(find.widgetWithIcon(IconButton, Icons.close), findsOneWidget);
|
|
|
|
final Text helperText = tester.widget(find.text('hint text'));
|
|
expect(helperText.style?.color, colorScheme.onSurfaceVariant);
|
|
expect(helperText.style?.fontSize, 16.0);
|
|
expect(helperText.style?.fontFamily, 'Roboto');
|
|
expect(helperText.style?.fontWeight, FontWeight.w400);
|
|
|
|
const String input = 'entered text';
|
|
await tester.enterText(find.byType(SearchBar), input);
|
|
final EditableText inputText = tester.widget(find.text(input));
|
|
expect(inputText.style.color, colorScheme.onSurface);
|
|
expect(inputText.style.fontSize, 16.0);
|
|
expect(inputText.style.fontFamily, 'Roboto');
|
|
expect(inputText.style.fontWeight, FontWeight.w400);
|
|
});
|
|
|
|
testWidgets('The search view default size on different platforms', (WidgetTester tester) async {
|
|
// The search view should be is full-screen on mobile platforms,
|
|
// and have a size of (360, 2/3 screen height) on other platforms
|
|
Widget buildSearchAnchor(TargetPlatform platform) {
|
|
return MaterialApp(
|
|
theme: ThemeData(platform: platform),
|
|
home: Scaffold(
|
|
body: SafeArea(
|
|
child: Material(
|
|
child: Align(
|
|
alignment: Alignment.topLeft,
|
|
child: SearchAnchor(
|
|
builder: (BuildContext context, SearchController controller) {
|
|
return const Icon(Icons.search);
|
|
},
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[];
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
for (final TargetPlatform platform in <TargetPlatform>[ TargetPlatform.iOS, TargetPlatform.android, TargetPlatform.fuchsia ]) {
|
|
await tester.pumpWidget(Container());
|
|
await tester.pumpWidget(buildSearchAnchor(platform));
|
|
await tester.tap(find.byIcon(Icons.search));
|
|
await tester.pumpAndSettle();
|
|
final SizedBox sizedBox = tester.widget<SizedBox>(find.descendant(of: findViewContent(), matching: find.byType(SizedBox)).first);
|
|
expect(sizedBox.width, 800.0);
|
|
expect(sizedBox.height, 600.0);
|
|
}
|
|
|
|
for (final TargetPlatform platform in <TargetPlatform>[ TargetPlatform.linux, TargetPlatform.windows ]) {
|
|
await tester.pumpWidget(Container());
|
|
await tester.pumpWidget(buildSearchAnchor(platform));
|
|
await tester.tap(find.byIcon(Icons.search));
|
|
await tester.pumpAndSettle();
|
|
final SizedBox sizedBox = tester.widget<SizedBox>(find.descendant(of: findViewContent(), matching: find.byType(SizedBox)).first);
|
|
expect(sizedBox.width, 360.0);
|
|
expect(sizedBox.height, 400.0);
|
|
}
|
|
});
|
|
|
|
testWidgets('SearchAnchor respects isFullScreen property', (WidgetTester tester) async {
|
|
Widget buildSearchAnchor(TargetPlatform platform) {
|
|
return MaterialApp(
|
|
theme: ThemeData(platform: platform),
|
|
home: Scaffold(
|
|
body: SafeArea(
|
|
child: Material(
|
|
child: Align(
|
|
alignment: Alignment.topLeft,
|
|
child: SearchAnchor(
|
|
isFullScreen: true,
|
|
builder: (BuildContext context, SearchController controller) {
|
|
return const Icon(Icons.search);
|
|
},
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[];
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
for (final TargetPlatform platform in <TargetPlatform>[ TargetPlatform.linux, TargetPlatform.windows ]) {
|
|
await tester.pumpWidget(Container());
|
|
await tester.pumpWidget(buildSearchAnchor(platform));
|
|
await tester.tap(find.byIcon(Icons.search));
|
|
await tester.pumpAndSettle();
|
|
final SizedBox sizedBox = tester.widget<SizedBox>(find.descendant(of: findViewContent(), matching: find.byType(SizedBox)).first);
|
|
expect(sizedBox.width, 800.0);
|
|
expect(sizedBox.height, 600.0);
|
|
}
|
|
});
|
|
|
|
testWidgets('SearchAnchor respects controller property', (WidgetTester tester) async {
|
|
const String defaultText = 'initial text';
|
|
final SearchController controller = SearchController();
|
|
controller.text = defaultText;
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Material(
|
|
child: SearchAnchor(
|
|
searchController: controller,
|
|
builder: (BuildContext context, SearchController controller) {
|
|
return IconButton(icon: const Icon(Icons.search), onPressed: () {
|
|
controller.openView();
|
|
},);
|
|
},
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[];
|
|
},
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.tap(find.widgetWithIcon(IconButton, Icons.search));
|
|
await tester.pumpAndSettle();
|
|
expect(controller.value.text, defaultText);
|
|
expect(find.text(defaultText), findsOneWidget);
|
|
|
|
const String updatedText = 'updated text';
|
|
await tester.enterText(find.byType(SearchBar), updatedText);
|
|
expect(controller.value.text, updatedText);
|
|
expect(find.text(defaultText), findsNothing);
|
|
expect(find.text(updatedText), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('SearchAnchor respects viewBuilder property', (WidgetTester tester) async {
|
|
Widget buildAnchor({ViewBuilder? viewBuilder}) {
|
|
return MaterialApp(
|
|
home: Material(
|
|
child: SearchAnchor(
|
|
viewBuilder: viewBuilder,
|
|
builder: (BuildContext context, SearchController controller) {
|
|
return IconButton(icon: const Icon(Icons.search), onPressed: () {
|
|
controller.openView();
|
|
},);
|
|
},
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[];
|
|
},
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
await tester.pumpWidget(buildAnchor());
|
|
await tester.tap(find.widgetWithIcon(IconButton, Icons.search));
|
|
await tester.pumpAndSettle();
|
|
// Default is a ListView.
|
|
expect(find.byType(ListView), findsOneWidget);
|
|
|
|
await tester.pumpWidget(Container());
|
|
await tester.pumpWidget(buildAnchor(viewBuilder: (Iterable<Widget> suggestions)
|
|
=> GridView.count(crossAxisCount: 5, children: suggestions.toList(),)
|
|
));
|
|
await tester.tap(find.widgetWithIcon(IconButton, Icons.search));
|
|
await tester.pumpAndSettle();
|
|
expect(find.byType(ListView), findsNothing);
|
|
expect(find.byType(GridView), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('SearchAnchor respects viewLeading property', (WidgetTester tester) async {
|
|
Widget buildAnchor({Widget? viewLeading}) {
|
|
return MaterialApp(
|
|
home: Material(
|
|
child: SearchAnchor(
|
|
viewLeading: viewLeading,
|
|
builder: (BuildContext context, SearchController controller) {
|
|
return IconButton(icon: const Icon(Icons.search), onPressed: () {
|
|
controller.openView();
|
|
},);
|
|
},
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[];
|
|
},
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
await tester.pumpWidget(buildAnchor());
|
|
await tester.tap(find.widgetWithIcon(IconButton, Icons.search));
|
|
await tester.pumpAndSettle();
|
|
// Default is a icon button with arrow_back.
|
|
expect(find.widgetWithIcon(IconButton, Icons.arrow_back), findsOneWidget);
|
|
|
|
await tester.pumpWidget(Container());
|
|
await tester.pumpWidget(buildAnchor(viewLeading: const Icon(Icons.history)));
|
|
await tester.tap(find.widgetWithIcon(IconButton, Icons.search));
|
|
await tester.pumpAndSettle();
|
|
expect(find.byIcon(Icons.arrow_back), findsNothing);
|
|
expect(find.byIcon(Icons.history), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('SearchAnchor respects viewTrailing property', (WidgetTester tester) async {
|
|
Widget buildAnchor({Iterable<Widget>? viewTrailing}) {
|
|
return MaterialApp(
|
|
home: Material(
|
|
child: SearchAnchor(
|
|
viewTrailing: viewTrailing,
|
|
builder: (BuildContext context, SearchController controller) {
|
|
return IconButton(icon: const Icon(Icons.search), onPressed: () {
|
|
controller.openView();
|
|
},);
|
|
},
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[];
|
|
},
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
await tester.pumpWidget(buildAnchor());
|
|
await tester.tap(find.widgetWithIcon(IconButton, Icons.search));
|
|
await tester.pumpAndSettle();
|
|
// Default is a icon button with close icon.
|
|
expect(find.widgetWithIcon(IconButton, Icons.close), findsOneWidget);
|
|
|
|
await tester.pumpWidget(Container());
|
|
await tester.pumpWidget(buildAnchor(viewTrailing: <Widget>[const Icon(Icons.history)]));
|
|
await tester.tap(find.widgetWithIcon(IconButton, Icons.search));
|
|
await tester.pumpAndSettle();
|
|
expect(find.byIcon(Icons.close), findsNothing);
|
|
expect(find.byIcon(Icons.history), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('SearchAnchor respects viewHintText property', (WidgetTester tester) async {
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Material(
|
|
child: SearchAnchor(
|
|
viewHintText: 'hint text',
|
|
builder: (BuildContext context, SearchController controller) {
|
|
return IconButton(icon: const Icon(Icons.search), onPressed: () {
|
|
controller.openView();
|
|
},);
|
|
},
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[];
|
|
},
|
|
),
|
|
),
|
|
));
|
|
await tester.tap(find.widgetWithIcon(IconButton, Icons.search));
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('hint text'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('SearchAnchor respects viewBackgroundColor property', (WidgetTester tester) async {
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Material(
|
|
child: SearchAnchor(
|
|
viewBackgroundColor: Colors.purple,
|
|
builder: (BuildContext context, SearchController controller) {
|
|
return IconButton(icon: const Icon(Icons.search), onPressed: () {
|
|
controller.openView();
|
|
},);
|
|
},
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[];
|
|
},
|
|
),
|
|
),
|
|
));
|
|
|
|
await tester.tap(find.widgetWithIcon(IconButton, Icons.search));
|
|
await tester.pumpAndSettle();
|
|
expect(getSearchViewMaterial(tester).color, Colors.purple);
|
|
});
|
|
|
|
testWidgets('SearchAnchor respects viewElevation property', (WidgetTester tester) async {
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Material(
|
|
child: SearchAnchor(
|
|
viewElevation: 3.0,
|
|
builder: (BuildContext context, SearchController controller) {
|
|
return IconButton(icon: const Icon(Icons.search), onPressed: () {
|
|
controller.openView();
|
|
},);
|
|
},
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[];
|
|
},
|
|
),
|
|
),
|
|
));
|
|
|
|
await tester.tap(find.widgetWithIcon(IconButton, Icons.search));
|
|
await tester.pumpAndSettle();
|
|
expect(getSearchViewMaterial(tester).elevation, 3.0);
|
|
});
|
|
|
|
testWidgets('SearchAnchor respects viewSurfaceTint property', (WidgetTester tester) async {
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Material(
|
|
child: SearchAnchor(
|
|
viewSurfaceTintColor: Colors.purple,
|
|
builder: (BuildContext context, SearchController controller) {
|
|
return IconButton(icon: const Icon(Icons.search), onPressed: () {
|
|
controller.openView();
|
|
},);
|
|
},
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[];
|
|
},
|
|
),
|
|
),
|
|
));
|
|
|
|
await tester.tap(find.widgetWithIcon(IconButton, Icons.search));
|
|
await tester.pumpAndSettle();
|
|
expect(getSearchViewMaterial(tester).surfaceTintColor, Colors.purple);
|
|
});
|
|
|
|
testWidgets('SearchAnchor respects viewSide property', (WidgetTester tester) async {
|
|
const BorderSide side = BorderSide(color: Colors.purple, width: 5.0);
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Material(
|
|
child: SearchAnchor(
|
|
isFullScreen: false,
|
|
viewSide: side,
|
|
builder: (BuildContext context, SearchController controller) {
|
|
return IconButton(icon: const Icon(Icons.search), onPressed: () {
|
|
controller.openView();
|
|
},);
|
|
},
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[];
|
|
},
|
|
),
|
|
),
|
|
));
|
|
|
|
await tester.tap(find.widgetWithIcon(IconButton, Icons.search));
|
|
await tester.pumpAndSettle();
|
|
expect(getSearchViewMaterial(tester).shape, RoundedRectangleBorder(side: side, borderRadius: BorderRadius.circular(28.0)));
|
|
});
|
|
|
|
testWidgets('SearchAnchor respects viewShape property', (WidgetTester tester) async {
|
|
const BorderSide side = BorderSide(color: Colors.purple, width: 5.0);
|
|
const OutlinedBorder shape = StadiumBorder(side: side);
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Material(
|
|
child: SearchAnchor(
|
|
isFullScreen: false,
|
|
viewShape: shape,
|
|
builder: (BuildContext context, SearchController controller) {
|
|
return IconButton(icon: const Icon(Icons.search), onPressed: () {
|
|
controller.openView();
|
|
},);
|
|
},
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[];
|
|
},
|
|
),
|
|
),
|
|
));
|
|
|
|
await tester.tap(find.widgetWithIcon(IconButton, Icons.search));
|
|
await tester.pumpAndSettle();
|
|
expect(getSearchViewMaterial(tester).shape, shape);
|
|
});
|
|
|
|
testWidgets('SearchAnchor respects headerTextStyle property', (WidgetTester tester) async {
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Material(
|
|
child: SearchAnchor(
|
|
headerTextStyle: theme.textTheme.bodyLarge?.copyWith(color: Colors.red),
|
|
builder: (BuildContext context, SearchController controller) {
|
|
return IconButton(icon: const Icon(Icons.search), onPressed: () {
|
|
controller.openView();
|
|
},);
|
|
},
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[];
|
|
},
|
|
),
|
|
),
|
|
));
|
|
|
|
await tester.tap(find.widgetWithIcon(IconButton, Icons.search));
|
|
await tester.pumpAndSettle();
|
|
await tester.enterText(find.byType(SearchBar), 'input text');
|
|
await tester.pumpAndSettle();
|
|
|
|
final EditableText inputText = tester.widget(find.text('input text'));
|
|
expect(inputText.style.color, Colors.red);
|
|
});
|
|
|
|
testWidgets('SearchAnchor respects headerHintStyle property', (WidgetTester tester) async {
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Material(
|
|
child: SearchAnchor(
|
|
viewHintText: 'hint text',
|
|
headerHintStyle: theme.textTheme.bodyLarge?.copyWith(color: Colors.orange),
|
|
builder: (BuildContext context, SearchController controller) {
|
|
return IconButton(icon: const Icon(Icons.search), onPressed: () {
|
|
controller.openView();
|
|
},);
|
|
},
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[];
|
|
},
|
|
),
|
|
),
|
|
));
|
|
|
|
await tester.tap(find.widgetWithIcon(IconButton, Icons.search));
|
|
await tester.pumpAndSettle();
|
|
|
|
final Text inputText = tester.widget(find.text('hint text'));
|
|
expect(inputText.style?.color, Colors.orange);
|
|
});
|
|
|
|
testWidgets('SearchAnchor respects dividerColor property', (WidgetTester tester) async {
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Material(
|
|
child: SearchAnchor(
|
|
dividerColor: Colors.red,
|
|
builder: (BuildContext context, SearchController controller) {
|
|
return IconButton(icon: const Icon(Icons.search), onPressed: () {
|
|
controller.openView();
|
|
},);
|
|
},
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[];
|
|
},
|
|
),
|
|
),
|
|
));
|
|
|
|
await tester.tap(find.widgetWithIcon(IconButton, Icons.search));
|
|
await tester.pumpAndSettle();
|
|
|
|
final Finder findDivider = find.byType(Divider);
|
|
final Container dividerContainer = tester.widget<Container>(find.descendant(of: findDivider, matching: find.byType(Container)).first);
|
|
final BoxDecoration decoration = dividerContainer.decoration! as BoxDecoration;
|
|
expect(decoration.border!.bottom.color, Colors.red);
|
|
});
|
|
|
|
testWidgets('SearchAnchor respects viewConstraints property', (WidgetTester tester) async {
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Material(
|
|
child: Center(
|
|
child: SearchAnchor(
|
|
isFullScreen: false,
|
|
viewConstraints: BoxConstraints.tight(const Size(280.0, 390.0)),
|
|
builder: (BuildContext context, SearchController controller) {
|
|
return IconButton(icon: const Icon(Icons.search), onPressed: () {
|
|
controller.openView();
|
|
},);
|
|
},
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[];
|
|
},
|
|
),
|
|
),
|
|
),
|
|
));
|
|
|
|
await tester.tap(find.widgetWithIcon(IconButton, Icons.search));
|
|
await tester.pumpAndSettle();
|
|
|
|
final SizedBox sizedBox = tester.widget<SizedBox>(find.descendant(of: findViewContent(), matching: find.byType(SizedBox)).first);
|
|
expect(sizedBox.width, 280.0);
|
|
expect(sizedBox.height, 390.0);
|
|
});
|
|
|
|
testWidgets('SearchAnchor respects builder property - LTR', (WidgetTester tester) async {
|
|
Widget buildAnchor({required SearchAnchorChildBuilder builder}) {
|
|
return MaterialApp(
|
|
home: Material(
|
|
child: Align(
|
|
alignment: Alignment.topCenter,
|
|
child: SearchAnchor(
|
|
isFullScreen: false,
|
|
builder: builder,
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[];
|
|
},
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
await tester.pumpWidget(buildAnchor(
|
|
builder: (BuildContext context, SearchController controller)
|
|
=> const Icon(Icons.search)
|
|
));
|
|
final Rect anchorRect = tester.getRect(find.byIcon(Icons.search));
|
|
expect(anchorRect.size, const Size(24.0, 24.0));
|
|
expect(anchorRect, equals(const Rect.fromLTRB(388.0, 0.0, 412.0, 24.0)));
|
|
|
|
await tester.tap(find.byIcon(Icons.search));
|
|
await tester.pumpAndSettle();
|
|
|
|
final Rect searchViewRect = tester.getRect(find.descendant(of: findViewContent(), matching: find.byType(SizedBox)).first);
|
|
expect(searchViewRect, equals(const Rect.fromLTRB(388.0, 0.0, 748.0, 400.0)));
|
|
|
|
// Search view top left should be the same as the anchor top left
|
|
expect(searchViewRect.topLeft, anchorRect.topLeft);
|
|
});
|
|
|
|
testWidgets('SearchAnchor respects builder property - RTL', (WidgetTester tester) async {
|
|
Widget buildAnchor({required SearchAnchorChildBuilder builder}) {
|
|
return MaterialApp(
|
|
home: Directionality(
|
|
textDirection: TextDirection.rtl,
|
|
child: Material(
|
|
child: Align(
|
|
alignment: Alignment.topCenter,
|
|
child: SearchAnchor(
|
|
isFullScreen: false,
|
|
builder: builder,
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[];
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
await tester.pumpWidget(buildAnchor(builder: (BuildContext context, SearchController controller)
|
|
=> const Icon(Icons.search)));
|
|
final Rect anchorRect = tester.getRect(find.byIcon(Icons.search));
|
|
expect(anchorRect.size, const Size(24.0, 24.0));
|
|
expect(anchorRect, equals(const Rect.fromLTRB(388.0, 0.0, 412.0, 24.0)));
|
|
|
|
await tester.tap(find.byIcon(Icons.search));
|
|
await tester.pumpAndSettle();
|
|
|
|
final Rect searchViewRect = tester.getRect(find.descendant(of: findViewContent(), matching: find.byType(SizedBox)).first);
|
|
expect(searchViewRect, equals(const Rect.fromLTRB(52.0, 0.0, 412.0, 400.0)));
|
|
|
|
// Search view top right should be the same as the anchor top right
|
|
expect(searchViewRect.topRight, anchorRect.topRight);
|
|
});
|
|
|
|
testWidgets('SearchAnchor respects suggestionsBuilder property', (WidgetTester tester) async {
|
|
final SearchController controller = SearchController();
|
|
const String suggestion = 'suggestion text';
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return Material(
|
|
child: Align(
|
|
alignment: Alignment.topCenter,
|
|
child: SearchAnchor(
|
|
searchController: controller,
|
|
builder: (BuildContext context, SearchController controller) {
|
|
return const Icon(Icons.search);
|
|
},
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[
|
|
ListTile(
|
|
title: const Text(suggestion),
|
|
onTap: () {
|
|
setState(() {
|
|
controller.closeView(suggestion);
|
|
});
|
|
}),
|
|
];
|
|
},
|
|
),
|
|
),
|
|
);
|
|
}
|
|
),
|
|
));
|
|
await tester.tap(find.byIcon(Icons.search));
|
|
await tester.pumpAndSettle();
|
|
|
|
final Finder listTile = find.widgetWithText(ListTile, suggestion);
|
|
expect(listTile, findsOneWidget);
|
|
await tester.tap(listTile);
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(controller.isOpen, false);
|
|
expect(controller.value.text, suggestion);
|
|
});
|
|
|
|
testWidgets('SearchAnchor suggestionsBuilder property could be async', (WidgetTester tester) async {
|
|
final SearchController controller = SearchController();
|
|
const String suggestion = 'suggestion text';
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: StatefulBuilder(
|
|
builder: (BuildContext context, StateSetter setState) {
|
|
return Material(
|
|
child: Align(
|
|
alignment: Alignment.topCenter,
|
|
child: SearchAnchor(
|
|
searchController: controller,
|
|
builder: (BuildContext context, SearchController controller) {
|
|
return const Icon(Icons.search);
|
|
},
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) async {
|
|
return <Widget>[
|
|
ListTile(
|
|
title: const Text(suggestion),
|
|
onTap: () {
|
|
setState(() {
|
|
controller.closeView(suggestion);
|
|
});
|
|
},
|
|
),
|
|
];
|
|
},
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
));
|
|
await tester.tap(find.byIcon(Icons.search));
|
|
await tester.pumpAndSettle();
|
|
|
|
final Finder text = find.text(suggestion);
|
|
expect(text, findsOneWidget);
|
|
await tester.tap(text);
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(controller.isOpen, false);
|
|
expect(controller.value.text, suggestion);
|
|
});
|
|
|
|
testWidgets('SearchAnchor.bar has a default search bar as the anchor', (WidgetTester tester) async {
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Material(
|
|
child: Align(
|
|
alignment: Alignment.topLeft,
|
|
child: SearchAnchor.bar(
|
|
isFullScreen: false,
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[];
|
|
},
|
|
),
|
|
),
|
|
),),
|
|
);
|
|
|
|
expect(find.byType(SearchBar), findsOneWidget);
|
|
final Rect anchorRect = tester.getRect(find.byType(SearchBar));
|
|
expect(anchorRect.size, const Size(800.0, 56.0));
|
|
expect(anchorRect, equals(const Rect.fromLTRB(0.0, 0.0, 800.0, 56.0)));
|
|
|
|
await tester.tap(find.byIcon(Icons.search));
|
|
await tester.pumpAndSettle();
|
|
|
|
final Rect searchViewRect = tester.getRect(find.descendant(of: findViewContent(), matching: find.byType(SizedBox)).first);
|
|
expect(searchViewRect, equals(const Rect.fromLTRB(0.0, 0.0, 800.0, 400.0)));
|
|
|
|
// Search view has same width with the default anchor(search bar).
|
|
expect(searchViewRect.width, anchorRect.width);
|
|
});
|
|
|
|
testWidgets('SearchController can open/close view', (WidgetTester tester) async {
|
|
final SearchController controller = SearchController();
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Material(
|
|
child: SearchAnchor.bar(
|
|
searchController: controller,
|
|
isFullScreen: false,
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[
|
|
ListTile(
|
|
title: const Text('item 0'),
|
|
onTap: () {
|
|
controller.closeView('item 0');
|
|
},
|
|
)
|
|
];
|
|
},
|
|
),
|
|
),),
|
|
);
|
|
|
|
expect(controller.isOpen, false);
|
|
await tester.tap(find.byType(SearchBar));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(controller.isOpen, true);
|
|
await tester.tap(find.widgetWithText(ListTile, 'item 0'));
|
|
await tester.pumpAndSettle();
|
|
expect(controller.isOpen, false);
|
|
controller.openView();
|
|
expect(controller.isOpen, true);
|
|
});
|
|
|
|
testWidgets('Search view does not go off the screen - LTR', (WidgetTester tester) async {
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Material(
|
|
child: Align(
|
|
// Put the search anchor on the bottom-right corner of the screen to test
|
|
// if the search view goes off the window.
|
|
alignment: Alignment.bottomRight,
|
|
child: SearchAnchor(
|
|
isFullScreen: false,
|
|
builder: (BuildContext context, SearchController controller) {
|
|
return IconButton(
|
|
icon: const Icon(Icons.search),
|
|
onPressed: () {
|
|
controller.openView();
|
|
},
|
|
);
|
|
},
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[];
|
|
},
|
|
),
|
|
),
|
|
),),
|
|
);
|
|
|
|
final Finder findIconButton = find.widgetWithIcon(IconButton, Icons.search);
|
|
final Rect iconButton = tester.getRect(findIconButton);
|
|
// Icon button has a size of (48.0, 48.0) and the screen size is (800.0, 600.0).
|
|
expect(iconButton, equals(const Rect.fromLTRB(752.0, 552.0, 800.0, 600.0)));
|
|
|
|
await tester.tap(find.byIcon(Icons.search));
|
|
await tester.pumpAndSettle();
|
|
|
|
final Rect searchViewRect = tester.getRect(find.descendant(of: findViewContent(), matching: find.byType(SizedBox)).first);
|
|
expect(searchViewRect, equals(const Rect.fromLTRB(440.0, 200.0, 800.0, 600.0)));
|
|
});
|
|
|
|
testWidgets('Search view does not go off the screen - RTL', (WidgetTester tester) async {
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Directionality(
|
|
textDirection: TextDirection.rtl,
|
|
child: Material(
|
|
child: Align(
|
|
// Put the search anchor on the bottom-left corner of the screen to test
|
|
// if the search view goes off the window when the text direction is right-to-left.
|
|
alignment: Alignment.bottomLeft,
|
|
child: SearchAnchor(
|
|
isFullScreen: false,
|
|
builder: (BuildContext context, SearchController controller) {
|
|
return IconButton(
|
|
icon: const Icon(Icons.search),
|
|
onPressed: () {
|
|
controller.openView();
|
|
},
|
|
);
|
|
},
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[];
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),),
|
|
);
|
|
|
|
final Finder findIconButton = find.widgetWithIcon(IconButton, Icons.search);
|
|
final Rect iconButton = tester.getRect(findIconButton);
|
|
expect(iconButton, equals(const Rect.fromLTRB(0.0, 552.0, 48.0, 600.0)));
|
|
|
|
await tester.tap(find.byIcon(Icons.search));
|
|
await tester.pumpAndSettle();
|
|
|
|
final Rect searchViewRect = tester.getRect(find.descendant(of: findViewContent(), matching: find.byType(SizedBox)).first);
|
|
expect(searchViewRect, equals(const Rect.fromLTRB(0.0, 200.0, 360.0, 600.0)));
|
|
});
|
|
|
|
testWidgets('Search view becomes smaller if the window size is smaller than the view size', (WidgetTester tester) async {
|
|
addTearDown(tester.view.reset);
|
|
tester.view.physicalSize = const Size(200.0, 200.0);
|
|
tester.view.devicePixelRatio = 1.0;
|
|
|
|
Widget buildSearchAnchor({TextDirection textDirection = TextDirection.ltr}) {
|
|
return MaterialApp(
|
|
home: Directionality(
|
|
textDirection: textDirection,
|
|
child: Material(
|
|
child: SearchAnchor(
|
|
isFullScreen: false,
|
|
builder: (BuildContext context, SearchController controller) {
|
|
return Align(
|
|
alignment: Alignment.bottomRight,
|
|
child: IconButton(
|
|
icon: const Icon(Icons.search),
|
|
onPressed: () {
|
|
controller.openView();
|
|
},
|
|
),
|
|
);
|
|
},
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[];
|
|
},
|
|
),
|
|
),
|
|
),);
|
|
}
|
|
|
|
// Test LTR text direction.
|
|
await tester.pumpWidget(buildSearchAnchor());
|
|
|
|
final Finder findIconButton = find.widgetWithIcon(IconButton, Icons.search);
|
|
final Rect iconButton = tester.getRect(findIconButton);
|
|
// The icon button size is (48.0, 48.0), and the screen size is (200.0, 200.0)
|
|
expect(iconButton, equals(const Rect.fromLTRB(152.0, 152.0, 200.0, 200.0)));
|
|
|
|
await tester.tap(find.byIcon(Icons.search));
|
|
await tester.pumpAndSettle();
|
|
|
|
final Rect searchViewRect = tester.getRect(find.descendant(of: findViewContent(), matching: find.byType(SizedBox)).first);
|
|
expect(searchViewRect, equals(const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0)));
|
|
|
|
// Test RTL text direction.
|
|
await tester.pumpWidget(Container());
|
|
await tester.pumpWidget(buildSearchAnchor(textDirection: TextDirection.rtl));
|
|
|
|
final Finder findIconButtonRTL = find.widgetWithIcon(IconButton, Icons.search);
|
|
final Rect iconButtonRTL = tester.getRect(findIconButtonRTL);
|
|
// The icon button size is (48.0, 48.0), and the screen size is (200.0, 200.0)
|
|
expect(iconButtonRTL, equals(const Rect.fromLTRB(152.0, 152.0, 200.0, 200.0)));
|
|
|
|
await tester.tap(find.byIcon(Icons.search));
|
|
await tester.pumpAndSettle();
|
|
|
|
final Rect searchViewRectRTL = tester.getRect(find.descendant(of: findViewContent(), matching: find.byType(SizedBox)).first);
|
|
expect(searchViewRectRTL, equals(const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0)));
|
|
});
|
|
|
|
testWidgets('Docked search view route is popped if the window size changes', (WidgetTester tester) async {
|
|
addTearDown(tester.view.reset);
|
|
tester.view.physicalSize = const Size(500.0, 600.0);
|
|
tester.view.devicePixelRatio = 1.0;
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Material(
|
|
child: SearchAnchor(
|
|
isFullScreen: false,
|
|
builder: (BuildContext context, SearchController controller) {
|
|
return Align(
|
|
alignment: Alignment.bottomRight,
|
|
child: IconButton(
|
|
icon: const Icon(Icons.search),
|
|
onPressed: () {
|
|
controller.openView();
|
|
},
|
|
),
|
|
);
|
|
},
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[];
|
|
},
|
|
),
|
|
),
|
|
));
|
|
|
|
// Open the search view
|
|
await tester.tap(find.byIcon(Icons.search));
|
|
await tester.pumpAndSettle();
|
|
expect(find.byIcon(Icons.arrow_back), findsOneWidget);
|
|
|
|
// Change window size
|
|
tester.view.physicalSize = const Size(250.0, 200.0);
|
|
tester.view.devicePixelRatio = 1.0;
|
|
await tester.pumpAndSettle();
|
|
expect(find.byIcon(Icons.arrow_back), findsNothing);
|
|
});
|
|
|
|
testWidgets('Full-screen search view route should stay if the window size changes', (WidgetTester tester) async {
|
|
addTearDown(tester.view.reset);
|
|
tester.view.physicalSize = const Size(500.0, 600.0);
|
|
tester.view.devicePixelRatio = 1.0;
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Material(
|
|
child: SearchAnchor(
|
|
isFullScreen: true,
|
|
builder: (BuildContext context, SearchController controller) {
|
|
return Align(
|
|
alignment: Alignment.bottomRight,
|
|
child: IconButton(
|
|
icon: const Icon(Icons.search),
|
|
onPressed: () {
|
|
controller.openView();
|
|
},
|
|
),
|
|
);
|
|
},
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[];
|
|
},
|
|
),
|
|
),
|
|
));
|
|
|
|
// Open a full-screen search view
|
|
await tester.tap(find.byIcon(Icons.search));
|
|
await tester.pumpAndSettle();
|
|
expect(find.byIcon(Icons.arrow_back), findsOneWidget);
|
|
|
|
// Change window size
|
|
tester.view.physicalSize = const Size(250.0, 200.0);
|
|
tester.view.devicePixelRatio = 1.0;
|
|
await tester.pumpAndSettle();
|
|
expect(find.byIcon(Icons.arrow_back), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('Search view route does not throw exception during pop animation', (WidgetTester tester) async {
|
|
// Regression test for https://github.com/flutter/flutter/issues/126590.
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Material(
|
|
child: Center(
|
|
child: SearchAnchor(
|
|
builder: (BuildContext context, SearchController controller) {
|
|
return IconButton(
|
|
icon: const Icon(Icons.search),
|
|
onPressed: () {
|
|
controller.openView();
|
|
},
|
|
);
|
|
},
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return List<Widget>.generate(5, (int index) {
|
|
final String item = 'item $index';
|
|
return ListTile(
|
|
leading: const Icon(Icons.history),
|
|
title: Text(item),
|
|
trailing: const Icon(Icons.chevron_right),
|
|
onTap: () {},
|
|
);
|
|
});
|
|
}),
|
|
),
|
|
),
|
|
));
|
|
|
|
// Open search view
|
|
await tester.tap(find.byIcon(Icons.search));
|
|
await tester.pumpAndSettle();
|
|
|
|
// Pop search view route
|
|
await tester.tap(find.byIcon(Icons.arrow_back));
|
|
await tester.pumpAndSettle();
|
|
|
|
// No exception.
|
|
});
|
|
|
|
testWidgets('Docked search should position itself correctly based on closest navigator', (WidgetTester tester) async {
|
|
const double rootSpacing = 100.0;
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
builder: (BuildContext context, Widget? child) {
|
|
return Scaffold(
|
|
body: Padding(
|
|
padding: const EdgeInsets.all(rootSpacing),
|
|
child: child,
|
|
),
|
|
);
|
|
},
|
|
home: Material(
|
|
child: SearchAnchor(
|
|
isFullScreen: false,
|
|
builder: (BuildContext context, SearchController controller) {
|
|
return IconButton(
|
|
icon: const Icon(Icons.search),
|
|
onPressed: () {
|
|
controller.openView();
|
|
},
|
|
);
|
|
},
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[];
|
|
},
|
|
),
|
|
),
|
|
));
|
|
|
|
await tester.tap(find.byIcon(Icons.search));
|
|
await tester.pumpAndSettle();
|
|
|
|
final Rect searchViewRect = tester.getRect(find.descendant(of: findViewContent(), matching: find.byType(SizedBox)).first);
|
|
expect(searchViewRect.topLeft, equals(const Offset(rootSpacing, rootSpacing)));
|
|
});
|
|
|
|
|
|
// Regression tests for https://github.com/flutter/flutter/issues/126623
|
|
group('Overall InputDecorationTheme does not impact SearchBar and SearchView', () {
|
|
|
|
const InputDecorationTheme inputDecorationTheme = InputDecorationTheme(
|
|
focusColor: Colors.green,
|
|
hoverColor: Colors.blue,
|
|
outlineBorder: BorderSide(color: Colors.pink, width: 10),
|
|
isDense: true,
|
|
contentPadding: EdgeInsets.symmetric(horizontal: 20),
|
|
hintStyle: TextStyle(color: Colors.purpleAccent),
|
|
fillColor: Colors.tealAccent,
|
|
filled: true,
|
|
isCollapsed: true,
|
|
border: OutlineInputBorder(),
|
|
focusedBorder: UnderlineInputBorder(),
|
|
enabledBorder: UnderlineInputBorder(),
|
|
errorBorder: UnderlineInputBorder(),
|
|
focusedErrorBorder: UnderlineInputBorder(),
|
|
disabledBorder: UnderlineInputBorder(),
|
|
constraints: BoxConstraints(maxWidth: 300),
|
|
);
|
|
final ThemeData theme = ThemeData(
|
|
useMaterial3: true,
|
|
inputDecorationTheme: inputDecorationTheme
|
|
);
|
|
|
|
void checkDecorationInSearchBar(WidgetTester tester) {
|
|
final Finder textField = findTextField();
|
|
final InputDecoration? decoration = tester.widget<TextField>(textField).decoration;
|
|
|
|
expect(decoration?.border, InputBorder.none);
|
|
expect(decoration?.focusedBorder, InputBorder.none);
|
|
expect(decoration?.enabledBorder, InputBorder.none);
|
|
expect(decoration?.errorBorder, null);
|
|
expect(decoration?.focusedErrorBorder, null);
|
|
expect(decoration?.disabledBorder, null);
|
|
expect(decoration?.constraints, null);
|
|
expect(decoration?.isCollapsed, false);
|
|
expect(decoration?.filled, false);
|
|
expect(decoration?.fillColor, null);
|
|
expect(decoration?.focusColor, null);
|
|
expect(decoration?.hoverColor, null);
|
|
expect(decoration?.contentPadding, EdgeInsets.zero);
|
|
expect(decoration?.hintStyle?.color, theme.colorScheme.onSurfaceVariant);
|
|
}
|
|
|
|
testWidgets('Overall InputDecorationTheme does not override text field style'
|
|
' in SearchBar', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: theme,
|
|
home: const Center(
|
|
child: Material(
|
|
child: SearchBar(hintText: 'hint text'),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
// Check input decoration in `SearchBar`
|
|
checkDecorationInSearchBar(tester);
|
|
|
|
// Check search bar defaults.
|
|
final Finder searchBarMaterial = find.descendant(
|
|
of: find.byType(SearchBar),
|
|
matching: find.byType(Material),
|
|
);
|
|
|
|
final Material material = tester.widget<Material>(searchBarMaterial);
|
|
checkSearchBarDefaults(tester, theme.colorScheme, material);
|
|
});
|
|
|
|
testWidgets('Overall InputDecorationTheme does not override text field style'
|
|
' in the search view route', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: theme,
|
|
home: Scaffold(
|
|
body: Material(
|
|
child: Align(
|
|
alignment: Alignment.topLeft,
|
|
child: SearchAnchor(
|
|
viewHintText: 'hint text',
|
|
builder: (BuildContext context, SearchController controller) {
|
|
return const Icon(Icons.search);
|
|
},
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[];
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.tap(find.byIcon(Icons.search));
|
|
await tester.pumpAndSettle();
|
|
|
|
// Check input decoration in `SearchBar`
|
|
checkDecorationInSearchBar(tester);
|
|
|
|
// Check search bar defaults in search view route.
|
|
final Finder searchBarMaterial = find.descendant(
|
|
of: find.descendant(of: findViewContent(), matching: find.byType(SearchBar)),
|
|
matching: find.byType(Material),
|
|
).first;
|
|
|
|
final Material material = tester.widget<Material>(searchBarMaterial);
|
|
expect(material.color, Colors.transparent);
|
|
expect(material.elevation, 0.0);
|
|
final Text hintText = tester.widget(find.text('hint text'));
|
|
expect(hintText.style?.color, theme.colorScheme.onSurfaceVariant);
|
|
|
|
const String input = 'entered text';
|
|
await tester.enterText(find.byType(SearchBar), input);
|
|
final EditableText inputText = tester.widget(find.text(input));
|
|
expect(inputText.style.color, theme.colorScheme.onSurface);
|
|
});
|
|
});
|
|
}
|
|
|
|
Future<void> checkSearchBarDefaults(WidgetTester tester, ColorScheme colorScheme, Material material) async {
|
|
expect(material.animationDuration, const Duration(milliseconds: 200));
|
|
expect(material.borderOnForeground, true);
|
|
expect(material.borderRadius, null);
|
|
expect(material.clipBehavior, Clip.none);
|
|
expect(material.color, colorScheme.surface);
|
|
expect(material.elevation, 6.0);
|
|
expect(material.shadowColor, colorScheme.shadow);
|
|
expect(material.surfaceTintColor, colorScheme.surfaceTint);
|
|
expect(material.shape, const StadiumBorder());
|
|
|
|
final Text helperText = tester.widget(find.text('hint text'));
|
|
expect(helperText.style?.color, colorScheme.onSurfaceVariant);
|
|
expect(helperText.style?.fontSize, 16.0);
|
|
expect(helperText.style?.fontFamily, 'Roboto');
|
|
expect(helperText.style?.fontWeight, FontWeight.w400);
|
|
|
|
const String input = 'entered text';
|
|
await tester.enterText(find.byType(SearchBar), input);
|
|
final EditableText inputText = tester.widget(find.text(input));
|
|
expect(inputText.style.color, colorScheme.onSurface);
|
|
expect(inputText.style.fontSize, 16.0);
|
|
expect(helperText.style?.fontFamily, 'Roboto');
|
|
expect(inputText.style.fontWeight, FontWeight.w400);
|
|
}
|
|
|
|
Finder findTextField() {
|
|
return find.descendant(
|
|
of: find.byType(SearchBar),
|
|
matching: find.byType(TextField)
|
|
);
|
|
}
|
|
|
|
TextStyle? _iconStyle(WidgetTester tester, IconData icon) {
|
|
final RichText iconRichText = tester.widget<RichText>(
|
|
find.descendant(of: find.byIcon(icon), matching: find.byType(RichText)),
|
|
);
|
|
return iconRichText.text.style;
|
|
}
|
|
|
|
const Color pressedColor = Colors.red;
|
|
const Color hoveredColor = Colors.orange;
|
|
const Color focusedColor = Colors.yellow;
|
|
const Color defaultColor = Colors.green;
|
|
|
|
Color _getColor(Set<MaterialState> states) {
|
|
if (states.contains(MaterialState.pressed)) {
|
|
return pressedColor;
|
|
}
|
|
if (states.contains(MaterialState.hovered)) {
|
|
return hoveredColor;
|
|
}
|
|
if (states.contains(MaterialState.focused)) {
|
|
return focusedColor;
|
|
}
|
|
return defaultColor;
|
|
}
|
|
|
|
final ThemeData theme = ThemeData();
|
|
final TextStyle? pressedStyle = theme.textTheme.bodyLarge?.copyWith(color: pressedColor);
|
|
final TextStyle? hoveredStyle = theme.textTheme.bodyLarge?.copyWith(color: hoveredColor);
|
|
final TextStyle? focusedStyle = theme.textTheme.bodyLarge?.copyWith(color: focusedColor);
|
|
|
|
TextStyle? _getTextStyle(Set<MaterialState> states) {
|
|
if (states.contains(MaterialState.pressed)) {
|
|
return pressedStyle;
|
|
}
|
|
if (states.contains(MaterialState.hovered)) {
|
|
return hoveredStyle;
|
|
}
|
|
if (states.contains(MaterialState.focused)) {
|
|
return focusedStyle;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
Future<TestGesture> _pointGestureToSearchBar(WidgetTester tester) async {
|
|
final Offset center = tester.getCenter(find.byType(SearchBar));
|
|
final TestGesture gesture = await tester.createGesture(
|
|
kind: PointerDeviceKind.mouse,
|
|
);
|
|
|
|
// On hovered.
|
|
await gesture.addPointer();
|
|
await gesture.moveTo(center);
|
|
return gesture;
|
|
}
|
|
Finder findViewContent() {
|
|
return find.byWidgetPredicate((Widget widget) {
|
|
return widget.runtimeType.toString() == '_ViewContent';
|
|
});
|
|
}
|
|
|
|
Material getSearchViewMaterial(WidgetTester tester) {
|
|
return tester.widget<Material>(find.descendant(of: findViewContent(), matching: find.byType(Material)).first);
|
|
}
|