Unverified Commit 4c1d015e authored by Darren Austin's avatar Darren Austin Committed by GitHub

Reland "Updated SearchDelegate to follow custom InputDecorationTheme" (#69653)

parent a75b5b6c
......@@ -6,6 +6,8 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'app_bar.dart';
import 'app_bar_theme.dart';
import 'color_scheme.dart';
import 'colors.dart';
import 'debug.dart';
import 'input_border.dart';
......@@ -92,9 +94,10 @@ Future<T?> showSearch<T>({
/// ## Handling emojis and other complex characters
/// {@macro flutter.widgets.EditableText.onChanged}
abstract class SearchDelegate<T> {
/// Constructor to be called by subclasses which may specify [searchFieldLabel], [keyboardType] and/or
/// [textInputAction].
/// Constructor to be called by subclasses which may specify
/// [searchFieldLabel], either [searchFieldStyle] or [searchFieldDecorationTheme],
/// [keyboardType] and/or [textInputAction]. Only one of [searchFieldLabel]
/// and [searchFieldDecorationTheme] may be non-null.
///
/// {@tool snippet}
/// ```dart
......@@ -124,9 +127,10 @@ abstract class SearchDelegate<T> {
SearchDelegate({
this.searchFieldLabel,
this.searchFieldStyle,
this.searchFieldDecorationTheme,
this.keyboardType,
this.textInputAction = TextInputAction.search,
});
}) : assert(searchFieldStyle == null || searchFieldDecorationTheme == null);
/// Suggestions shown in the body of the search page while the user types a
/// query into the search field.
......@@ -184,27 +188,39 @@ abstract class SearchDelegate<T> {
/// * [AppBar.actions], the intended use for the return value of this method.
List<Widget> buildActions(BuildContext context);
/// The theme used to style the [AppBar].
/// The theme used to configure the search page.
///
/// The returned [ThemeData] will be used to wrap the entire search page,
/// so it can be used to configure any of its components with the appropriate
/// theme properties.
///
/// By default, a white theme is used.
/// Unless overridden, the default theme will configure the AppBar containing
/// the search input text field with a white background and black text on light
/// themes. For dark themes the default is a dark grey background with light
/// color text.
///
/// See also:
///
/// * [AppBar.backgroundColor], which is set to [ThemeData.primaryColor].
/// * [AppBar.iconTheme], which is set to [ThemeData.primaryIconTheme].
/// * [AppBar.textTheme], which is set to [ThemeData.primaryTextTheme].
/// * [AppBar.brightness], which is set to [ThemeData.primaryColorBrightness].
/// * [AppBarTheme], which configures the AppBar's appearance.
/// * [InputDecorationTheme], which configures the appearance of the search
/// text field.
ThemeData appBarTheme(BuildContext context) {
assert(context != null);
final ThemeData theme = Theme.of(context);
final ColorScheme colorScheme = theme.colorScheme;
assert(theme != null);
return theme.copyWith(
primaryColor: theme.brightness == Brightness.dark
? theme.primaryColor
: Colors.white,
primaryIconTheme: theme.primaryIconTheme.copyWith(color: Colors.grey),
primaryColorBrightness: Brightness.light,
primaryTextTheme: theme.textTheme,
appBarTheme: AppBarTheme(
brightness: colorScheme.brightness,
color: colorScheme.brightness == Brightness.dark ? Colors.grey[900] : Colors.white,
iconTheme: theme.primaryIconTheme.copyWith(color: Colors.grey),
textTheme: theme.textTheme,
),
inputDecorationTheme: searchFieldDecorationTheme ??
InputDecorationTheme(
hintStyle: searchFieldStyle ?? theme.inputDecorationTheme.hintStyle,
border: InputBorder.none,
),
);
}
......@@ -276,8 +292,17 @@ abstract class SearchDelegate<T> {
///
/// If this value is set to null, the value of the ambient [Theme]'s
/// [InputDecorationTheme.hintStyle] will be used instead.
///
/// Only one of [searchFieldStyle] or [searchFieldDecorationTheme] can
/// be non-null.
final TextStyle? searchFieldStyle;
/// The [InputDecorationTheme] used to configure the search field's visuals.
///
/// Only one of [searchFieldStyle] or [searchFieldDecorationTheme] can
/// be non-null.
final InputDecorationTheme? searchFieldDecorationTheme;
/// The type of action button to use for the keyboard.
///
/// Defaults to the default value specified in [TextField].
......@@ -482,8 +507,6 @@ class _SearchPageState<T> extends State<_SearchPage<T>> {
final ThemeData theme = widget.delegate.appBarTheme(context);
final String searchFieldLabel = widget.delegate.searchFieldLabel
?? MaterialLocalizations.of(context).searchFieldLabel;
final TextStyle? searchFieldStyle = widget.delegate.searchFieldStyle
?? theme.inputDecorationTheme.hintStyle;
Widget? body;
switch(widget.delegate._currentBody) {
case _SearchBody.suggestions:
......@@ -501,7 +524,8 @@ class _SearchPageState<T> extends State<_SearchPage<T>> {
case null:
break;
}
final String routeName;
late final String routeName;
switch (theme.platform) {
case TargetPlatform.iOS:
case TargetPlatform.macOS:
......@@ -519,33 +543,28 @@ class _SearchPageState<T> extends State<_SearchPage<T>> {
scopesRoute: true,
namesRoute: true,
label: routeName,
child: Scaffold(
appBar: AppBar(
backgroundColor: theme.primaryColor,
iconTheme: theme.primaryIconTheme,
textTheme: theme.primaryTextTheme,
brightness: theme.primaryColorBrightness,
leading: widget.delegate.buildLeading(context),
title: TextField(
controller: widget.delegate._queryTextController,
focusNode: focusNode,
style: theme.textTheme.headline6,
textInputAction: widget.delegate.textInputAction,
keyboardType: widget.delegate.keyboardType,
onSubmitted: (String _) {
widget.delegate.showResults(context);
},
decoration: InputDecoration(
border: InputBorder.none,
hintText: searchFieldLabel,
hintStyle: searchFieldStyle,
child: Theme(
data: theme,
child: Scaffold(
appBar: AppBar(
leading: widget.delegate.buildLeading(context),
title: TextField(
controller: widget.delegate._queryTextController,
focusNode: focusNode,
style: theme.textTheme.headline6,
textInputAction: widget.delegate.textInputAction,
keyboardType: widget.delegate.keyboardType,
onSubmitted: (String _) {
widget.delegate.showResults(context);
},
decoration: InputDecoration(hintText: searchFieldLabel),
),
actions: widget.delegate.buildActions(context),
),
body: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: body,
),
actions: widget.delegate.buildActions(context),
),
body: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: body,
),
),
);
......
......@@ -122,7 +122,8 @@ void main() {
});
testWidgets('Hint text color overridden', (WidgetTester tester) async {
final _TestSearchDelegate delegate = _TestSearchDelegate();
const String searchHintText = 'Enter search terms';
final _TestSearchDelegate delegate = _TestSearchDelegate(searchHint: searchHintText);
await tester.pumpWidget(TestHomePage(
delegate: delegate,
......@@ -130,9 +131,8 @@ void main() {
await tester.tap(find.byTooltip('Search'));
await tester.pumpAndSettle();
final TextField textField = tester.widget<TextField>(find.byType(TextField));
final Color hintColor = textField.decoration!.hintStyle!.color!;
expect(hintColor, delegate.hintTextColor);
final Text hintText = tester.widget(find.text(searchHintText));
expect(hintText.style!.color, _TestSearchDelegate.hintTextColor);
});
testWidgets('Requests suggestions', (WidgetTester tester) async {
......@@ -540,20 +540,18 @@ void main() {
});
testWidgets('Custom searchFieldStyle value', (WidgetTester tester) async {
const TextStyle searchStyle = TextStyle(color: Colors.red, fontSize: 3);
const String searchHintText = 'Enter search terms';
const TextStyle searchFieldStyle = TextStyle(color: Colors.red, fontSize: 3);
final _TestSearchDelegate delegate = _TestSearchDelegate(searchFieldStyle: searchStyle);
final _TestSearchDelegate delegate = _TestSearchDelegate(searchHint: searchHintText, searchFieldStyle: searchFieldStyle);
await tester.pumpWidget(
TestHomePage(
delegate: delegate,
));
await tester.pumpWidget(TestHomePage(delegate: delegate));
await tester.tap(find.byTooltip('Search'));
await tester.pumpAndSettle();
final TextField textField = tester.widget<TextField>(find.byType(TextField));
final TextStyle hintStyle = textField.decoration!.hintStyle!;
expect(hintStyle, delegate.searchFieldStyle);
final Text hintText = tester.widget(find.text(searchHintText));
expect(hintText.style?.color, delegate.searchFieldStyle?.color);
expect(hintText.style?.fontSize, delegate.searchFieldStyle?.fontSize);
});
testWidgets('keyboard show search button by default', (WidgetTester tester) async {
......@@ -692,6 +690,23 @@ void main() {
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
});
testWidgets('Custom searchFieldDecorationTheme value',
(WidgetTester tester) async {
const InputDecorationTheme searchFieldDecorationTheme = InputDecorationTheme(
hintStyle: TextStyle(color: _TestSearchDelegate.hintTextColor),
);
final _TestSearchDelegate delegate = _TestSearchDelegate(
searchFieldDecorationTheme: searchFieldDecorationTheme,
);
await tester.pumpWidget(TestHomePage(delegate: delegate));
await tester.tap(find.byTooltip('Search'));
await tester.pumpAndSettle();
final ThemeData textFieldTheme = Theme.of(tester.element(find.byType(TextField)));
expect(textFieldTheme.inputDecorationTheme, searchFieldDecorationTheme);
});
// Regression test for: https://github.com/flutter/flutter/issues/66781
testWidgets('text in search bar contrasts background (light mode)', (WidgetTester tester) async {
final ThemeData themeData = ThemeData.light();
......@@ -709,8 +724,11 @@ void main() {
await tester.tap(find.byTooltip('Search'));
await tester.pumpAndSettle();
final AppBar appBar = tester.widget<AppBar>(find.byType(AppBar));
expect(appBar.backgroundColor, Colors.white);
final Material appBarBackground = tester.widget<Material>(find.descendant(
of: find.byType(AppBar),
matching: find.byType(Material),
));
expect(appBarBackground.color, Colors.white);
final TextField textField = tester.widget<TextField>(find.byType(TextField));
expect(textField.style!.color, themeData.textTheme.bodyText1!.color);
......@@ -734,8 +752,11 @@ void main() {
await tester.tap(find.byTooltip('Search'));
await tester.pumpAndSettle();
final AppBar appBar = tester.widget<AppBar>(find.byType(AppBar));
expect(appBar.backgroundColor, themeData.primaryColor);
final Material appBarBackground = tester.widget<Material>(find.descendant(
of: find.byType(AppBar),
matching: find.byType(Material),
));
expect(appBarBackground.color, themeData.primaryColor);
final TextField textField = tester.widget<TextField>(find.byType(TextField));
expect(textField.style!.color, themeData.textTheme.bodyText1!.color);
......@@ -803,16 +824,22 @@ class _TestSearchDelegate extends SearchDelegate<String> {
this.result = 'Result',
this.actions = const <Widget>[],
this.defaultAppBarTheme = false,
InputDecorationTheme? searchFieldDecorationTheme,
TextStyle? searchFieldStyle,
String? searchHint,
TextInputAction textInputAction = TextInputAction.search,
}) : super(searchFieldLabel: searchHint, textInputAction: textInputAction, searchFieldStyle: searchFieldStyle);
}) : super(
searchFieldLabel: searchHint,
textInputAction: textInputAction,
searchFieldStyle: searchFieldStyle,
searchFieldDecorationTheme: searchFieldDecorationTheme,
);
final bool defaultAppBarTheme;
final String suggestions;
final String result;
final List<Widget> actions;
final Color hintTextColor = Colors.green;
static const Color hintTextColor = Colors.green;
@override
ThemeData appBarTheme(BuildContext context) {
......@@ -821,7 +848,13 @@ class _TestSearchDelegate extends SearchDelegate<String> {
}
final ThemeData theme = Theme.of(context);
return theme.copyWith(
inputDecorationTheme: InputDecorationTheme(hintStyle: TextStyle(color: hintTextColor)),
inputDecorationTheme: searchFieldDecorationTheme ??
InputDecorationTheme(
hintStyle: searchFieldStyle ??
const TextStyle(
color: hintTextColor,
),
),
);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment