Add missing features to `DefaultTextStyleTransition` and `AnimatedDefaultTextStyle` (#51517)
diff --git a/packages/flutter/lib/src/material/material.dart b/packages/flutter/lib/src/material/material.dart index 7f8d105..19aa171 100644 --- a/packages/flutter/lib/src/material/material.dart +++ b/packages/flutter/lib/src/material/material.dart
@@ -350,7 +350,7 @@ ); Widget contents = widget.child; if (contents != null) { - contents = AnimatedDefaultTextStyle( + contents = AnimatedDefaultTextStyle.merge( style: widget.textStyle ?? Theme.of(context).textTheme.bodyText2, duration: widget.animationDuration, child: contents,
diff --git a/packages/flutter/lib/src/widgets/implicit_animations.dart b/packages/flutter/lib/src/widgets/implicit_animations.dart index 99757be..3e2300d 100644 --- a/packages/flutter/lib/src/widgets/implicit_animations.dart +++ b/packages/flutter/lib/src/widgets/implicit_animations.dart
@@ -1582,14 +1582,67 @@ /// See [DefaultTextStyle.maxLines] for more details. final int maxLines; - /// The strategy to use when calculating the width of the Text. - /// - /// See [TextWidthBasis] for possible values and their implications. + /// {@macro lutter.widgets.text.DefaultTextStyle.tetWidthBasis} final TextWidthBasis textWidthBasis; /// {@macro flutter.dart:ui.textHeightBehavior} final ui.TextHeightBehavior textHeightBehavior; + /// Creates an animated 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 + /// for the [BuildContext] where the widget is inserted, and any of the other + /// arguments that are not null replace the corresponding properties on that + /// same default text style. + /// + /// This constructor cannot be used to override the [maxLines] property of the + /// ancestor with the value null, since null here is used to mean "defer to + /// ancestor". To replace a non-null [maxLines] from an ancestor with the null + /// value (to remove the restriction on number of lines), manually obtain the + /// ambient [DefaultTextStyle] using [DefaultTextStyle.of], then create a new + /// [DefaultTextStyle] using the [new DefaultTextStyle] constructor directly. + /// See the source below for an example of how to do this (since that's + /// essentially what this constructor does). + /// + /// Since the ancestor may not have been an AnimatedDefaultTextStyle, the + /// [duration] property is required. + static Widget merge({ + Key key, + @required Widget child, + TextStyle style, + TextAlign textAlign, + bool softWrap, + TextOverflow overflow, + int maxLines, + TextWidthBasis textWidthBasis, + ui.TextHeightBehavior textHeightBehavior, + Curve curve = Curves.linear, + @required Duration duration, + VoidCallback onEnd, + }) { + assert(child != null); + return Builder( + builder: (BuildContext context) { + final DefaultTextStyle parent = DefaultTextStyle.of(context); + return AnimatedDefaultTextStyle( + key: key, + style: parent.style.merge(style), + textAlign: textAlign ?? parent.textAlign, + softWrap: softWrap ?? parent.softWrap, + overflow: overflow ?? parent.overflow, + maxLines: maxLines ?? parent.maxLines, + textWidthBasis: textWidthBasis ?? parent.textWidthBasis, + textHeightBehavior: textHeightBehavior ?? parent.textHeightBehavior, + duration: duration, + curve: curve, + onEnd: onEnd, + child: child, + ); + }, + ); + } + @override _AnimatedDefaultTextStyleState createState() => _AnimatedDefaultTextStyleState();
diff --git a/packages/flutter/lib/src/widgets/text.dart b/packages/flutter/lib/src/widgets/text.dart index 70cea57..198230d 100644 --- a/packages/flutter/lib/src/widgets/text.dart +++ b/packages/flutter/lib/src/widgets/text.dart
@@ -95,6 +95,7 @@ TextOverflow overflow, int maxLines, TextWidthBasis textWidthBasis, + ui.TextHeightBehavior textHeightBehavior, @required Widget child, }) { assert(child != null); @@ -109,6 +110,7 @@ overflow: overflow ?? parent.overflow, maxLines: maxLines ?? parent.maxLines, textWidthBasis: textWidthBasis ?? parent.textWidthBasis, + textHeightBehavior: textHeightBehavior ?? parent.textHeightBehavior, child: child, ); }, @@ -140,9 +142,11 @@ /// [Text.maxLines]. final int maxLines; + /// {@template flutter.widgets.text.DefaultTextStyle.tetWidthBasis} /// The strategy to use when calculating the width of the Text. /// /// See [TextWidthBasis] for possible values and their implications. + /// {@endtemplate} final TextWidthBasis textWidthBasis; /// {@macro flutter.dart:ui.textHeightBehavior}
diff --git a/packages/flutter/lib/src/widgets/transitions.dart b/packages/flutter/lib/src/widgets/transitions.dart index 72093ce..39fe5b2 100644 --- a/packages/flutter/lib/src/widgets/transitions.dart +++ b/packages/flutter/lib/src/widgets/transitions.dart
@@ -3,6 +3,7 @@ // found in the LICENSE file. import 'dart:math' as math; +import 'dart:ui' as ui show TextHeightBehavior; import 'package:flutter/rendering.dart'; import 'package:vector_math/vector_math_64.dart' show Matrix4; @@ -969,8 +970,11 @@ this.softWrap = true, this.overflow = TextOverflow.clip, this.maxLines, + this.textWidthBasis = TextWidthBasis.parent, + this.textHeightBehavior, }) : assert(style != null), assert(child != null), + assert(textWidthBasis != null), super(key: key, listenable: style); /// The animation that controls the descendants' text style. @@ -993,6 +997,14 @@ /// See [DefaultTextStyle.maxLines] for more details. final int maxLines; + /// The strategy to use when calculating the width of the Text. + /// + /// See [TextWidthBasis] for possible values and their implications. + final TextWidthBasis textWidthBasis; + + /// {@macro flutter.dart:ui.textHeightBehavior} + final ui.TextHeightBehavior textHeightBehavior; + /// The widget below this widget in the tree. /// /// {@macro flutter.widgets.child} @@ -1006,6 +1018,8 @@ softWrap: softWrap, overflow: overflow, maxLines: maxLines, + textWidthBasis: textWidthBasis, + textHeightBehavior: textHeightBehavior, child: child, ); }
diff --git a/packages/flutter/test/widgets/implicit_animations_test.dart b/packages/flutter/test/widgets/implicit_animations_test.dart index 133dd3f..a065af9 100644 --- a/packages/flutter/test/widgets/implicit_animations_test.dart +++ b/packages/flutter/test/widgets/implicit_animations_test.dart
@@ -289,6 +289,36 @@ expect(mockOnEndFunction.called, 1); }); + testWidgets('AnimatedDefaultTextStyle merge test', (WidgetTester tester) async { + const Key animatedKey = Key('animatedStyle'); + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.rtl, + child: DefaultTextStyle( + style: const TextStyle(fontSize: 1234), + textHeightBehavior: const TextHeightBehavior( + applyHeightToFirstAscent: false, + ), + maxLines: 10, + softWrap: true, + child: AnimatedDefaultTextStyle.merge( + key: animatedKey, + maxLines: 20, + duration: const Duration(seconds: 10), + child: const Text('woah!'), + ), + ), + ) + ); + await tester.pump(); + + final Finder animatedDefaultTextStyleFinder = find.byKey(animatedKey); + AnimatedDefaultTextStyle getAnimatedDefautTextStyleWidget(Finder finder) => tester.widget<AnimatedDefaultTextStyle>(finder); + expect(getAnimatedDefautTextStyleWidget(animatedDefaultTextStyleFinder).textHeightBehavior, const TextHeightBehavior(applyHeightToFirstAscent: false,)); + expect(getAnimatedDefautTextStyleWidget(animatedDefaultTextStyleFinder).softWrap, true); + expect(getAnimatedDefautTextStyleWidget(animatedDefaultTextStyleFinder).maxLines, 20); + }); + testWidgets('AnimatedPhysicalModel onEnd callback test', (WidgetTester tester) async { await tester.pumpWidget(wrap( child: TestAnimatedWidget(
diff --git a/packages/flutter/test/widgets/transitions_test.dart b/packages/flutter/test/widgets/transitions_test.dart index 82662c4..0ee1f14 100644 --- a/packages/flutter/test/widgets/transitions_test.dart +++ b/packages/flutter/test/widgets/transitions_test.dart
@@ -408,4 +408,44 @@ expect(_getOpacity(tester, 'Fade In'), 1.0); }); }); + + testWidgets('DefaultTextStyleTransition builds fully featured DefaultTextStyle', (WidgetTester tester) async { + const DefaultTextStyleTransition styleTransition = DefaultTextStyleTransition( + style: AlwaysStoppedAnimation<TextStyle>(TextStyle()), + child: Text('step on legos!'), + textAlign: TextAlign.right, + softWrap: false, + overflow: TextOverflow.fade, + maxLines: 5, + textWidthBasis: TextWidthBasis.longestLine, + textHeightBehavior: TextHeightBehavior( + applyHeightToFirstAscent: false, + applyHeightToLastDescent: false, + ), + ); + + expect((styleTransition.child as Text).data, 'step on legos!'); + expect(styleTransition.textAlign, TextAlign.right); + expect(styleTransition.softWrap, false); + expect(styleTransition.overflow, TextOverflow.fade); + expect(styleTransition.maxLines, 5); + expect(styleTransition.textWidthBasis, TextWidthBasis.longestLine); + expect(styleTransition.textHeightBehavior, const TextHeightBehavior( + applyHeightToFirstAscent: false, + applyHeightToLastDescent: false, + )); + + final DefaultTextStyle style = styleTransition.build(null) as DefaultTextStyle; + + expect((style.child as Text).data, 'step on legos!'); + expect(style.textAlign, TextAlign.right); + expect(style.softWrap, false); + expect(style.overflow, TextOverflow.fade); + expect(style.maxLines, 5); + expect(style.textWidthBasis, TextWidthBasis.longestLine); + expect(style.textHeightBehavior, const TextHeightBehavior( + applyHeightToFirstAscent: false, + applyHeightToLastDescent: false, + )); + }); }