Flutter Animation – Hukum Fisika Pada Animasi Widget

Untuk teman-teman yang akan baru memulai belajar Flutter silahkan masuk ke artikel ini dulu ya teman-teman ➡ Aplikasi Pertamaku “Halo Semuaaa…“. Jika sudah yuk lanjut baca artikel ini…

Jangan lupa baca artikel sebelumnya ya teman-temanMembuat Animasi Transisi Route.


Simulasi fisika dapat memberikan pengalaman interaksi pengguna pada aplikasi sehingga terasa realistis dan interaktif. Dalam hal ini, kita akan menganimasikan widget agar berfungsi seolah-olah widget tersebut terpasang ke pegas atau jatuh kembali karena tarikan gravitasi. Langkah-langkahnya sebagai berikut:

  1. Melakukan Set Up pada Animation Controller.
  2. Menggeser widget menggunakan gestures.
  3. Membuat animasi widget.
  4. Menghitung kecepatan untuk mensimulasikan gerakan pegas.

1. Melakukan Set Up pada Animation Controller

Dimulai dengan stateful widget yang disebut DraggableCard:

Langkah selanjutnya, buat kelas _DraggableCardState menjadi extend dari SingleTickerProviderStateMixin. Kemudian buat AnimationController di dalam initState dan set vsync ke this.

(Modifikasi dan tambahkan cuplikan kode berikut pada kelas _DraggableCardState)

class _DraggableCardState extends State<DraggableCard>
    with SingleTickerProviderStateMixin {
  AnimationController _controller;

  @override
  void initState() {
    _controller =
        AnimationController(vsync: this, duration: Duration(seconds: 1));
    super.initState();
  }


  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
  //...

2. Menggeser Widget Menggunakan Gestures

Agar sebuah widget dapat digerakkan saat digeser, maka kita perlu menambahkan field Alignment ke kelas _DraggableCardState:

Alignment _dragAlignment = Alignment.center;

Selanjutkan kita tambahkan GestureDetector yang fungsinya untuk menangani callback onPanDownonPanUpdate, danonPanEndSupaya dapat mengatur alignment, maka MediaQuery untuk mendapatkan ukuran widget, dan bagi dengan nilai 2 (fungsinya untuk mengonversi unit “piksel yang ditarik” ke koordinat yang digunakan Align). Kemudian, set alignment widget Align ke _dragAlignment:

(Modifikasi dan tambahkan cuplikan kode berikut pada kelas _DraggableCardState)

@override
Widget build(BuildContext context) {
  var size = MediaQuery.of(context).size;
  return GestureDetector(
    onPanDown: (details) {},
    onPanUpdate: (details) {
      setState(() {
        _dragAlignment += Alignment(
          details.delta.dx / (size.width / 2),
          details.delta.dy / (size.height / 2),
        );
      });
    },
    onPanEnd: (details) {},
    child: Align(
      alignment: _dragAlignment,
      child: Card(
        child: widget.child,
      ),
    ),
  );
}

3. Membuat Animasi Widget

Nantinya saat widget digeser kemudian dilepas, akan memberikan efek spring yang membuat widget tersebut kembali ke tengah.

Selanjutnya tambahkan field Animation<Alignment> dan method _runAnimation fungsinya untuk mendefinisikan Tween yang menginterpolasi antara titik tempat widget ditarik ke titik di tengah.

(Modifikasi dan tambahkan cuplikan kode berikut pada kelas _DraggableCardState)

  Animation<Alignment> _animation;

  void _runAnimation() {
    _animation = _controller.drive(
      AlignmentTween(
        begin: _dragAlignment,
        end: Alignment.center,
      ),
    );
   _controller.reset();
   _controller.forward();
  }

Selanjutnya, modifikasi _dragAlignment ketika AnimationController menghasilkan nilai:

@override
void initState() {
  super.initState();
  _controller = AnimationController(vsync: this, duration: Duration(seconds: 1));
  _controller.addListener(() {
    setState(() {
      _dragAlignment = _animation.value;
    });
  });
}

Lalu, buat widget Align yang di dalamnya menggunakan field  _dragAlignment:

child: Align(
  alignment: _dragAlignment,
  child: Card(
    child: widget.child,
  ),
),

Terakhir, modifikasi GestureDetector untuk mengelola animation controller:

onPanDown: (details) {
 _controller.stop();
},
onPanUpdate: (details) {
 setState(() {
   _dragAlignment += Alignment(
     details.delta.dx / (size.width / 2),
     details.delta.dy / (size.height / 2),
   );
 });
},
onPanEnd: (details) {
 _runAnimation();
},

4. Menghitung Kecepatan Untuk Mensimulasikan Gerakan Pegas

Langkah terakhir adalah melakukan sedikit penghitungan kecepatan widget setelah selesai digeser, tujuannya agar widget melanjutkan gerakan secara realistis pada kecepatan tertentu sebelum widget dilepas kembali. (Method _runAnimation sudah memiliki sets arah dengan menyetel animasi alignment awal dan akhir)

Pertama, lakukan imporphysics package:

import 'package:flutter/physics.dart';

Perlu teman-teman ketahui, callback onPanEnd menyediakan objek DragEndDetails yang befungsi untuk memberikan kecepatan pointer saat berhenti menyentuh layar. Kecepatannya memiliki satuan piksel per detik, tetapi widget Align tidak menggunakan satuan piksel melainkan menggunakan nilai koordinat antara [-1.0, -1.0] dan [1.0, 1.0], di mana [0.0, 0.0] mewakili titik pusat.  Size yang dihitung pada langkah 2 digunakan untuk mengubah piksel menjadi nilai koordinat pada range yang sudah ditentukan.

Selanjutnya, AnimationController memiliki method animateWith() yang dapat diberikan class SpringSimulation:

void _runAnimation(Offset pixelsPerSecond, Size size) {
  _animation = _controller.drive(
    AlignmentTween(
      begin: _dragAlignment,
      end: Alignment.center,
    ),
  );
  // Calculate the velocity relative to the unit interval, [0,1],
  // used by the animation controller.
  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);
}

Jangan lupa untuk memanggil _runAnimation() yang di dalamnya terdapat velocity dan size:

onPanEnd: (details) {
  _runAnimation(details.velocity.pixelsPerSecond, size);
},

Berikut cuplikan kode dan simulasinya, jika teman-teman menggunakan VSCode jalankan projectnya dengan menekan F5, klik hot reload (⚡) atau klik tombol ▶, maka berikut hasil akhirnya (klik logo Flutter dan geser secara random ke kiri, kanan, atas dan bawah lalu lepas logo tersebut):


Jika ada pertanyaan silahkan komen dan jika artikel ini dirasa bermanfaat, jangan lupa like dan sharenya ya teman-teman. ??????? Sampai bertemu di artikel selanjutnya.
Terima Kasih, Assalamu’alaykum… Salam KODINGINDONESIA

Referensi : https://flutter.dev//

Anton Prafanto

Konten developer kodingindonesia.com & staf pengajar tetap di Universitas Mulawarman Samarinda

all author posts