Skip to content

Commit 433bca5

Browse files
authored
Fix SearchAnchor's search view isn't updated when the theme changes & widgets inside the search view do not inherit local themes (#132749)
fixes [SearchAnchor (search view) UI glitch on platform brightness changes](flutter/flutter#131835) fixes [Search view widgets cannot inherit local themes](flutter/flutter#132741) ### Description - This fixes an issue where the `SearchAnchor`'s search view isn't updated when the platform brightness changes. - Fixes an issue where widgets inside the search view cannot use local themes ### Actual Results `SearchAnchor` currently passed both global and local themes on the search view popup pushing and it uses anchor. button's context to look up the theme. ![search_view drawio (1)](https://github.com/flutter/flutter/assets/48603081/b5317fb1-ee73-461c-a119-f2a1e29f5909) As a result, when the platform changes and the search view is rebuilt, it cannot use the updated theme. https://github.com/flutter/flutter/assets/48603081/2f1ebe74-e7d5-4ef3-b97c-a741c3d68964 ### Expected Results Similar to `PopupMenuButton`, the theme should be located in the search view so that when the platform brightness is updated and the search view is rebuilt it can use the updated theme. ![search_view drawio](https://github.com/flutter/flutter/assets/48603081/4e48c0cb-a558-4de6-9865-5f51981a343f) https://github.com/flutter/flutter/assets/48603081/d8d85982-c661-4cac-83e8-0488b1d93daf However, the search view's context cannot access local themes so I added support for `InheritedTheme`, which fixes the local. theme issue for both the search view and widgets inside the search view. ### When using local themes for the `SearchAnchor`'s search view and widgets inside the view. ### Before ![Screenshot 2023-08-17 at 15 54 02](https://github.com/flutter/flutter/assets/48603081/dec18ba3-9f01-4706-987a-eb2fd4afb180) ### After ![Screenshot 2023-08-17 at 15 55 15](https://github.com/flutter/flutter/assets/48603081/13f2797a-7f70-43b5-bc56-7971cf76a61d)
1 parent d3c45f1 commit 433bca5

3 files changed

Lines changed: 168 additions & 53 deletions

File tree

packages/flutter/lib/src/material/search_anchor.dart

Lines changed: 51 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,8 @@ class _SearchAnchorState extends State<SearchAnchor> {
342342
}
343343

344344
void _openView() {
345-
Navigator.of(context).push(_SearchViewRoute(
345+
final NavigatorState navigator = Navigator.of(context);
346+
navigator.push(_SearchViewRoute(
346347
viewLeading: widget.viewLeading,
347348
viewTrailing: widget.viewTrailing,
348349
viewHintText: widget.viewHintText,
@@ -363,6 +364,7 @@ class _SearchAnchorState extends State<SearchAnchor> {
363364
searchController: _searchController,
364365
suggestionsBuilder: widget.suggestionsBuilder,
365366
textCapitalization: widget.textCapitalization,
367+
capturedThemes: InheritedTheme.capture(from: context, to: navigator.context),
366368
));
367369
}
368370

@@ -433,6 +435,7 @@ class _SearchViewRoute extends PopupRoute<_SearchViewRoute> {
433435
required this.anchorKey,
434436
required this.searchController,
435437
required this.suggestionsBuilder,
438+
required this.capturedThemes,
436439
});
437440

438441
final ValueGetter<bool>? toggleVisibility;
@@ -455,6 +458,7 @@ class _SearchViewRoute extends PopupRoute<_SearchViewRoute> {
455458
final GlobalKey anchorKey;
456459
final SearchController searchController;
457460
final SuggestionsBuilder suggestionsBuilder;
461+
final CapturedThemes capturedThemes;
458462

459463
@override
460464
Color? get barrierColor => Colors.transparent;
@@ -467,7 +471,6 @@ class _SearchViewRoute extends PopupRoute<_SearchViewRoute> {
467471

468472
late final SearchViewThemeData viewDefaults;
469473
late final SearchViewThemeData viewTheme;
470-
late final DividerThemeData dividerTheme;
471474
final RectTween _rectTween = RectTween();
472475

473476
Rect? getRect() {
@@ -502,7 +505,6 @@ class _SearchViewRoute extends PopupRoute<_SearchViewRoute> {
502505
void updateViewConfig(BuildContext context) {
503506
viewDefaults = _SearchViewDefaultsM3(context, isFullScreen: showFullScreenView);
504507
viewTheme = SearchViewTheme.of(context);
505-
dividerTheme = DividerTheme.of(context);
506508
}
507509

508510
void updateTweens(BuildContext context) {
@@ -576,30 +578,29 @@ class _SearchViewRoute extends PopupRoute<_SearchViewRoute> {
576578
curve: _kViewFadeOnInterval,
577579
reverseCurve: _kViewFadeOnInterval.flipped,
578580
),
579-
child: _ViewContent(
580-
viewLeading: viewLeading,
581-
viewTrailing: viewTrailing,
582-
viewHintText: viewHintText,
583-
viewBackgroundColor: viewBackgroundColor,
584-
viewElevation: viewElevation,
585-
viewSurfaceTintColor: viewSurfaceTintColor,
586-
viewSide: viewSide,
587-
viewShape: viewShape,
588-
viewHeaderTextStyle: viewHeaderTextStyle,
589-
viewHeaderHintStyle: viewHeaderHintStyle,
590-
dividerColor: dividerColor,
591-
showFullScreenView: showFullScreenView,
592-
animation: curvedAnimation,
593-
topPadding: topPadding,
594-
viewMaxWidth: _rectTween.end!.width,
595-
viewRect: viewRect,
596-
viewDefaults: viewDefaults,
597-
viewTheme: viewTheme,
598-
dividerTheme: dividerTheme,
599-
viewBuilder: viewBuilder,
600-
searchController: searchController,
601-
suggestionsBuilder: suggestionsBuilder,
602-
textCapitalization: textCapitalization,
581+
child: capturedThemes.wrap(
582+
_ViewContent(
583+
viewLeading: viewLeading,
584+
viewTrailing: viewTrailing,
585+
viewHintText: viewHintText,
586+
viewBackgroundColor: viewBackgroundColor,
587+
viewElevation: viewElevation,
588+
viewSurfaceTintColor: viewSurfaceTintColor,
589+
viewSide: viewSide,
590+
viewShape: viewShape,
591+
viewHeaderTextStyle: viewHeaderTextStyle,
592+
viewHeaderHintStyle: viewHeaderHintStyle,
593+
dividerColor: dividerColor,
594+
showFullScreenView: showFullScreenView,
595+
animation: curvedAnimation,
596+
topPadding: topPadding,
597+
viewMaxWidth: _rectTween.end!.width,
598+
viewRect: viewRect,
599+
viewBuilder: viewBuilder,
600+
searchController: searchController,
601+
suggestionsBuilder: suggestionsBuilder,
602+
textCapitalization: textCapitalization,
603+
),
603604
),
604605
);
605606
}
@@ -631,9 +632,6 @@ class _ViewContent extends StatefulWidget {
631632
required this.animation,
632633
required this.viewMaxWidth,
633634
required this.viewRect,
634-
required this.viewDefaults,
635-
required this.viewTheme,
636-
required this.dividerTheme,
637635
required this.searchController,
638636
required this.suggestionsBuilder,
639637
});
@@ -656,9 +654,6 @@ class _ViewContent extends StatefulWidget {
656654
final Animation<double> animation;
657655
final double viewMaxWidth;
658656
final Rect viewRect;
659-
final SearchViewThemeData viewDefaults;
660-
final SearchViewThemeData viewTheme;
661-
final DividerThemeData dividerTheme;
662657
final SearchController searchController;
663658
final SuggestionsBuilder suggestionsBuilder;
664659

@@ -747,39 +742,43 @@ class _ViewContentState extends State<_ViewContent> {
747742
),
748743
];
749744

745+
final SearchViewThemeData viewDefaults = _SearchViewDefaultsM3(context, isFullScreen: widget.showFullScreenView);
746+
final SearchViewThemeData viewTheme = SearchViewTheme.of(context);
747+
final DividerThemeData dividerTheme = DividerTheme.of(context);
748+
750749
final Color effectiveBackgroundColor = widget.viewBackgroundColor
751-
?? widget.viewTheme.backgroundColor
752-
?? widget.viewDefaults.backgroundColor!;
750+
?? viewTheme.backgroundColor
751+
?? viewDefaults.backgroundColor!;
753752
final Color effectiveSurfaceTint = widget.viewSurfaceTintColor
754-
?? widget.viewTheme.surfaceTintColor
755-
?? widget.viewDefaults.surfaceTintColor!;
753+
?? viewTheme.surfaceTintColor
754+
?? viewDefaults.surfaceTintColor!;
756755
final double effectiveElevation = widget.viewElevation
757-
?? widget.viewTheme.elevation
758-
?? widget.viewDefaults.elevation!;
756+
?? viewTheme.elevation
757+
?? viewDefaults.elevation!;
759758
final BorderSide? effectiveSide = widget.viewSide
760-
?? widget.viewTheme.side
761-
?? widget.viewDefaults.side;
759+
?? viewTheme.side
760+
?? viewDefaults.side;
762761
OutlinedBorder effectiveShape = widget.viewShape
763-
?? widget.viewTheme.shape
764-
?? widget.viewDefaults.shape!;
762+
?? viewTheme.shape
763+
?? viewDefaults.shape!;
765764
if (effectiveSide != null) {
766765
effectiveShape = effectiveShape.copyWith(side: effectiveSide);
767766
}
768767
final Color effectiveDividerColor = widget.dividerColor
769-
?? widget.viewTheme.dividerColor
770-
?? widget.dividerTheme.color
771-
?? widget.viewDefaults.dividerColor!;
768+
?? viewTheme.dividerColor
769+
?? dividerTheme.color
770+
?? viewDefaults.dividerColor!;
772771
final TextStyle? effectiveTextStyle = widget.viewHeaderTextStyle
773-
?? widget.viewTheme.headerTextStyle
774-
?? widget.viewDefaults.headerTextStyle;
772+
?? viewTheme.headerTextStyle
773+
?? viewDefaults.headerTextStyle;
775774
final TextStyle? effectiveHintStyle = widget.viewHeaderHintStyle
776-
?? widget.viewTheme.headerHintStyle
775+
?? viewTheme.headerHintStyle
777776
?? widget.viewHeaderTextStyle
778-
?? widget.viewTheme.headerTextStyle
779-
?? widget.viewDefaults.headerHintStyle;
777+
?? viewTheme.headerTextStyle
778+
?? viewDefaults.headerHintStyle;
780779

781780
final Widget viewDivider = DividerTheme(
782-
data: widget.dividerTheme.copyWith(color: effectiveDividerColor),
781+
data: dividerTheme.copyWith(color: effectiveDividerColor),
783782
child: const Divider(height: 1),
784783
);
785784

packages/flutter/lib/src/material/search_view_theme.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ class SearchViewThemeData with Diagnosticable {
187187
///
188188
/// * [SearchViewThemeData], which describes the actual configuration of a search view
189189
/// theme.
190-
class SearchViewTheme extends InheritedWidget {
190+
class SearchViewTheme extends InheritedTheme {
191191
/// Creates a const theme that controls the configurations for the search view
192192
/// created by the [SearchAnchor] widget.
193193
const SearchViewTheme({
@@ -212,6 +212,11 @@ class SearchViewTheme extends InheritedWidget {
212212
return searchViewTheme?.data ?? Theme.of(context).searchViewTheme;
213213
}
214214

215+
@override
216+
Widget wrap(BuildContext context, Widget child) {
217+
return SearchViewTheme(data: data, child: child);
218+
}
219+
215220
@override
216221
bool updateShouldNotify(SearchViewTheme oldWidget) => data != oldWidget.data;
217222
}

packages/flutter/test/material/search_anchor_test.dart

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2055,6 +2055,117 @@ void main() {
20552055
expect(inputText.style.color, theme.colorScheme.onSurface);
20562056
});
20572057
});
2058+
2059+
testWidgets('SearchAnchor view respects theme brightness', (WidgetTester tester) async {
2060+
Widget buildSearchAnchor(ThemeData theme) {
2061+
return MaterialApp(
2062+
theme: theme,
2063+
home: Center(
2064+
child: Material(
2065+
child: SearchAnchor(
2066+
builder: (BuildContext context, SearchController controller) {
2067+
return IconButton(
2068+
icon: const Icon(Icons.ac_unit),
2069+
onPressed: () {
2070+
controller.openView();
2071+
},
2072+
);
2073+
},
2074+
suggestionsBuilder: (BuildContext context, SearchController controller) {
2075+
return <Widget>[];
2076+
},
2077+
),
2078+
),
2079+
),
2080+
);
2081+
}
2082+
2083+
ThemeData theme = ThemeData(brightness: Brightness.light);
2084+
await tester.pumpWidget(buildSearchAnchor(theme));
2085+
2086+
// Open the search view.
2087+
await tester.tap(find.widgetWithIcon(IconButton, Icons.ac_unit));
2088+
await tester.pumpAndSettle();
2089+
2090+
// Test the search view background color.
2091+
Material material = getSearchViewMaterial(tester);
2092+
expect(material.color, theme.colorScheme.surface);
2093+
2094+
// Change the theme brightness.
2095+
theme = ThemeData(brightness: Brightness.dark);
2096+
await tester.pumpWidget(buildSearchAnchor(theme));
2097+
await tester.pumpAndSettle();
2098+
2099+
// Test the search view background color.
2100+
material = getSearchViewMaterial(tester);
2101+
expect(material.color, theme.colorScheme.surface);
2102+
});
2103+
2104+
testWidgets('Search view widgets can inherit local themes', (WidgetTester tester) async {
2105+
final ThemeData globalTheme = ThemeData(colorSchemeSeed: Colors.red);
2106+
final ThemeData localTheme = ThemeData(
2107+
colorSchemeSeed: Colors.green,
2108+
iconButtonTheme: IconButtonThemeData(
2109+
style: IconButton.styleFrom(
2110+
backgroundColor: const Color(0xffffff00)
2111+
),
2112+
),
2113+
cardTheme: const CardTheme(color: Color(0xff00ffff)),
2114+
);
2115+
Widget buildSearchAnchor() {
2116+
return MaterialApp(
2117+
theme: globalTheme,
2118+
home: Center(
2119+
child: Builder(
2120+
builder: (BuildContext context) {
2121+
return Theme(
2122+
data: localTheme,
2123+
child: Material(
2124+
child: SearchAnchor.bar(
2125+
suggestionsBuilder: (BuildContext context, SearchController controller) {
2126+
return <Widget>[
2127+
Card(
2128+
child: ListTile(
2129+
onTap: () {},
2130+
title: const Text('Item 1'),
2131+
),
2132+
),
2133+
];
2134+
},
2135+
),
2136+
),
2137+
);
2138+
}
2139+
),
2140+
),
2141+
);
2142+
}
2143+
2144+
await tester.pumpWidget(buildSearchAnchor());
2145+
2146+
// Open the search view.
2147+
await tester.tap(find.byType(SearchBar));
2148+
await tester.pumpAndSettle();
2149+
2150+
// Test the search view background color.
2151+
final Material searchViewMaterial = getSearchViewMaterial(tester);
2152+
expect(searchViewMaterial.color, localTheme.colorScheme.surface);
2153+
2154+
// Test the search view icons background color.
2155+
final Material iconButtonMaterial = tester.widget<Material>(find.descendant(
2156+
of: find.byType(IconButton),
2157+
matching: find.byType(Material),
2158+
).first);
2159+
expect(find.byWidget(iconButtonMaterial), findsOneWidget);
2160+
expect(iconButtonMaterial.color, localTheme.iconButtonTheme.style?.backgroundColor?.resolve(<MaterialState>{}));
2161+
2162+
// Test the suggestion card color.
2163+
final Material suggestionMaterial = tester.widget<Material>(find.descendant(
2164+
of: find.byType(Card),
2165+
matching: find.byType(Material),
2166+
).first);
2167+
expect(suggestionMaterial.color, localTheme.cardTheme.color);
2168+
});
20582169
}
20592170

20602171
Future<void> checkSearchBarDefaults(WidgetTester tester, ColorScheme colorScheme, Material material) async {

0 commit comments

Comments
 (0)