flutterflutter-getx

Use GetX for Physics Simulation Animation? How to synchronize animationController.value.value with dragAlignment.value?


I'm new to Flutter. I heard that GetX can simplify development. I want to rewrite Flutter's official physical simulation example using GetX.

My code:

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter/physics.dart';

//pageview
class Home extends StatelessWidget {
  const Home({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Physics'),
        actions: const [
          Row(children: [Icon(Icons.share)])
        ],
        centerTitle: true,
        elevation: 0,
      ),
      body: Center(
        child: DraggableCard(child: const FlutterLogo(size: 128)),
      ),
    );
  }
}

// widget's view
class DraggableCard extends GetView {
  DraggableCard({required this.child, super.key});
  final Widget child;
  final logic = _DraggableCardLogic();

  @override
  Widget build(BuildContext context) {
    final size = Get.size;
    return GetX(
      init: _DraggableCardLogic(),
      initState: (_) {},
      builder: (_) {
        return GestureDetector(
          onPanDown: (details) {
            logic._aniC.stop();
          },
          onPanUpdate: (details) {
            logic._dragAlignment.value += Alignment(
              details.delta.dx / (size.width / 2),
              details.delta.dy / (size.height / 2),
            );
            // print(logic._dragAlignment);
          },
          onPanEnd: (details) {
            logic._runAni(details.velocity.pixelsPerSecond, size);
          },
          child: Align(
            alignment: logic._dragAlignment.value,
            child: Card(child: child),
          ),
        );
      },
    );
  }
}

//logic&state
class _DraggableCardLogic extends GetxController
    with GetSingleTickerProviderStateMixin {
  late AnimationController _aniC = AnimationController(vsync: this);
  final _dragAlignment = Alignment.center.obs;
  late final _aniBack = _aniC
      .drive(AlignmentTween(
        begin: _dragAlignment.value,
        end: Alignment.center,
      ))
      .obs;

  void _runAni(Offset pixelsPerSec, Size size) {
    _aniBack.value = _aniC.drive(AlignmentTween(
      begin: _dragAlignment.value,
      end: Alignment.center,
    ));
    print('DRAG:${_aniBack.value}');

    final unitsPerSecX = pixelsPerSec.dx / size.width;
    final unitsPerSecY = pixelsPerSec.dy / size.height;
    final unitsPerSec = Offset(unitsPerSecX, unitsPerSecY);
    final unitVelocity = unitsPerSec.distance;

    const spring = SpringDescription(mass: 30, stiffness: 1, damping: 1);

    final simulation = SpringSimulation(spring, 0, 1, -unitVelocity);
    _aniC.animateWith(simulation);
    // how to synth the state?
  }

  @override
  void onInit() {
    super.onInit();
    _aniC = AnimationController(vsync: this);
    _aniC.addListener(() {
      _dragAlignment.value = _aniBack.value.value;
    },);
    print('init!');
  }

  @override
  void onClose() {
    _aniC.dispose();
    super.onClose();
  }
}

What I want (Flutter Official Demo):

Official Demo

My(No reset animation):

No Reset Animation

The drag animation has been implemented, but I still cannot implement the reset animation of the physical simulation _aniC.animateWith(simulation)

My guess: _aniC<AnimationController> is already executing animateWith(simulation), but does not synchronize the _animation.value.value to _dragAlignment.value, I've tried addListener(){} but that didn't work :(


Solution

  • I have solved it by using GetX,below code:

    import 'package:flutter/material.dart';
    import 'package:flutter/physics.dart';
    import 'package:get/get.dart';
    
    
    class HomeDemo extends StatelessWidget {
      const HomeDemo({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return  Scaffold(
          body: DraggableCardView(),
        );
      }
    }
    
    class DraggableCardController extends GetxController with GetSingleTickerProviderStateMixin  {
      final Rx<Alignment> _dragAlignment = Rx<Alignment>(Alignment.center);
    
      Alignment get dragAlignment => _dragAlignment.value;
    
      set dragAlignment(Alignment value) => _dragAlignment.value = value;
    
      late AnimationController _controller;
      late Animation<Alignment> _animation;
    
      @override
      void onInit() {
        _controller = AnimationController(vsync: this);
        _controller.addListener(() {
          _dragAlignment.value = _animation.value;
        });
        super.onInit();
      }
    
      void runAnimation(Offset pixelsPerSecond, Size size) {
        _animation = _controller.drive(
          AlignmentTween(
            begin: dragAlignment,
            end: Alignment.center,
          ),
        );
    
        final unitsPerSecondX = pixelsPerSecond.dx / size.width;
        final unitsPerSecondY = pixelsPerSecond.dy / size.height;
        final unitsPerSecond = Offset(unitsPerSecondX, unitsPerSecondY);
        final unitVelocity = unitsPerSecond.distance;
    
        const spring = SpringDescription(
          mass: 30,
          stiffness: 1,
          damping: 1,
        );
    
        final simulation = SpringSimulation(spring, 0, 1, -unitVelocity);
    
        _controller.animateWith(simulation);
      }
    
      @override
      void dispose() {
        _controller.dispose();
        super.dispose();
      }
    }
    
    class DraggableCardView extends StatelessWidget {
      final DraggableCardController controller = Get.put(DraggableCardController());
    
       DraggableCardView({super.key});
    
      @override
      Widget build(BuildContext context) {
        final size = MediaQuery.of(context).size;
        return GestureDetector(
          onPanDown: (details) {
            controller._controller.stop();
          },
          onPanUpdate: (details) {
            controller.dragAlignment += Alignment(
              details.delta.dx / (size.width / 2),
              details.delta.dy / (size.height / 2),
            );
          },
          onPanEnd: (details) {
            controller.runAnimation(details.velocity.pixelsPerSecond, size);
          },
          child: Obx(
            () => Align(
              alignment: controller.dragAlignment,
              child: const Card(
                child: FlutterLogo(
                  size: 128,
                ),
              ),
            ),
          ),
        );
      }
    }
    

    I hope this will help you.