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