Commit 8fdd2066 authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

DefaultTextStyle.merge and IconTheme.merge improvements (#9358)

I can't figure out if this is genius or a giant hack.

This lets you use DefaultTextStyle.merge and IconTheme.merge without
specifying a BuildContext. It automatically merges in at the
appropriate place in the tree using a Builder widget.
parent aaa0a1cf
...@@ -248,8 +248,7 @@ class CardCollectionState extends State<CardCollection> { ...@@ -248,8 +248,7 @@ class CardCollectionState extends State<CardCollection> {
controller: cardModel.textController, controller: cardModel.textController,
), ),
) )
: new DefaultTextStyle.merge( : DefaultTextStyle.merge(
context: context,
style: cardLabelStyle.copyWith( style: cardLabelStyle.copyWith(
fontSize: _varyFontSizes ? 5.0 + index : null fontSize: _varyFontSizes ? 5.0 + index : null
), ),
......
...@@ -438,8 +438,7 @@ class _AppBarState extends State<AppBar> { ...@@ -438,8 +438,7 @@ class _AppBarState extends State<AppBar> {
Widget appBar = new ClipRect( Widget appBar = new ClipRect(
child: new CustomSingleChildLayout( child: new CustomSingleChildLayout(
delegate: const _ToolbarContainerLayout(), delegate: const _ToolbarContainerLayout(),
child: new IconTheme.merge( child: IconTheme.merge(
context: context,
data: appBarIconTheme, data: appBarIconTheme,
child: new DefaultTextStyle( child: new DefaultTextStyle(
style: sideStyle, style: sideStyle,
......
...@@ -347,8 +347,7 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr ...@@ -347,8 +347,7 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr
alignment: FractionalOffset.bottomCenter, alignment: FractionalOffset.bottomCenter,
child: new Container( child: new Container(
margin: const EdgeInsets.only(bottom: 10.0), margin: const EdgeInsets.only(bottom: 10.0),
child: new DefaultTextStyle.merge( child: DefaultTextStyle.merge(
context: context,
style: new TextStyle( style: new TextStyle(
fontSize: 14.0, fontSize: 14.0,
color: colorTween.evaluate(_animations[i]), color: colorTween.evaluate(_animations[i]),
...@@ -419,8 +418,7 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr ...@@ -419,8 +418,7 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr
margin: const EdgeInsets.only(bottom: 10.0), margin: const EdgeInsets.only(bottom: 10.0),
child: new FadeTransition( child: new FadeTransition(
opacity: _animations[i], opacity: _animations[i],
child: new DefaultTextStyle.merge( child: DefaultTextStyle.merge(
context: context,
style: const TextStyle( style: const TextStyle(
fontSize: 14.0, fontSize: 14.0,
color: Colors.white color: Colors.white
......
...@@ -292,8 +292,7 @@ class _MaterialButtonState extends State<MaterialButton> { ...@@ -292,8 +292,7 @@ class _MaterialButtonState extends State<MaterialButton> {
final double height = widget.height ?? buttonTheme.height; final double height = widget.height ?? buttonTheme.height;
final int elevation = (_highlight ? widget.highlightElevation : widget.elevation) ?? 0; final int elevation = (_highlight ? widget.highlightElevation : widget.elevation) ?? 0;
final bool hasColorOrElevation = (widget.color != null || elevation > 0); final bool hasColorOrElevation = (widget.color != null || elevation > 0);
Widget contents = new IconTheme.merge( Widget contents = IconTheme.merge(
context: context,
data: new IconThemeData( data: new IconThemeData(
color: textColor color: textColor
), ),
......
...@@ -466,8 +466,7 @@ class DataTable extends StatelessWidget { ...@@ -466,8 +466,7 @@ class DataTable extends StatelessWidget {
? (placeholder ? Colors.black38 : Colors.black87) ? (placeholder ? Colors.black38 : Colors.black87)
: (placeholder ? Colors.white30 : Colors.white70) : (placeholder ? Colors.white30 : Colors.white70)
), ),
child: new IconTheme.merge( child: IconTheme.merge(
context: context,
data: new IconThemeData( data: new IconThemeData(
color: isLightTheme ? Colors.black54 : Colors.white70 color: isLightTheme ? Colors.black54 : Colors.white70
), ),
......
...@@ -124,8 +124,7 @@ class _FloatingActionButtonState extends State<FloatingActionButton> { ...@@ -124,8 +124,7 @@ class _FloatingActionButtonState extends State<FloatingActionButton> {
} }
Widget result = new Center( Widget result = new Center(
child: new IconTheme.merge( child: IconTheme.merge(
context: context,
data: new IconThemeData(color: iconColor), data: new IconThemeData(color: iconColor),
child: widget.child child: widget.child
) )
......
...@@ -124,8 +124,7 @@ class GridTileBar extends StatelessWidget { ...@@ -124,8 +124,7 @@ class GridTileBar extends StatelessWidget {
height: (title != null && subtitle != null) ? 68.0 : 48.0, height: (title != null && subtitle != null) ? 68.0 : 48.0,
child: new Theme( child: new Theme(
data: darkTheme, data: darkTheme,
child: new IconTheme.merge( child: IconTheme.merge(
context: context,
data: const IconThemeData(color: Colors.white), data: const IconThemeData(color: Colors.white),
child: new Row( child: new Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
......
...@@ -151,8 +151,7 @@ class IconButton extends StatelessWidget { ...@@ -151,8 +151,7 @@ class IconButton extends StatelessWidget {
width: iconSize, width: iconSize,
child: new Align( child: new Align(
alignment: alignment, alignment: alignment,
child: new IconTheme.merge( child: IconTheme.merge(
context: context,
data: new IconThemeData( data: new IconThemeData(
size: iconSize, size: iconSize,
color: currentColor color: currentColor
......
...@@ -28,17 +28,20 @@ class IconTheme extends InheritedWidget { ...@@ -28,17 +28,20 @@ class IconTheme extends InheritedWidget {
/// Creates an icon theme that controls the color, opacity, and size of /// Creates an icon theme that controls the color, opacity, and size of
/// descendant widgets, and merges in the current icon theme, if any. /// descendant widgets, and merges in the current icon theme, if any.
/// ///
/// The [context], [data], and [child] arguments must not be null. /// The [data] and [child] arguments must not be null.
factory IconTheme.merge({ static Widget merge({
Key key, Key key,
@required BuildContext context,
@required IconThemeData data, @required IconThemeData data,
@required Widget child @required Widget child
}) { }) {
return new IconTheme( return new Builder(
key: key, builder: (BuildContext context) {
data: _getInheritedIconThemeData(context).merge(data), return new IconTheme(
child: child key: key,
data: _getInheritedIconThemeData(context).merge(data),
child: child,
);
},
); );
} }
......
...@@ -439,8 +439,7 @@ class InputDecorator extends StatelessWidget { ...@@ -439,8 +439,7 @@ class InputDecorator extends StatelessWidget {
new Container( new Container(
margin: new EdgeInsets.only(top: iconTop), margin: new EdgeInsets.only(top: iconTop),
width: isDense ? 40.0 : 48.0, width: isDense ? 40.0 : 48.0,
child: new IconTheme.merge( child: IconTheme.merge(
context: context,
data: new IconThemeData( data: new IconThemeData(
color: isFocused ? activeColor : Colors.black45, color: isFocused ? activeColor : Colors.black45,
size: isDense ? 18.0 : 24.0, size: isDense ? 18.0 : 24.0,
......
...@@ -345,8 +345,7 @@ class ListTile extends StatelessWidget { ...@@ -345,8 +345,7 @@ class ListTile extends StatelessWidget {
final List<Widget> children = <Widget>[]; final List<Widget> children = <Widget>[];
if (leading != null) { if (leading != null) {
children.add(new IconTheme.merge( children.add(IconTheme.merge(
context: context,
data: new IconThemeData(color: _iconColor(theme, tileTheme)), data: new IconThemeData(color: _iconColor(theme, tileTheme)),
child: new Container( child: new Container(
margin: const EdgeInsets.only(right: 16.0), margin: const EdgeInsets.only(right: 16.0),
......
...@@ -377,8 +377,7 @@ class PaginatedDataTableState extends State<PaginatedDataTable> { ...@@ -377,8 +377,7 @@ class PaginatedDataTableState extends State<PaginatedDataTable> {
// See https://material.google.com/components/data-tables.html#data-tables-tables-within-cards // See https://material.google.com/components/data-tables.html#data-tables-tables-within-cards
style: _selectedRowCount > 0 ? themeData.textTheme.subhead.copyWith(color: themeData.accentColor) style: _selectedRowCount > 0 ? themeData.textTheme.subhead.copyWith(color: themeData.accentColor)
: themeData.textTheme.title.copyWith(fontWeight: FontWeight.w400), : themeData.textTheme.title.copyWith(fontWeight: FontWeight.w400),
child: new IconTheme.merge( child: IconTheme.merge(
context: context,
data: const IconThemeData( data: const IconThemeData(
opacity: 0.54 opacity: 0.54
), ),
...@@ -413,8 +412,7 @@ class PaginatedDataTableState extends State<PaginatedDataTable> { ...@@ -413,8 +412,7 @@ class PaginatedDataTableState extends State<PaginatedDataTable> {
), ),
new DefaultTextStyle( new DefaultTextStyle(
style: footerTextStyle, style: footerTextStyle,
child: new IconTheme.merge( child: IconTheme.merge(
context: context,
data: const IconThemeData( data: const IconThemeData(
opacity: 0.54 opacity: 0.54
), ),
......
...@@ -160,8 +160,7 @@ class _PopupMenuItemState<T extends PopupMenuItem<dynamic>> extends State<T> { ...@@ -160,8 +160,7 @@ class _PopupMenuItemState<T extends PopupMenuItem<dynamic>> extends State<T> {
); );
if (!widget.enabled) { if (!widget.enabled) {
final bool isDark = theme.brightness == Brightness.dark; final bool isDark = theme.brightness == Brightness.dark;
item = new IconTheme.merge( item = IconTheme.merge(
context: context,
data: new IconThemeData(opacity: isDark ? 0.5 : 0.38), data: new IconThemeData(opacity: isDark ? 0.5 : 0.38),
child: item child: item
); );
......
...@@ -138,8 +138,7 @@ class _TabStyle extends AnimatedWidget { ...@@ -138,8 +138,7 @@ class _TabStyle extends AnimatedWidget {
return new DefaultTextStyle( return new DefaultTextStyle(
style: textStyle.copyWith(color: color), style: textStyle.copyWith(color: color),
child: new IconTheme.merge( child: IconTheme.merge(
context: context,
data: new IconThemeData( data: new IconThemeData(
size: 24.0, size: 24.0,
color: color, color: color,
......
...@@ -201,8 +201,7 @@ class _TwoLevelSublistState extends State<TwoLevelSublist> with SingleTickerProv ...@@ -201,8 +201,7 @@ class _TwoLevelSublistState extends State<TwoLevelSublist> with SingleTickerProv
), ),
child: new Column( child: new Column(
children: <Widget>[ children: <Widget>[
new IconTheme.merge( IconTheme.merge(
context: context,
data: new IconThemeData(color: _iconColor.evaluate(_easeInAnimation)), data: new IconThemeData(color: _iconColor.evaluate(_easeInAnimation)),
child: new TwoLevelListItem( child: new TwoLevelListItem(
onTap: _handleOnTap, onTap: _handleOnTap,
......
...@@ -41,15 +41,15 @@ class DefaultTextStyle extends InheritedWidget { ...@@ -41,15 +41,15 @@ class DefaultTextStyle extends InheritedWidget {
maxLines = null, maxLines = null,
overflow = TextOverflow.clip; overflow = TextOverflow.clip;
/// Creates a default text style that inherits from the given [BuildContext]. /// Creates a default text style that overrides the text styles in scope at
/// this point in the widget tree.
/// ///
/// The given [style] is merged with the [style] from the default text style /// The given [style] is merged with the [style] from the default text style
/// for the given [BuildContext] and, if non-null, the given [textAlign] /// for the [BuildContext] where the widget is inserted, and any of the other
/// replaces the [textAlign] from the default text style for the given /// arguments that are not null replace the corresponding properties on that
/// [BuildContext]. /// same default text style.
factory DefaultTextStyle.merge({ static Widget merge({
Key key, Key key,
@required BuildContext context,
TextStyle style, TextStyle style,
TextAlign textAlign, TextAlign textAlign,
bool softWrap, bool softWrap,
...@@ -57,17 +57,20 @@ class DefaultTextStyle extends InheritedWidget { ...@@ -57,17 +57,20 @@ class DefaultTextStyle extends InheritedWidget {
int maxLines, int maxLines,
@required Widget child, @required Widget child,
}) { }) {
assert(context != null);
assert(child != null); assert(child != null);
final DefaultTextStyle parent = DefaultTextStyle.of(context); return new Builder(
return new DefaultTextStyle( builder: (BuildContext context) {
key: key, final DefaultTextStyle parent = DefaultTextStyle.of(context);
style: parent.style.merge(style), return new DefaultTextStyle(
textAlign: textAlign ?? parent.textAlign, key: key,
softWrap: softWrap ?? parent.softWrap, style: parent.style.merge(style),
overflow: overflow ?? parent.overflow, textAlign: textAlign ?? parent.textAlign,
maxLines: maxLines ?? parent.maxLines, softWrap: softWrap ?? parent.softWrap,
child: child overflow: overflow ?? parent.overflow,
maxLines: maxLines ?? parent.maxLines,
child: child
);
},
); );
} }
......
...@@ -7,6 +7,7 @@ import 'package:flutter/material.dart'; ...@@ -7,6 +7,7 @@ import 'package:flutter/material.dart';
void main() { void main() {
testWidgets('Navigator.push works within a PopupMenuButton', (WidgetTester tester) async { testWidgets('Navigator.push works within a PopupMenuButton', (WidgetTester tester) async {
final Key targetKey = new UniqueKey();
await tester.pumpWidget( await tester.pumpWidget(
new MaterialApp( new MaterialApp(
routes: <String, WidgetBuilder> { routes: <String, WidgetBuilder> {
...@@ -17,6 +18,7 @@ void main() { ...@@ -17,6 +18,7 @@ void main() {
home: new Material( home: new Material(
child: new Center( child: new Center(
child: new Builder( child: new Builder(
key: targetKey,
builder: (BuildContext context) { builder: (BuildContext context) {
return new PopupMenuButton<int>( return new PopupMenuButton<int>(
onSelected: (int value) { onSelected: (int value) {
...@@ -38,7 +40,7 @@ void main() { ...@@ -38,7 +40,7 @@ void main() {
) )
); );
await tester.tap(find.byType(Builder)); await tester.tap(find.byKey(targetKey));
await tester.pump(); await tester.pump();
await tester.pump(const Duration(seconds: 1)); // finish the menu animation await tester.pump(const Duration(seconds: 1)); // finish the menu animation
......
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