diff --git a/packages/flutter/lib/src/material/material.dart b/packages/flutter/lib/src/material/material.dart index 7f8d10585e..19aa171fc6 100644 --- a/packages/flutter/lib/src/material/material.dart +++ b/packages/flutter/lib/src/material/material.dart @@ -350,7 +350,7 @@ class _MaterialState extends State with TickerProviderStateMixin { ); 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 99757bef8b..3e2300d05f 100644 --- a/packages/flutter/lib/src/widgets/implicit_animations.dart +++ b/packages/flutter/lib/src/widgets/implicit_animations.dart @@ -1582,14 +1582,67 @@ class AnimatedDefaultTextStyle extends ImplicitlyAnimatedWidget { /// 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 70cea578cb..198230d23e 100644 --- a/packages/flutter/lib/src/widgets/text.dart +++ b/packages/flutter/lib/src/widgets/text.dart @@ -95,6 +95,7 @@ class DefaultTextStyle extends InheritedTheme { TextOverflow overflow, int maxLines, TextWidthBasis textWidthBasis, + ui.TextHeightBehavior textHeightBehavior, @required Widget child, }) { assert(child != null); @@ -109,6 +110,7 @@ class DefaultTextStyle extends InheritedTheme { overflow: overflow ?? parent.overflow, maxLines: maxLines ?? parent.maxLines, textWidthBasis: textWidthBasis ?? parent.textWidthBasis, + textHeightBehavior: textHeightBehavior ?? parent.textHeightBehavior, child: child, ); }, @@ -140,9 +142,11 @@ class DefaultTextStyle extends InheritedTheme { /// [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 72093cec1b..39fe5b23ad 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 @@ class DefaultTextStyleTransition extends AnimatedWidget { 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 @@ class DefaultTextStyleTransition extends AnimatedWidget { /// 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 @@ class DefaultTextStyleTransition extends AnimatedWidget { 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 133dd3fc1d..a065af905c 100644 --- a/packages/flutter/test/widgets/implicit_animations_test.dart +++ b/packages/flutter/test/widgets/implicit_animations_test.dart @@ -289,6 +289,36 @@ void main() { 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(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 82662c4f8b..0ee1f14816 100644 --- a/packages/flutter/test/widgets/transitions_test.dart +++ b/packages/flutter/test/widgets/transitions_test.dart @@ -408,4 +408,44 @@ void main() { expect(_getOpacity(tester, 'Fade In'), 1.0); }); }); + + testWidgets('DefaultTextStyleTransition builds fully featured DefaultTextStyle', (WidgetTester tester) async { + const DefaultTextStyleTransition styleTransition = DefaultTextStyleTransition( + style: AlwaysStoppedAnimation(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, + )); + }); }