Add transformAlignment and clipBehavior to AnimatedContainer (#67046)

* Add transformAlignment and clipBehavior to AnimatedContainer, make Container.transformAlignment an AlignmentGeometry.

* Add clipBehavior test

* Fix comment

* Clarify clipBehavior animation
diff --git a/packages/flutter/lib/src/widgets/container.dart b/packages/flutter/lib/src/widgets/container.dart
index 14ad931..ce9cb02 100644
--- a/packages/flutter/lib/src/widgets/container.dart
+++ b/packages/flutter/lib/src/widgets/container.dart
@@ -352,14 +352,14 @@
   /// The transformation matrix to apply before painting the container.
   final Matrix4? transform;
 
- /// The alignment of the origin, relative to the size of the container, if [transform] is specified.
- ///
- /// When [transform] is null, the value of this property is ignored.
- ///
- /// See also:
- ///
- ///  * [Transform.alignment], which is set by this property.
-  final Alignment? transformAlignment;
+  /// The alignment of the origin, relative to the size of the container, if [transform] is specified.
+  ///
+  /// When [transform] is null, the value of this property is ignored.
+  ///
+  /// See also:
+  ///
+  ///  * [Transform.alignment], which is set by this property.
+  final AlignmentGeometry? transformAlignment;
 
   /// The clip behavior when [Container.decoration] is not null.
   ///
diff --git a/packages/flutter/lib/src/widgets/implicit_animations.dart b/packages/flutter/lib/src/widgets/implicit_animations.dart
index 806d6b1..b5e1042 100644
--- a/packages/flutter/lib/src/widgets/implicit_animations.dart
+++ b/packages/flutter/lib/src/widgets/implicit_animations.dart
@@ -635,7 +635,9 @@
     BoxConstraints? constraints,
     this.margin,
     this.transform,
+    this.transformAlignment,
     this.child,
+    this.clipBehavior = Clip.none,
     Curve curve = Curves.linear,
     required Duration duration,
     VoidCallback? onEnd,
@@ -709,6 +711,28 @@
   /// The transformation matrix to apply before painting the container.
   final Matrix4? transform;
 
+  /// The alignment of the origin, relative to the size of the container, if [transform] is specified.
+  ///
+  /// When [transform] is null, the value of this property is ignored.
+  ///
+  /// See also:
+  ///
+  ///  * [Transform.alignment], which is set by this property.
+  final AlignmentGeometry? transformAlignment;
+
+  /// The clip behavior when [AnimatedContainer.decoration] is not null.
+  ///
+  /// Defaults to [Clip.none]. Must be [Clip.none] if [decoration] is null.
+  ///
+  /// Unlike other properties of [AnimatedContainer], changes to this property
+  /// apply immediately and have no animation.
+  ///
+  /// If a clip is to be applied, the [Decoration.getClipPath] method
+  /// for the provided decoration must return a clip path. (This is not
+  /// supported by all decorations; the default implementation of that
+  /// method throws an [UnsupportedError].)
+  final Clip clipBehavior;
+
   @override
   _AnimatedContainerState createState() => _AnimatedContainerState();
 
@@ -722,6 +746,8 @@
     properties.add(DiagnosticsProperty<BoxConstraints>('constraints', constraints, defaultValue: null, showName: false));
     properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('margin', margin, defaultValue: null));
     properties.add(ObjectFlagProperty<Matrix4>.has('transform', transform));
+    properties.add(DiagnosticsProperty<AlignmentGeometry>('transformAlignment', transformAlignment, defaultValue: null));
+    properties.add(DiagnosticsProperty<Clip>('clipBehavior', clipBehavior));
   }
 }
 
@@ -733,6 +759,7 @@
   BoxConstraintsTween? _constraints;
   EdgeInsetsGeometryTween? _margin;
   Matrix4Tween? _transform;
+  AlignmentGeometryTween? _transformAlignment;
 
   @override
   void forEachTween(TweenVisitor<dynamic> visitor) {
@@ -743,19 +770,23 @@
     _constraints = visitor(_constraints, widget.constraints, (dynamic value) => BoxConstraintsTween(begin: value as BoxConstraints)) as BoxConstraintsTween?;
     _margin = visitor(_margin, widget.margin, (dynamic value) => EdgeInsetsGeometryTween(begin: value as EdgeInsetsGeometry)) as EdgeInsetsGeometryTween?;
     _transform = visitor(_transform, widget.transform, (dynamic value) => Matrix4Tween(begin: value as Matrix4)) as Matrix4Tween?;
+    _transformAlignment = visitor(_transformAlignment, widget.transformAlignment, (dynamic value) => AlignmentGeometryTween(begin: value as AlignmentGeometry)) as AlignmentGeometryTween?;
   }
 
   @override
   Widget build(BuildContext context) {
+    final Animation<double> animation = this.animation!;
     return Container(
       child: widget.child,
-      alignment: _alignment?.evaluate(animation!),
-      padding: _padding?.evaluate(animation!),
-      decoration: _decoration?.evaluate(animation!),
-      foregroundDecoration: _foregroundDecoration?.evaluate(animation!),
-      constraints: _constraints?.evaluate(animation!),
-      margin: _margin?.evaluate(animation!),
-      transform: _transform?.evaluate(animation!),
+      alignment: _alignment?.evaluate(animation),
+      padding: _padding?.evaluate(animation),
+      decoration: _decoration?.evaluate(animation),
+      foregroundDecoration: _foregroundDecoration?.evaluate(animation),
+      constraints: _constraints?.evaluate(animation),
+      margin: _margin?.evaluate(animation),
+      transform: _transform?.evaluate(animation),
+      transformAlignment: _transformAlignment?.evaluate(animation),
+      clipBehavior: widget.clipBehavior,
     );
   }
 
@@ -769,6 +800,7 @@
     description.add(DiagnosticsProperty<BoxConstraintsTween>('constraints', _constraints, showName: false, defaultValue: null));
     description.add(DiagnosticsProperty<EdgeInsetsGeometryTween>('margin', _margin, defaultValue: null));
     description.add(ObjectFlagProperty<Matrix4Tween>.has('transform', _transform));
+    description.add(DiagnosticsProperty<AlignmentGeometryTween>('transformAlignment', _transformAlignment, defaultValue: null));
   }
 }
 
diff --git a/packages/flutter/test/widgets/animated_container_test.dart b/packages/flutter/test/widgets/animated_container_test.dart
index d2df68f..4d06ca5 100644
--- a/packages/flutter/test/widgets/animated_container_test.dart
+++ b/packages/flutter/test/widgets/animated_container_test.dart
@@ -286,4 +286,74 @@
     expect(text.size.width, equals(200.0));
     expect(text.size.height, equals(100.0));
   });
+
+  testWidgets('AnimatedContainer sets transformAlignment', (WidgetTester tester) async {
+    final Key target = UniqueKey();
+
+    await tester.pumpWidget(
+      Center(
+        child: Directionality(
+          textDirection: TextDirection.ltr,
+          child: AnimatedContainer(
+            duration: const Duration(milliseconds: 200),
+            child: SizedBox(key: target, width: 100.0, height: 200.0),
+            transform: Matrix4.diagonal3Values(0.5, 0.5, 1),
+            transformAlignment: Alignment.topLeft,
+          ),
+        ),
+      ),
+    );
+
+    expect(tester.getSize(find.byKey(target)), const Size(100.0, 200.0));
+    expect(tester.getTopLeft(find.byKey(target)), const Offset(350.0, 200.0));
+
+    await tester.pumpWidget(
+      Center(
+        child: Directionality(
+          textDirection: TextDirection.ltr,
+          child: AnimatedContainer(
+            duration: const Duration(milliseconds: 200),
+            child: SizedBox(key: target, width: 100.0, height: 200.0),
+            transform: Matrix4.diagonal3Values(0.5, 0.5, 1),
+            transformAlignment: Alignment.bottomRight,
+          ),
+        ),
+      ),
+    );
+
+    expect(tester.getSize(find.byKey(target)), const Size(100.0, 200.0));
+    expect(tester.getTopLeft(find.byKey(target)), const Offset(350.0, 200.0));
+
+    await tester.pump(const Duration(milliseconds: 100));
+
+    expect(tester.getSize(find.byKey(target)), const Size(100.0, 200.0));
+    expect(tester.getTopLeft(find.byKey(target)), const Offset(375.0, 250.0));
+
+    await tester.pump(const Duration(milliseconds: 500));
+
+    expect(tester.getSize(find.byKey(target)), const Size(100.0, 200.0));
+    expect(tester.getTopLeft(find.byKey(target)), const Offset(400.0, 300.0));
+  });
+
+  testWidgets('AnimatedContainer sets clipBehavior', (WidgetTester tester) async {
+    await tester.pumpWidget(
+      AnimatedContainer(
+        decoration: const BoxDecoration(
+          color: Color(0xFFED1D7F),
+        ),
+        duration: const Duration(milliseconds: 200),
+      )
+    );
+    expect(tester.firstWidget<Container>(find.byType(Container)).clipBehavior, Clip.none);
+    await tester.pumpWidget(
+      AnimatedContainer(
+        decoration: const BoxDecoration(
+          color: Color(0xFFED1D7F),
+        ),
+        duration: const Duration(milliseconds: 200),
+        clipBehavior: Clip.antiAlias,
+      )
+    );
+    expect(tester.firstWidget<Container>(find.byType(Container)).clipBehavior, Clip.antiAlias);
+  });
 }