Unverified Commit 9fb1c521 authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

InheritedTheme updates (#69050)

parent d50bfd5f
......@@ -147,8 +147,7 @@ class MaterialBannerTheme extends InheritedTheme {
@override
Widget wrap(BuildContext context, Widget child) {
final MaterialBannerTheme? ancestorTheme = context.findAncestorWidgetOfExactType<MaterialBannerTheme>();
return identical(this, ancestorTheme) ? child : MaterialBannerTheme(data: data, child: child);
return MaterialBannerTheme(data: data, child: child);
}
@override
......
......@@ -417,7 +417,7 @@ class _ModalBottomSheetState<T> extends State<_ModalBottomSheet<T>> {
class _ModalBottomSheetRoute<T> extends PopupRoute<T> {
_ModalBottomSheetRoute({
this.builder,
this.theme,
required this.capturedThemes,
this.barrierLabel,
this.backgroundColor,
this.elevation,
......@@ -434,7 +434,7 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> {
super(settings: settings);
final WidgetBuilder? builder;
final ThemeData? theme;
final CapturedThemes capturedThemes;
final bool isScrollControlled;
final Color? backgroundColor;
final double? elevation;
......@@ -470,25 +470,27 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> {
@override
Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
final BottomSheetThemeData sheetTheme = theme?.bottomSheetTheme ?? Theme.of(context)!.bottomSheetTheme;
// By definition, the bottom sheet is aligned to the bottom of the page
// and isn't exposed to the top padding of the MediaQuery.
Widget bottomSheet = MediaQuery.removePadding(
final Widget bottomSheet = MediaQuery.removePadding(
context: context,
removeTop: true,
child: _ModalBottomSheet<T>(
route: this,
backgroundColor: backgroundColor ?? sheetTheme.modalBackgroundColor ?? sheetTheme.backgroundColor,
elevation: elevation ?? sheetTheme.modalElevation ?? sheetTheme.elevation,
shape: shape,
clipBehavior: clipBehavior,
isScrollControlled: isScrollControlled,
enableDrag: enableDrag,
child: Builder(
builder: (BuildContext context) {
final BottomSheetThemeData sheetTheme = Theme.of(context)!.bottomSheetTheme;
return _ModalBottomSheet<T>(
route: this,
backgroundColor: backgroundColor ?? sheetTheme.modalBackgroundColor ?? sheetTheme.backgroundColor,
elevation: elevation ?? sheetTheme.modalElevation ?? sheetTheme.elevation,
shape: shape,
clipBehavior: clipBehavior,
isScrollControlled: isScrollControlled,
enableDrag: enableDrag,
);
},
),
);
if (theme != null)
bottomSheet = Theme(data: theme!, child: bottomSheet);
return bottomSheet;
return capturedThemes.wrap(bottomSheet);
}
}
......@@ -667,9 +669,10 @@ Future<T?> showModalBottomSheet<T>({
assert(debugCheckHasMediaQuery(context));
assert(debugCheckHasMaterialLocalizations(context));
return Navigator.of(context, rootNavigator: useRootNavigator)!.push(_ModalBottomSheetRoute<T>(
final NavigatorState navigator = Navigator.of(context, rootNavigator: useRootNavigator)!;
return navigator.push(_ModalBottomSheetRoute<T>(
builder: builder,
theme: Theme.of(context, shadowThemeOnly: true),
capturedThemes: InheritedTheme.capture(from: context, to: navigator.context),
isScrollControlled: isScrollControlled,
barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
backgroundColor: backgroundColor,
......
......@@ -241,8 +241,7 @@ class ButtonTheme extends InheritedTheme {
@override
Widget wrap(BuildContext context, Widget child) {
final ButtonTheme? ancestorTheme = context.findAncestorWidgetOfExactType<ButtonTheme>();
return identical(this, ancestorTheme) ? child : ButtonTheme.fromButtonThemeData(data: data, child: child);
return ButtonTheme.fromButtonThemeData(data: data, child: child);
}
@override
......
......@@ -91,8 +91,7 @@ class ChipTheme extends InheritedTheme {
@override
Widget wrap(BuildContext context, Widget child) {
final ChipTheme? ancestorTheme = context.findAncestorWidgetOfExactType<ChipTheme>();
return identical(this, ancestorTheme) ? child : ChipTheme(data: data, child: child);
return ChipTheme(data: data, child: child);
}
@override
......
......@@ -978,18 +978,12 @@ Future<T?> showDialog<T>({
assert(useRootNavigator != null);
assert(debugCheckHasMaterialLocalizations(context));
final ThemeData? theme = Theme.of(context, shadowThemeOnly: true);
final CapturedThemes themes = InheritedTheme.capture(from: context, to: Navigator.of(context, rootNavigator: useRootNavigator)!.context);
return showGeneralDialog(
context: context,
pageBuilder: (BuildContext buildContext, Animation<double> animation, Animation<double> secondaryAnimation) {
final Widget pageChild = child ?? Builder(builder: builder!);
Widget dialog = Builder(
builder: (BuildContext context) {
return theme != null
? Theme(data: theme, child: pageChild)
: pageChild;
}
);
Widget dialog = themes.wrap(pageChild);
if (useSafeArea) {
dialog = SafeArea(child: dialog);
}
......
......@@ -166,8 +166,7 @@ class DividerTheme extends InheritedTheme {
@override
Widget wrap(BuildContext context, Widget child) {
final DividerTheme? ancestorTheme = context.findAncestorWidgetOfExactType<DividerTheme>();
return identical(this, ancestorTheme) ? child : DividerTheme(data: data, child: child);
return DividerTheme(data: data, child: child);
}
@override
......
......@@ -402,7 +402,7 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
required this.buttonRect,
required this.selectedIndex,
this.elevation = 8,
this.theme,
required this.capturedThemes,
required this.style,
this.barrierLabel,
this.itemHeight,
......@@ -415,7 +415,7 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
final Rect buttonRect;
final int selectedIndex;
final int elevation;
final ThemeData? theme;
final CapturedThemes capturedThemes;
final TextStyle style;
final double? itemHeight;
final Color? dropdownColor;
......@@ -447,7 +447,7 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
buttonRect: buttonRect,
selectedIndex: selectedIndex,
elevation: elevation,
theme: theme,
capturedThemes: capturedThemes,
style: style,
dropdownColor: dropdownColor,
);
......@@ -533,7 +533,7 @@ class _DropdownRoutePage<T> extends StatelessWidget {
required this.buttonRect,
required this.selectedIndex,
this.elevation = 8,
this.theme,
required this.capturedThemes,
this.style,
required this.dropdownColor,
}) : super(key: key);
......@@ -545,7 +545,7 @@ class _DropdownRoutePage<T> extends StatelessWidget {
final Rect buttonRect;
final int selectedIndex;
final int elevation;
final ThemeData? theme;
final CapturedThemes capturedThemes;
final TextStyle? style;
final Color? dropdownColor;
......@@ -565,7 +565,7 @@ class _DropdownRoutePage<T> extends StatelessWidget {
}
final TextDirection? textDirection = Directionality.of(context);
Widget menu = _DropdownMenu<T>(
final Widget menu = _DropdownMenu<T>(
route: route,
padding: padding.resolve(textDirection),
buttonRect: buttonRect,
......@@ -573,9 +573,6 @@ class _DropdownRoutePage<T> extends StatelessWidget {
dropdownColor: dropdownColor,
);
if (theme != null)
menu = Theme(data: theme!, child: menu);
return MediaQuery.removePadding(
context: context,
removeTop: true,
......@@ -590,7 +587,7 @@ class _DropdownRoutePage<T> extends StatelessWidget {
route: route,
textDirection: textDirection,
),
child: menu,
child: capturedThemes.wrap(menu),
);
},
),
......@@ -1207,6 +1204,7 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi
)
];
final NavigatorState navigator = Navigator.of(context)!;
assert(_dropdownRoute == null);
_dropdownRoute = _DropdownRoute<T>(
items: menuItems,
......@@ -1214,14 +1212,14 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi
padding: _kMenuItemPadding.resolve(textDirection),
selectedIndex: _selectedIndex ?? 0,
elevation: widget.elevation,
theme: Theme.of(context, shadowThemeOnly: true),
capturedThemes: InheritedTheme.capture(from: context, to: navigator.context),
style: _textStyle!,
barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
itemHeight: widget.itemHeight,
dropdownColor: widget.dropdownColor,
);
Navigator.push(context, _dropdownRoute!).then<void>((_DropdownRouteResult<T>? newValue) {
navigator.push(_dropdownRoute!).then<void>((_DropdownRouteResult<T>? newValue) {
_removeDropdownRoute();
if (!mounted || newValue == null)
return;
......
......@@ -116,8 +116,7 @@ class ElevatedButtonTheme extends InheritedTheme {
@override
Widget wrap(BuildContext context, Widget child) {
final ElevatedButtonTheme? ancestorTheme = context.findAncestorWidgetOfExactType<ElevatedButtonTheme>();
return identical(this, ancestorTheme) ? child : ElevatedButtonTheme(data: data, child: child);
return ElevatedButtonTheme(data: data, child: child);
}
@override
......
......@@ -145,8 +145,7 @@ class ListTileTheme extends InheritedTheme {
@override
Widget wrap(BuildContext context, Widget child) {
final ListTileTheme? ancestorTheme = context.findAncestorWidgetOfExactType<ListTileTheme>();
return identical(this, ancestorTheme) ? child : ListTileTheme(
return ListTileTheme(
dense: dense,
shape: shape,
style: style,
......
......@@ -207,8 +207,7 @@ class NavigationRailTheme extends InheritedTheme {
@override
Widget wrap(BuildContext context, Widget child) {
final NavigationRailTheme? ancestorTheme = context.findAncestorWidgetOfExactType<NavigationRailTheme>();
return identical(this, ancestorTheme) ? child : NavigationRailTheme(data: data, child: child);
return NavigationRailTheme(data: data, child: child);
}
@override
......
......@@ -116,8 +116,7 @@ class OutlinedButtonTheme extends InheritedTheme {
@override
Widget wrap(BuildContext context, Widget child) {
final OutlinedButtonTheme? ancestorTheme = context.findAncestorWidgetOfExactType<OutlinedButtonTheme>();
return identical(this, ancestorTheme) ? child : OutlinedButtonTheme(data: data, child: child);
return OutlinedButtonTheme(data: data, child: child);
}
@override
......
......@@ -690,14 +690,11 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
required this.items,
this.initialValue,
this.elevation,
this.theme,
required this.popupMenuTheme,
required this.barrierLabel,
this.semanticLabel,
this.shape,
this.color,
required this.showMenuContext,
required this.captureInheritedThemes,
required this.capturedThemes,
}) : itemSizes = List<Size?>.filled(items.length, null);
final RelativeRect position;
......@@ -705,13 +702,10 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
final List<Size?> itemSizes;
final T? initialValue;
final double? elevation;
final ThemeData? theme;
final String? semanticLabel;
final ShapeBorder? shape;
final Color? color;
final PopupMenuThemeData popupMenuTheme;
final BuildContext showMenuContext;
final bool captureInheritedThemes;
final CapturedThemes capturedThemes;
@override
Animation<double> createAnimation() {
......@@ -745,16 +739,7 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
}
}
Widget menu = _PopupMenu<T>(route: this, semanticLabel: semanticLabel);
if (captureInheritedThemes) {
menu = InheritedTheme.captureAll(showMenuContext, menu);
} else {
// For the sake of backwards compatibility. An (unlikely) app that relied
// on having menus only inherit from the material Theme could set
// captureInheritedThemes to false and get the original behavior.
if (theme != null)
menu = Theme(data: theme!, child: menu);
}
final Widget menu = _PopupMenu<T>(route: this, semanticLabel: semanticLabel);
return SafeArea(
child: Builder(
......@@ -766,7 +751,7 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
selectedItemIndex,
Directionality.of(context)!,
),
child: menu,
child: capturedThemes.wrap(menu),
);
},
),
......@@ -838,14 +823,12 @@ Future<T?> showMenu<T>({
String? semanticLabel,
ShapeBorder? shape,
Color? color,
bool captureInheritedThemes = true,
bool useRootNavigator = false,
}) {
assert(context != null);
assert(position != null);
assert(useRootNavigator != null);
assert(items != null && items.isNotEmpty);
assert(captureInheritedThemes != null);
assert(debugCheckHasMaterialLocalizations(context));
switch (Theme.of(context)!.platform) {
......@@ -859,19 +842,17 @@ Future<T?> showMenu<T>({
semanticLabel ??= MaterialLocalizations.of(context).popupMenuLabel;
}
return Navigator.of(context, rootNavigator: useRootNavigator)!.push(_PopupMenuRoute<T>(
final NavigatorState navigator = Navigator.of(context, rootNavigator: useRootNavigator)!;
return navigator.push(_PopupMenuRoute<T>(
position: position,
items: items,
initialValue: initialValue,
elevation: elevation,
semanticLabel: semanticLabel,
theme: Theme.of(context, shadowThemeOnly: true),
popupMenuTheme: PopupMenuTheme.of(context),
barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
shape: shape,
color: color,
showMenuContext: context,
captureInheritedThemes: captureInheritedThemes,
capturedThemes: InheritedTheme.capture(from: context, to: navigator.context),
));
}
......@@ -964,11 +945,9 @@ class PopupMenuButton<T> extends StatefulWidget {
this.enabled = true,
this.shape,
this.color,
this.captureInheritedThemes = true,
}) : assert(itemBuilder != null),
assert(offset != null),
assert(enabled != null),
assert(captureInheritedThemes != null),
assert(!(child != null && icon != null),
'You can only pass [child] or [icon], not both.'),
super(key: key);
......@@ -1050,11 +1029,6 @@ class PopupMenuButton<T> extends StatefulWidget {
/// Theme.of(context).cardColor is used.
final Color? color;
/// If true (the default) then the menu will be wrapped with copies
/// of the [InheritedTheme]s, like [Theme] and [PopupMenuTheme], which
/// are defined above the [BuildContext] where the menu is shown.
final bool captureInheritedThemes;
@override
PopupMenuButtonState<T> createState() => PopupMenuButtonState<T>();
}
......@@ -1094,7 +1068,6 @@ class PopupMenuButtonState<T> extends State<PopupMenuButton<T>> {
position: position,
shape: widget.shape ?? popupMenuTheme.shape,
color: widget.color ?? popupMenuTheme.color,
captureInheritedThemes: widget.captureInheritedThemes,
)
.then<void>((T? newValue) {
if (!mounted)
......
......@@ -152,8 +152,7 @@ class PopupMenuTheme extends InheritedTheme {
@override
Widget wrap(BuildContext context, Widget child) {
final PopupMenuTheme? ancestorTheme = context.findAncestorWidgetOfExactType<PopupMenuTheme>();
return identical(this, ancestorTheme) ? child : PopupMenuTheme(data: data, child: child);
return PopupMenuTheme(data: data, child: child);
}
@override
......
......@@ -117,8 +117,7 @@ class SliderTheme extends InheritedTheme {
@override
Widget wrap(BuildContext context, Widget child) {
final SliderTheme? ancestorTheme = context.findAncestorWidgetOfExactType<SliderTheme>();
return identical(this, ancestorTheme) ? child : SliderTheme(data: data, child: child);
return SliderTheme(data: data, child: child);
}
@override
......
......@@ -116,8 +116,7 @@ class TextButtonTheme extends InheritedTheme {
@override
Widget wrap(BuildContext context, Widget child) {
final TextButtonTheme? ancestorTheme = context.findAncestorWidgetOfExactType<TextButtonTheme>();
return identical(this, ancestorTheme) ? child : TextButtonTheme(data: data, child: child);
return TextButtonTheme(data: data, child: child);
}
@override
......
......@@ -157,8 +157,7 @@ class TextSelectionTheme extends InheritedTheme {
@override
Widget wrap(BuildContext context, Widget child) {
final TextSelectionTheme? ancestorTheme = context.findAncestorWidgetOfExactType<TextSelectionTheme>();
return identical(this, ancestorTheme) ? child : TextSelectionTheme(data: data, child: child);
return TextSelectionTheme(data: data, child: child);
}
@override
......
......@@ -176,8 +176,7 @@ class _InheritedTheme extends InheritedTheme {
@override
Widget wrap(BuildContext context, Widget child) {
final _InheritedTheme? ancestorTheme = context.findAncestorWidgetOfExactType<_InheritedTheme>();
return identical(this, ancestorTheme) ? child : Theme(data: theme.data, child: child);
return Theme(data: theme.data, child: child);
}
@override
......
......@@ -385,8 +385,7 @@ class TimePickerTheme extends InheritedTheme {
@override
Widget wrap(BuildContext context, Widget child) {
final TimePickerTheme? ancestorTheme = context.findAncestorWidgetOfExactType<TimePickerTheme>();
return identical(this, ancestorTheme) ? child : TimePickerTheme(data: data, child: child);
return TimePickerTheme(data: data, child: child);
}
@override
......
......@@ -271,8 +271,7 @@ class ToggleButtonsTheme extends InheritedTheme {
@override
Widget wrap(BuildContext context, Widget child) {
final ToggleButtonsTheme? ancestorTheme = context.findAncestorWidgetOfExactType<ToggleButtonsTheme>();
return identical(this, ancestorTheme) ? child : ToggleButtonsTheme(data: data, child: child);
return ToggleButtonsTheme(data: data, child: child);
}
@override
......
......@@ -244,8 +244,7 @@ class TooltipTheme extends InheritedTheme {
@override
Widget wrap(BuildContext context, Widget child) {
final TooltipTheme? ancestorTheme = context.findAncestorWidgetOfExactType<TooltipTheme>();
return identical(this, ancestorTheme) ? child : TooltipTheme(data: data, child: child);
return TooltipTheme(data: data, child: child);
}
@override
......
......@@ -79,8 +79,7 @@ class IconTheme extends InheritedTheme {
@override
Widget wrap(BuildContext context, Widget child) {
final IconTheme? iconTheme = context.findAncestorWidgetOfExactType<IconTheme>();
return identical(this, iconTheme) ? child : IconTheme(data: data, child: child);
return IconTheme(data: data, child: child);
}
@override
......
......@@ -9,23 +9,21 @@ import 'framework.dart';
/// An [InheritedWidget] that defines visual properties like colors
/// and text styles, which the [child]'s subtree depends on.
///
/// The [wrap] method is used by [captureAll] to construct a widget
/// that will wrap a child in all of the inherited themes which
/// are present in a build context but are not present in the
/// context that the returned widget is eventually built in.
/// The [wrap] method is used by [captureAll] and [CapturedThemes.wrap] to
/// construct a widget that will wrap a child in all of the inherited themes
/// which are present in a specified part of the widget tree.
///
/// A widget that's shown in a different context from the one it's
/// built in, like the contents of a new route or an overlay, will
/// be able to depend on inherited widget ancestors of the context
/// it's built in.
/// A widget that's shown in a different context from the one it's built in,
/// like the contents of a new route or an overlay, will be able to see the
/// ancestor inherited themes of the context it was built in.
///
/// {@tool dartpad --template=freeform}
/// This example demonstrates how `InheritedTheme.captureAll()` can be used
/// This example demonstrates how `InheritedTheme.capture()` can be used
/// to wrap the contents of a new route with the inherited themes that
/// are present when the route is built - but are not present when route
/// are present when the route was built - but are not present when route
/// is actually shown.
///
/// If the same code is run without `InheritedTheme.captureAll(), the
/// If the same code is run without `InheritedTheme.capture(), the
/// new route's Text widget will inherit the "something must be wrong"
/// fallback text style, rather than the default text style defined in MyApp.
///
......@@ -43,19 +41,25 @@ import 'framework.dart';
/// class MyAppBody extends StatelessWidget {
/// @override
/// Widget build(BuildContext context) {
/// final NavigatorState navigator = Navigator.of(context);
/// // This InheritedTheme.capture() saves references to themes that are
/// // found above the context provided to this widget's build method
/// // excluding themes are are found above the navigator. Those themes do
/// // not have to be captured, because they will already be visible from
/// // the new route pushed onto said navigator.
/// // Themes are captured outside of the route's builder because when the
/// // builder executes, the context may not be valid anymore.
/// final CapturedThemes themes = InheritedTheme.capture(from: context, to: navigator.context);
/// return GestureDetector(
/// onTap: () {
/// Navigator.of(context).push(
/// MaterialPageRoute(
/// builder: (BuildContext _) {
/// // InheritedTheme.captureAll() saves references to themes that
/// // are found above the context provided to this widget's build
/// // method, notably the DefaultTextStyle defined in MyApp. The
/// // context passed to the MaterialPageRoute's builder is not used,
/// // because its ancestors are above MyApp's home.
/// return InheritedTheme.captureAll(context, Container(
/// // Wrap the actual child of the route in the previously
/// // captured themes.
/// return themes.wrap(Container(
/// alignment: Alignment.center,
/// color: Theme.of(context).colorScheme.surface,
/// color: Colors.white,
/// child: Text('Hello World'),
/// ));
/// },
......@@ -95,27 +99,72 @@ abstract class InheritedTheme extends InheritedWidget {
/// Return a copy of this inherited theme with the specified [child].
///
/// If the identical inherited theme is already visible from [context] then
/// just return the [child].
///
/// This implementation for [TooltipTheme] is typical:
///
/// ```dart
/// Widget wrap(BuildContext context, Widget child) {
/// final TooltipTheme ancestorTheme = context.findAncestorWidgetOfExactType<TooltipTheme>());
/// return identical(this, ancestorTheme) ? child : TooltipTheme(data: data, child: child);
/// return TooltipTheme(data: data, child: child);
/// }
/// ```
Widget wrap(BuildContext context, Widget child);
/// Returns a widget that will [wrap] child in all of the inherited themes
/// which are visible from [context].
static Widget captureAll(BuildContext context, Widget child) {
/// Returns a widget that will [wrap] `child` in all of the inherited themes
/// which are present between `context` and the specified `to`
/// [BuildContext].
///
/// The `to` context must be an ancestor of `context`. If `to` is not
/// specified, all inherited themes up to the root of the widget tree are
/// captured.
///
/// After calling this method, the themes present between `context` and `to`
/// are frozen for the provided `child`. If the themes (or their theme data)
/// change in the original subtree, those changes will not be visible to
/// the wrapped `child` - unless this method is called again to re-wrap the
/// child.
static Widget captureAll(BuildContext context, Widget child, {BuildContext? to}) {
assert(child != null);
assert(context != null);
return capture(from: context, to: to).wrap(child);
}
/// Returns a [CapturedThemes] object that includes all the [InheritedTheme]s
/// between the given `from` and `to` [BuildContext]s.
///
/// The `to` context must be an ancestor of the `from` context. If `to` is
/// null, all ancestor inherited themes of `from` up to the root of the
/// widget tree are captured.
///
/// After calling this method, the themes present between `from` and `to` are
/// frozen in the returned [CapturedThemes] object. If the themes (or their
/// theme data) change in the original subtree, those changes will not be
/// applied to the themes captured in the [CapturedThemes] object - unless
/// this method is called again to re-capture the updated themes.
///
/// To wrap a [Widget] in the captured themes, call [CapturedThemes.wrap].
static CapturedThemes capture({ required BuildContext from, required BuildContext? to }) {
assert(from != null);
if (from == to) {
// Nothing to capture.
return CapturedThemes._(const <InheritedTheme>[]);
}
final List<InheritedTheme> themes = <InheritedTheme>[];
final Set<Type> themeTypes = <Type>{};
context.visitAncestorElements((Element ancestor) {
late bool debugDidFindAncestor;
assert(() {
debugDidFindAncestor = to == null;
return true;
}());
from.visitAncestorElements((Element ancestor) {
if (ancestor == to) {
assert(() {
debugDidFindAncestor = true;
return true;
}());
return false;
}
if (ancestor is InheritedElement && ancestor.widget is InheritedTheme) {
final InheritedTheme theme = ancestor.widget as InheritedTheme;
final Type themeType = theme.runtimeType;
......@@ -130,7 +179,23 @@ abstract class InheritedTheme extends InheritedWidget {
return true;
});
return _CaptureAll(themes: themes, child: child);
assert(debugDidFindAncestor, 'The provided `to` context must be an ancestor of the `from` context.');
return CapturedThemes._(themes);
}
}
/// Stores a list of captured [InheritedTheme]s that can be wrapped around a
/// child [Widget].
///
/// Used as return type by [InheritedTheme.capture].
class CapturedThemes {
CapturedThemes._(this._themes);
final List<InheritedTheme> _themes;
/// Wraps a `child` [Widget] in the [InheritedTheme]s captured in this object.
Widget wrap(Widget child) {
return _CaptureAll(themes: _themes, child: child);
}
}
......
......@@ -181,8 +181,7 @@ class DefaultTextStyle extends InheritedTheme {
@override
Widget wrap(BuildContext context, Widget child) {
final DefaultTextStyle? defaultTextStyle = context.findAncestorWidgetOfExactType<DefaultTextStyle>();
return identical(this, defaultTextStyle) ? child : DefaultTextStyle(
return DefaultTextStyle(
style: style,
textAlign: textAlign,
softWrap: softWrap,
......@@ -266,8 +265,7 @@ class DefaultTextHeightBehavior extends InheritedTheme {
@override
Widget wrap(BuildContext context, Widget child) {
final DefaultTextHeightBehavior? defaultTextHeightBehavior = context.findAncestorWidgetOfExactType<DefaultTextHeightBehavior>();
return identical(this, defaultTextHeightBehavior) ? child : DefaultTextHeightBehavior(
return DefaultTextHeightBehavior(
textHeightBehavior: textHeightBehavior,
child: child,
);
......
......@@ -96,7 +96,6 @@ void main() {
testWidgets('PopupMenuTheme.wrap()', (WidgetTester tester) async {
const double menuFontSize = 24;
const Color menuTextColor = Color(0xFF0000FF);
bool captureInheritedThemes = true;
Widget buildFrame() {
return MaterialApp(
......@@ -112,7 +111,6 @@ void main() {
// The appearance of the menu items' text is defined by the
// PopupMenuTheme defined above. Popup menus use
// InheritedTheme.captureAll() by default.
captureInheritedThemes: captureInheritedThemes,
child: const Text('show popupmenu'),
onSelected: (int result) { },
itemBuilder: (BuildContext context) {
......@@ -146,17 +144,6 @@ void main() {
// Dismiss the menu
await tester.tap(find.text('One'));
await tester.pumpAndSettle(); // menu route animation
// Defeat the default support for capturing the PopupMenuTheme.
captureInheritedThemes = false;
await tester.pumpWidget(buildFrame());
await tester.tap(find.text('show popupmenu'));
await tester.pumpAndSettle(); // menu route animation
expect(itemTextStyle('One').fontSize, isNot(menuFontSize));
expect(itemTextStyle('One').color, isNot(menuTextColor));
expect(itemTextStyle('Two').fontSize, isNot(menuFontSize));
expect(itemTextStyle('Two').color, isNot(menuTextColor));
});
testWidgets('BannerTheme.wrap()', (WidgetTester tester) 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