Unverified Commit 433bca5e authored by Taha Tesser's avatar Taha Tesser Committed by GitHub

Fix `SearchAnchor`'s search view isn't updated when the theme changes &...

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](https://github.com/flutter/flutter/issues/131835)
fixes [Search view widgets cannot inherit local themes](https://github.com/flutter/flutter/issues/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)
parent d3c45f14
......@@ -342,7 +342,8 @@ class _SearchAnchorState extends State<SearchAnchor> {
}
void _openView() {
Navigator.of(context).push(_SearchViewRoute(
final NavigatorState navigator = Navigator.of(context);
navigator.push(_SearchViewRoute(
viewLeading: widget.viewLeading,
viewTrailing: widget.viewTrailing,
viewHintText: widget.viewHintText,
......@@ -363,6 +364,7 @@ class _SearchAnchorState extends State<SearchAnchor> {
searchController: _searchController,
suggestionsBuilder: widget.suggestionsBuilder,
textCapitalization: widget.textCapitalization,
capturedThemes: InheritedTheme.capture(from: context, to: navigator.context),
));
}
......@@ -433,6 +435,7 @@ class _SearchViewRoute extends PopupRoute<_SearchViewRoute> {
required this.anchorKey,
required this.searchController,
required this.suggestionsBuilder,
required this.capturedThemes,
});
final ValueGetter<bool>? toggleVisibility;
......@@ -455,6 +458,7 @@ class _SearchViewRoute extends PopupRoute<_SearchViewRoute> {
final GlobalKey anchorKey;
final SearchController searchController;
final SuggestionsBuilder suggestionsBuilder;
final CapturedThemes capturedThemes;
@override
Color? get barrierColor => Colors.transparent;
......@@ -467,7 +471,6 @@ class _SearchViewRoute extends PopupRoute<_SearchViewRoute> {
late final SearchViewThemeData viewDefaults;
late final SearchViewThemeData viewTheme;
late final DividerThemeData dividerTheme;
final RectTween _rectTween = RectTween();
Rect? getRect() {
......@@ -502,7 +505,6 @@ class _SearchViewRoute extends PopupRoute<_SearchViewRoute> {
void updateViewConfig(BuildContext context) {
viewDefaults = _SearchViewDefaultsM3(context, isFullScreen: showFullScreenView);
viewTheme = SearchViewTheme.of(context);
dividerTheme = DividerTheme.of(context);
}
void updateTweens(BuildContext context) {
......@@ -576,30 +578,29 @@ class _SearchViewRoute extends PopupRoute<_SearchViewRoute> {
curve: _kViewFadeOnInterval,
reverseCurve: _kViewFadeOnInterval.flipped,
),
child: _ViewContent(
viewLeading: viewLeading,
viewTrailing: viewTrailing,
viewHintText: viewHintText,
viewBackgroundColor: viewBackgroundColor,
viewElevation: viewElevation,
viewSurfaceTintColor: viewSurfaceTintColor,
viewSide: viewSide,
viewShape: viewShape,
viewHeaderTextStyle: viewHeaderTextStyle,
viewHeaderHintStyle: viewHeaderHintStyle,
dividerColor: dividerColor,
showFullScreenView: showFullScreenView,
animation: curvedAnimation,
topPadding: topPadding,
viewMaxWidth: _rectTween.end!.width,
viewRect: viewRect,
viewDefaults: viewDefaults,
viewTheme: viewTheme,
dividerTheme: dividerTheme,
viewBuilder: viewBuilder,
searchController: searchController,
suggestionsBuilder: suggestionsBuilder,
textCapitalization: textCapitalization,
child: capturedThemes.wrap(
_ViewContent(
viewLeading: viewLeading,
viewTrailing: viewTrailing,
viewHintText: viewHintText,
viewBackgroundColor: viewBackgroundColor,
viewElevation: viewElevation,
viewSurfaceTintColor: viewSurfaceTintColor,
viewSide: viewSide,
viewShape: viewShape,
viewHeaderTextStyle: viewHeaderTextStyle,
viewHeaderHintStyle: viewHeaderHintStyle,
dividerColor: dividerColor,
showFullScreenView: showFullScreenView,
animation: curvedAnimation,
topPadding: topPadding,
viewMaxWidth: _rectTween.end!.width,
viewRect: viewRect,
viewBuilder: viewBuilder,
searchController: searchController,
suggestionsBuilder: suggestionsBuilder,
textCapitalization: textCapitalization,
),
),
);
}
......@@ -631,9 +632,6 @@ class _ViewContent extends StatefulWidget {
required this.animation,
required this.viewMaxWidth,
required this.viewRect,
required this.viewDefaults,
required this.viewTheme,
required this.dividerTheme,
required this.searchController,
required this.suggestionsBuilder,
});
......@@ -656,9 +654,6 @@ class _ViewContent extends StatefulWidget {
final Animation<double> animation;
final double viewMaxWidth;
final Rect viewRect;
final SearchViewThemeData viewDefaults;
final SearchViewThemeData viewTheme;
final DividerThemeData dividerTheme;
final SearchController searchController;
final SuggestionsBuilder suggestionsBuilder;
......@@ -747,39 +742,43 @@ class _ViewContentState extends State<_ViewContent> {
),
];
final SearchViewThemeData viewDefaults = _SearchViewDefaultsM3(context, isFullScreen: widget.showFullScreenView);
final SearchViewThemeData viewTheme = SearchViewTheme.of(context);
final DividerThemeData dividerTheme = DividerTheme.of(context);
final Color effectiveBackgroundColor = widget.viewBackgroundColor
?? widget.viewTheme.backgroundColor
?? widget.viewDefaults.backgroundColor!;
?? viewTheme.backgroundColor
?? viewDefaults.backgroundColor!;
final Color effectiveSurfaceTint = widget.viewSurfaceTintColor
?? widget.viewTheme.surfaceTintColor
?? widget.viewDefaults.surfaceTintColor!;
?? viewTheme.surfaceTintColor
?? viewDefaults.surfaceTintColor!;
final double effectiveElevation = widget.viewElevation
?? widget.viewTheme.elevation
?? widget.viewDefaults.elevation!;
?? viewTheme.elevation
?? viewDefaults.elevation!;
final BorderSide? effectiveSide = widget.viewSide
?? widget.viewTheme.side
?? widget.viewDefaults.side;
?? viewTheme.side
?? viewDefaults.side;
OutlinedBorder effectiveShape = widget.viewShape
?? widget.viewTheme.shape
?? widget.viewDefaults.shape!;
?? viewTheme.shape
?? viewDefaults.shape!;
if (effectiveSide != null) {
effectiveShape = effectiveShape.copyWith(side: effectiveSide);
}
final Color effectiveDividerColor = widget.dividerColor
?? widget.viewTheme.dividerColor
?? widget.dividerTheme.color
?? widget.viewDefaults.dividerColor!;
?? viewTheme.dividerColor
?? dividerTheme.color
?? viewDefaults.dividerColor!;
final TextStyle? effectiveTextStyle = widget.viewHeaderTextStyle
?? widget.viewTheme.headerTextStyle
?? widget.viewDefaults.headerTextStyle;
?? viewTheme.headerTextStyle
?? viewDefaults.headerTextStyle;
final TextStyle? effectiveHintStyle = widget.viewHeaderHintStyle
?? widget.viewTheme.headerHintStyle
?? viewTheme.headerHintStyle
?? widget.viewHeaderTextStyle
?? widget.viewTheme.headerTextStyle
?? widget.viewDefaults.headerHintStyle;
?? viewTheme.headerTextStyle
?? viewDefaults.headerHintStyle;
final Widget viewDivider = DividerTheme(
data: widget.dividerTheme.copyWith(color: effectiveDividerColor),
data: dividerTheme.copyWith(color: effectiveDividerColor),
child: const Divider(height: 1),
);
......
......@@ -187,7 +187,7 @@ class SearchViewThemeData with Diagnosticable {
///
/// * [SearchViewThemeData], which describes the actual configuration of a search view
/// theme.
class SearchViewTheme extends InheritedWidget {
class SearchViewTheme extends InheritedTheme {
/// Creates a const theme that controls the configurations for the search view
/// created by the [SearchAnchor] widget.
const SearchViewTheme({
......@@ -212,6 +212,11 @@ class SearchViewTheme extends InheritedWidget {
return searchViewTheme?.data ?? Theme.of(context).searchViewTheme;
}
@override
Widget wrap(BuildContext context, Widget child) {
return SearchViewTheme(data: data, child: child);
}
@override
bool updateShouldNotify(SearchViewTheme oldWidget) => data != oldWidget.data;
}
......@@ -2055,6 +2055,117 @@ void main() {
expect(inputText.style.color, theme.colorScheme.onSurface);
});
});
testWidgets('SearchAnchor view respects theme brightness', (WidgetTester tester) async {
Widget buildSearchAnchor(ThemeData theme) {
return MaterialApp(
theme: theme,
home: Center(
child: Material(
child: SearchAnchor(
builder: (BuildContext context, SearchController controller) {
return IconButton(
icon: const Icon(Icons.ac_unit),
onPressed: () {
controller.openView();
},
);
},
suggestionsBuilder: (BuildContext context, SearchController controller) {
return <Widget>[];
},
),
),
),
);
}
ThemeData theme = ThemeData(brightness: Brightness.light);
await tester.pumpWidget(buildSearchAnchor(theme));
// Open the search view.
await tester.tap(find.widgetWithIcon(IconButton, Icons.ac_unit));
await tester.pumpAndSettle();
// Test the search view background color.
Material material = getSearchViewMaterial(tester);
expect(material.color, theme.colorScheme.surface);
// Change the theme brightness.
theme = ThemeData(brightness: Brightness.dark);
await tester.pumpWidget(buildSearchAnchor(theme));
await tester.pumpAndSettle();
// Test the search view background color.
material = getSearchViewMaterial(tester);
expect(material.color, theme.colorScheme.surface);
});
testWidgets('Search view widgets can inherit local themes', (WidgetTester tester) async {
final ThemeData globalTheme = ThemeData(colorSchemeSeed: Colors.red);
final ThemeData localTheme = ThemeData(
colorSchemeSeed: Colors.green,
iconButtonTheme: IconButtonThemeData(
style: IconButton.styleFrom(
backgroundColor: const Color(0xffffff00)
),
),
cardTheme: const CardTheme(color: Color(0xff00ffff)),
);
Widget buildSearchAnchor() {
return MaterialApp(
theme: globalTheme,
home: Center(
child: Builder(
builder: (BuildContext context) {
return Theme(
data: localTheme,
child: Material(
child: SearchAnchor.bar(
suggestionsBuilder: (BuildContext context, SearchController controller) {
return <Widget>[
Card(
child: ListTile(
onTap: () {},
title: const Text('Item 1'),
),
),
];
},
),
),
);
}
),
),
);
}
await tester.pumpWidget(buildSearchAnchor());
// Open the search view.
await tester.tap(find.byType(SearchBar));
await tester.pumpAndSettle();
// Test the search view background color.
final Material searchViewMaterial = getSearchViewMaterial(tester);
expect(searchViewMaterial.color, localTheme.colorScheme.surface);
// Test the search view icons background color.
final Material iconButtonMaterial = tester.widget<Material>(find.descendant(
of: find.byType(IconButton),
matching: find.byType(Material),
).first);
expect(find.byWidget(iconButtonMaterial), findsOneWidget);
expect(iconButtonMaterial.color, localTheme.iconButtonTheme.style?.backgroundColor?.resolve(<MaterialState>{}));
// Test the suggestion card color.
final Material suggestionMaterial = tester.widget<Material>(find.descendant(
of: find.byType(Card),
matching: find.byType(Material),
).first);
expect(suggestionMaterial.color, localTheme.cardTheme.color);
});
}
Future<void> checkSearchBarDefaults(WidgetTester tester, ColorScheme colorScheme, Material material) async {
......
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