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):
My(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 :(
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.