title: How to transform a widget by switching view?
body: <p>I want a smooth animation that switch between the previous Scale and position to current Scale and postion. But it look like that it's not the exact previous scale or position. Why the only first widget have a reverse scale =&gt; ZoomIn instead of ZoomOut. I add a isSet variable because of the first frame that show the widget far away from where it should start. I have try with AnimatedSlide but it's not the same way to calculate. I don't think I should add more code but if it's necessary for you I would. Am I far from the right way to to it? I'll be grateful. Thank you.</p>
  <p><a href="https://i.stack.imgur.com/vWtFH.gif" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/vWtFH.gif" alt="enter image description here" /></a></p>
  <pre><code>class ItemTransform extends ConsumerStatefulWidget {
    final Widget child;
    final String idItem;
  
    const ItemTransform({required this.idItem, required this.child, Key? key})
        : super(key: key);
  
    @override
    _ItemTransformState createState() =&gt; _ItemTransformState();
  }
  
  class _ItemTransformState extends ConsumerState&lt;ItemTransform&gt;
      with SingleTickerProviderStateMixin {
    late bool isSet;
    late AnimationController _animationController;
    late Animation&lt;double&gt; _animationOffset;
    late Animation&lt;double&gt; _animationScale;
  
    @override
    void initState() {
      super.initState();
      isSet = false;
      _animationController = AnimationController(
          duration: const Duration(milliseconds: 500), vsync: this);
  
      _animationOffset = Tween&lt;double&gt;(begin: 1.0, end: 0.0).animate(
          CurvedAnimation(parent: _animationController, curve: Curves.easeInOut));
  
      _animationScale = Tween(begin: 1.0, end: 1.0).animate(
          CurvedAnimation(parent: _animationController, curve: Curves.easeInOut));
  
      _animationController.forward();
  
      WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
        _afterLayout();
      });
    }
  
    void _afterLayout() {
      if (!mounted) {
        return;
      }
  
      final RenderBox renderBoxRed = context.findRenderObject() as RenderBox;
  
      final size = renderBoxRed.size;
      ref.read(viewAnimationProvider).setSize({widget.idItem: size});
  
      final position = renderBoxRed.localToGlobal(Offset.zero);
      ref.read(viewAnimationProvider).setPosition({widget.idItem: position});
  
      isSet = true;
    }
  
    @override
    void dispose() {
      _animationController.dispose();
      super.dispose();
    }
  
    @override
    Widget build(BuildContext context) {
      return AnimatedBuilder(
          animation: _animationController,
          builder: (context, _) {
            final oldOffset =
                ref.read(viewAnimationProvider).oldPositions[widget.idItem];
            final newOffset =
                ref.read(viewAnimationProvider).newPositions[widget.idItem];
            Offset? toMove;
            if (newOffset != null &amp;&amp; oldOffset != null) {
              if (isSet) {
                toMove = Offset(
                    (oldOffset.dx - newOffset.dx) * _animationOffset.value,
                    (oldOffset.dy - newOffset.dy) * _animationOffset.value);
              } else {
                toMove = Offset(
                    (newOffset.dx - oldOffset.dx) * _animationOffset.value,
                    (newOffset.dy - oldOffset.dy) * _animationOffset.value);
              }
            }
  
            final oldSizes =
                ref.read(viewAnimationProvider).oldSizes[widget.idItem];
            final newSizes =
                ref.read(viewAnimationProvider).newSizes[widget.idItem];
  
            if (oldSizes != null &amp;&amp; newSizes != null) {
              final oldSize = oldSizes.width / oldSizes.height;
              final newSize = newSizes.width / newSizes.height;
  
              if (isSet ) {
                _animationScale = Tween(begin: newSize / oldSize, end: 1.0)
                    .animate(CurvedAnimation(
                        parent: _animationController, curve: Curves.easeInOut));
              }else{
                _animationScale = Tween(begin: oldSize / newSize, end: 1.0).animate(
                    CurvedAnimation(
                        parent: _animationController, curve: Curves.easeInOut));
              }
            }
  
            return Transform.translate(
              offset: toMove ?? Offset.zero,
              child: Transform.scale(
                scale: _animationScale.value,
                child: widget.child,
              ),
            );
          });
    }
  }
  
  </code></pre>
  <pre><code>final viewAnimationProvider =
      ChangeNotifierProvider&lt;ViewAnimationController&gt;((ref) {
    return ViewAnimationController();
  });
  
  class ViewAnimationController extends ChangeNotifier {
    late TypeOfView oldView;
    late TypeOfView newView;
    late Map&lt;String, Offset&gt; oldPositions;
    late Map&lt;String, Offset&gt; newPositions;
    late Map&lt;String, Size&gt; oldSizes;
    late Map&lt;String, Size&gt; newSizes;
  
    ViewAnimationController() {
      oldView = TypeOfView.staggered;
      newView = TypeOfView.normal;
      oldPositions = &lt;String, Offset&gt;{};
      newPositions = &lt;String, Offset&gt;{};
      oldSizes = &lt;String, Size&gt;{};
      newSizes = &lt;String, Size&gt;{};
    }
  
    void setView(TypeOfView newV) {
      oldView = newView;
      newView = newV;
      notifyListeners();
    }
  
    void setPosition(Map&lt;String, Offset&gt; position) {
  
      final newDx = position.values.first.dx;
      final newDy = position.values.first.dy;
  
      final offset = newPositions[position.keys.first];
  
      if (offset != null) {
        if (newDx != newPositions[position.keys.first]!.dx ||
            newDy != newPositions[position.keys.first]!.dy) {
          oldPositions[position.keys.first] = newPositions[position.keys.first]!;
          newPositions[position.keys.first] = position.values.first;
          // newPositions.addAll(position);
        }
      } else {
        newPositions.addAll(position);
      }
    }
  
    void setSize(Map&lt;String, Size&gt; map) {
      final width = map.values.first.width;
      final height = map.values.first.height;
  
      final size = newSizes[map.keys.first];
  
      if (size != null) {
        if (width != size.width || height != size.height) {
          oldSizes[map.keys.first] = newSizes[map.keys.first]!;
          newSizes[map.keys.first] = map.values.first;
        }
      } else {
        newSizes.addAll(map);
      }
    }
  }
  
  </code></pre>
  <pre><code>switch (ref.watch(currentViewProvider)) {
                                    case TypeOfView.normal:
                                      return GridViewCount(
                                          currentSize: currentSize,
                                          listItem: listItem as List&lt;Item&gt;);
                                    case TypeOfView.staggered:
                                      return GridViewStaggered(
                                          currentSize: currentSize,
                                          listItem: listItem as List&lt;Item&gt;);
                                  }
  </code></pre>