Repository: flutter
Issue: Using composition in the animation widgets implementation would make them easier to understand and customize. 
Labels: framework,a: animation,c: proposal,P3,team-framework,triaged-framework

This is a minor suggestion, but the animation widgets in the flutter framework often use inheritance when they could use composition. Using composition would be more in line with the rest of the flutter framework, and would make animations easier to understand from a developers perspective. It would also make custom animations easier to implement. 

## AnimatedWidget
As one example, we could get rid of `AnimatedWidget`. AnimatedWidget is an abstract class that maintains an `AnimationController`, and rebuilds whenever the animation controller ticks. This logic could instead be put in the `AnimatedBuilder` widget, which essentially does the same thing, but is actually instantiable. 

<details>
 <summary>Proposed AnimatedBuilder</summary>

```dart
class AnimatedBuilder extends StatefulWidget {
  const AnimatedBuilder({
    Key? key,
    required this.animation,
    required this.builder,
    this.child,
  }) : assert(animation != null),
       assert(builder != null),
       super(key: key);

  final Listenable animation;
  final TransitionBuilder builder;
  final Widget? child;

  @override
  _AnimatedBuilderState createState() => _AnimatedBuilderState();
}

class _AnimatedBuilderState extends State<AnimatedBuilder> {
  @override
  void initState() {
    super.initState();
    widget.animation.addListener(_handleChange);
  }

  @override
  void didUpdateWidget(AnimatedBuilder oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.animation != oldWidget.animation) {
      oldWidget.animation.removeListener(_handleChange);
      widget.animation.addListener(_handleChange);
    }
  }

  @override
  void dispose() {
    widget.animation.removeListener(_handleChange);
    super.dispose();
  }

  void _handleChange() {
    setState(() {
      // The animation's state is our build state, and it changed already.
    });
  }

  @override
  Widget build(BuildContext context) {
    return widget.builder(context, widget.child);
  }
}
```
</details>

Then, the individual transition widgets could *compose* an `AnimatedBuilder` rather than _inherit_ from `AnimatedWidget`.

For example, the build method of `AlignTransition` would look like this:

```dart
@override
Widget build(BuildContext context) {
  return AnimatedBuilder(
    animation: alignment,
    child: child,
    builder: (context, child) {
      return Align(
        alignment: alignment.value,
        widthFactor: widthFactor,
        heightFactor: heightFactor,
        child: child,
      );
    },
  );
} 
```

And AlignTransition would extend `StatelessWidget` rather than `AnimatedWidget`. 

```dart
class AlignTransition extends StatelessWidget {
```

## ImplicitlyAnimatedWidget 

This one is a bit harder, as it is more complex, but could still be done without inheritance. The general concept of an `ImplicitlyAnimatedWidget` is its parameters are updated, it runs an animation from 0 to 1. It maintains that animation. It's still up to the individual `ImplicitlyAnimatedWidget` (for example, `AnimatedAlign`) to determine _how_ the animation is used. This could be done through a builder widget.


### ImplicitlyAnimatedWidget proposal

We should consider deprecating `ImplicitlyAnimatedWidget` in favor of an `ImplicitlyAnimatedWidgetBuilder`. `ImplicitlyAnimatedWidgetBuilder` takes in a widget. Whenever that widget changes, the `ImplicitlyAnimatedWidgetBuilder` will start an animation and call a builder function. 

<details>
 <summary>Proposed ImplicitlyAnimatedWidgetBuilder</summary>

```dart
class ImplicitlyAnimatedWidgetBuilder extends StatefulWidget {
  const ImplicitlyAnimatedWidget({
    Key? key,
    this.duration,
    required this.widgetToWatch,
    required this.builder,
  });

  /// The [builder] function will be called whenever this [widgetToWatch] changes. 
  Widget widgetToWatch;  

  /// Builds the child of this widget whenever [widgetToWatch] changes.
  ///
  /// When [widgetToWatch] is updated, the old widget state will be passed to child1, the updated
  /// widget state will be passed to child2, and the animation param will animate from 0 to 1.
  Widget Function(Widget child1, Widget child2, animation<double>);  
}  

class _ImplicitlyAnimatedWidgetBuilderState extends State<ImplicitlyAnimatedWidgetBuilder> {
  AnimationController _controller;
  Widget _animationBeginState;
  Widget _animationEndState;

  @override
  void didUpdateWidget(AnimatedBuilder oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.widgetToWatch != widgetToWatch) {
       _animationBeginState = oldWidget.widgetToWatch;
       _animationEndState = widgetToWatch;
        _controller
          ..value = 0.0
          ..forward();
    }
  }

  @override
  Widget build(BuildContext context) {
    return widget.builder(_animationBeginState, _animationEndState, _controller);
  }
}

```
</details>

Now, instead of `extend`ing `ImplicitlyAnimatedWidget`, something like `AnimatedAlign` would implement `StatelessWidget`.

```dart
class AnimatedAlign extends StatelessWidget {
```

And instead of specifying the animation behavior in the overriden `forEachTween` method (as it does now), it would specify it's behavior in its `build` method:

<details>
<summary>Proposed AnimatedAlign build method</summary>

```dart
@override
void build(BuildContext context) {
  return ImplicitlyAnimatedWidgetBuilder(
    widgetToWatch: Align(
      alignment: widget.alignment,
      widthFactor: widget.widthFactor,
      heightFactor: widget.heightFactor,
    ),
    builder: (Align alignFrom, Align alignTo, animation) {
      final alignment = AlignmentGeometryTween(
        begin: alignFrom.alignment,
        end: alignTo.alignment,
      ).transform(animation.value);
      
      final widthFactor = Tween<double>(
        begin: alignFrom.widthFactor,
        end: alignTo.widthFactor,
      ).transform(animation.value);

      final heightFactor = Tween<double>(
        begin: alignFrom.heightFactor,
        end: alignTo.heightFactor,
      ).transform(animation.value);
      
      return AnimatedBuilder(
        animation: animation,
        child: widget.child,
        builder: (context, child) {
          return Align(
            alignment: alignment,
            widthFactor: widthFactor,
            heightFactor: heightFactor,
            child: child,
          );
        }
      );
    },
  );
} 
```
</details>

This would remove the added layer of complexity that one faces when implementing an `ImplicitlyAnimatedWidget`, which is understanding what logic is being handled by the `ImplicitlyAnimatedWidget`, and how to extend and use it. 

### Conclusion

These are just a few examples of places where we could use composition over inheritance in the animations library of the flutter framework. Doing this could help make animations easier to understand as they, like everything else in the flutter framework, will simply be "widgets".