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,
+    ));
+  });
 }