Search code examples
flutterflutter-animation

Flutter hero animation between widgets not screens


Hero animation is the best for navigating between screen, but I need same animation between widgets. Like one card moving another place for example: Product Card moves to shoppingcart and something else. Thanks for answers!


Solution

  • Try this one, add_to_cart_animation:

    import 'package:add_to_cart_animation/add_to_cart_animation.dart';
    import 'package:add_to_cart_animation/add_to_cart_icon.dart';
    
    import 'package:flutter/material.dart';
    
    import 'list_item.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Add To Cart Animation',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: MyHomePage(title: 'Add To Cart Animation'),
          debugShowCheckedModeBanner: false,
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key? key, required this.title}) : super(key: key);
      final String title;
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      // We can detech the location of the card by this  GlobalKey<CartIconKey>
      GlobalKey<CartIconKey> gkCart = GlobalKey<CartIconKey>();
      late Function(GlobalKey) runAddToCardAnimation;
      var _cartQuantityItems = 0;
    
      @override
      Widget build(BuildContext context) {
        return AddToCartAnimation(
          // To send the library the location of the Cart icon
          gkCart: gkCart,
          rotation: true,
          dragToCardCurve: Curves.easeIn,
          dragToCardDuration: const Duration(milliseconds: 1000),
          previewCurve: Curves.linearToEaseOut,
          previewDuration: const Duration(milliseconds: 500),
          previewHeight: 30,
          previewWidth: 30,
          opacity: 0.85,
          initiaJump: false,
          receiveCreateAddToCardAnimationMethod: (addToCardAnimationMethod) {
            // You can run the animation by addToCardAnimationMethod, just pass trough the the global key of  the image as parameter
            this.runAddToCardAnimation = addToCardAnimationMethod;
          },
          child: Scaffold(
            appBar: AppBar(
              title: Text(widget.title),
              centerTitle: false,
              actions: [
                // Improvement/Suggestion 4.4 -> Adding 'clear-cart-button'
                IconButton(
                  icon: Icon(Icons.cleaning_services),
                  onPressed: () {
                    _cartQuantityItems = 0;
                    gkCart.currentState!.runClearCartAnimation();
                  },
                ),
                SizedBox(width: 16),
                AddToCartIcon(
                  key: gkCart,
                  icon: Icon(Icons.shopping_cart),
                  colorBadge: Colors.red,
                ),
                SizedBox(
                  width: 16,
                )
              ],
            ),
            body: ListView(
              children: [
                AppListItem(onClick: listClick, index: 1),
                AppListItem(onClick: listClick, index: 2),
                AppListItem(onClick: listClick, index: 3),
                AppListItem(onClick: listClick, index: 4),
                AppListItem(onClick: listClick, index: 5),
                AppListItem(onClick: listClick, index: 6),
                AppListItem(onClick: listClick, index: 7),
              ],
            ),
          ),
        );
      }
    
      // Improvement/Suggestion 4.4 -> Running AddTOCartAnimation BEFORE runCArtAnimation
      void listClick(GlobalKey gkImageContainer) async {
        await runAddToCardAnimation(gkImageContainer);
        await gkCart.currentState!.runCartAnimation((++_cartQuantityItems).toString());
      }
    }
    

    OR

    [not null safety] this is a sample of add to cart, add_cart_parabola:

    import 'dart:ui';
    import 'package:add_cart_parabola/add_cart_parabola.dart';
    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
    
            primarySwatch: Colors.blue,
          ),
          home: MyHomePage(title: 'Flutter Demo Home Page'),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key, this.title}) : super(key: key);
    
      final String title;
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      int _counter = 0;
    
      GlobalKey floatKey = GlobalKey();
      GlobalKey rootKey = GlobalKey();
      Offset floatOffset ;
    
      @override
      void initState() {
        super.initState();
        WidgetsBinding.instance.addPostFrameCallback((_){
          RenderBox renderBox = floatKey.currentContext.findRenderObject();
          floatOffset = renderBox.localToGlobal(Offset.zero);
        });
      }
    
      @override
      Widget build(BuildContext context) {
    
        return Scaffold(
          appBar: AppBar(
    
            title: Text(widget.title),
          ),
          body: Container(
            key: rootKey,
            width: double.infinity,
            height: double.infinity,
            color: Colors.grey,
            child: ListView(
              children: List.generate(40, (index){
                return generateItem(index);
              }).toList(),
            ),
          ),
          floatingActionButton: FloatingActionButton(
            backgroundColor: Colors.yellow,
            key: floatKey,
            tooltip: 'Increment',
            child: Icon(Icons.add),
          ),
        );
      }
      Widget generateItem(int index){
        Text text = Text("item $index",style: TextStyle(fontSize:
        25),);
    
        Offset temp;
        return GestureDetector(
          onPanDown: (details){
            temp = new Offset(details.globalPosition.dx, details.globalPosition
                .dy);
          },
          onTap: (){
            Function callback ;
            setState(() {
              OverlayEntry entry = OverlayEntry(
                  builder: (ctx){
                    return ParabolaAnimateWidget(rootKey,temp,floatOffset,
                        Icon(Icons.cancel,color: Colors.greenAccent,),callback,);
                  }
              );
    
              callback = (status){
                if(status == AnimationStatus.completed){
                  entry?.remove();
                }
              };
    
              Overlay.of(rootKey.currentContext).insert(entry);
            });
          },
          child: Container(
            color: Colors.orange,
            child: text,
          ),
        );
      }
    }