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

diff --git a/packages/flutter/lib/src/material/search.dart b/packages/flutter/lib/src/material/search.dart
index c4348c1..c5bb50f 100644
--- a/packages/flutter/lib/src/material/search.dart
+++ b/packages/flutter/lib/src/material/search.dart
@@ -6,6 +6,8 @@
 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 @@
 /// ## 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 @@
   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 @@
   ///  * [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.
   ///
-  /// By default, a white theme is used.
+  /// 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.
+  ///
+  /// 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 @@
   ///
   /// 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 @@
     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 @@
       case null:
         break;
     }
-    final String routeName;
+
+    late final String routeName;
     switch (theme.platform) {
       case TargetPlatform.iOS:
       case TargetPlatform.macOS:
@@ -519,33 +543,28 @@
       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),
           ),
-          actions: widget.delegate.buildActions(context),
-        ),
-        body: AnimatedSwitcher(
-          duration: const Duration(milliseconds: 300),
-          child: body,
+          body: AnimatedSwitcher(
+            duration: const Duration(milliseconds: 300),
+            child: body,
+          ),
         ),
       ),
     );
diff --git a/packages/flutter/test/material/search_test.dart b/packages/flutter/test/material/search_test.dart
index 94328ac..5a564c5 100644
--- a/packages/flutter/test/material/search_test.dart
+++ b/packages/flutter/test/material/search_test.dart
@@ -122,7 +122,8 @@
   });
 
   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 @@
     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 @@
   });
 
   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 @@
     }, 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 @@
       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 @@
       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 @@
     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 @@
     }
     final ThemeData theme = Theme.of(context);
     return theme.copyWith(
-      inputDecorationTheme: InputDecorationTheme(hintStyle: TextStyle(color: hintTextColor)),
+      inputDecorationTheme: searchFieldDecorationTheme ??
+          InputDecorationTheme(
+            hintStyle: searchFieldStyle ??
+                const TextStyle(
+                  color: hintTextColor,
+                ),
+          ),
     );
   }