Android memiliki fitur yang sangat menarik dari animasi View yang disebut CircularRevealAnimation
- secara harfiah "pengungkapan sirkuler." Flutter, pada gilirannya, meskipun memiliki kemampuan yang kaya untuk menjiwai widget, tidak menyediakan animasi seperti itu di luar kotak.

Artikel ini akan menunjukkan cara menerapkan animasi tersebut menggunakan Flutter dan menerbitkan perpustakaan di pub.dev untuk akses dan distribusi yang mudah.
Implementasi animasi
Di Flutter, semuanya adalah widget. Dan animasi tidak terkecuali. Oleh karena itu, kami membuat kelas CircularRevealAnimation
, yang akan memperluas kelas StatelessWidget
.
Memulai, menghentikan, dan kontrol animasi lainnya dilakukan menggunakan AnimationController
. Untuk membuat AnimationController
Anda harus mewarisi dari StatefulWidget
dan menambahkan kelas khusus SingleTickerProviderStateMixin
ke State
.
CircularRevealAnimation
kelas animasi kami tidak akan bertanggung jawab untuk mengelola animasi sendiri, tetapi akan menerima Animation<double>
sebagai parameter konstruktor yang diperlukan, sehingga tidak perlu mewarisi dari StatefulWidget
. Ini dilakukan agar CircularRevealAnimation
dapat dengan mudah digabungkan dengan animasi lain yang menggunakan AnimationController
sama. Misalnya, gabungkan animasi pengungkapan dengan animasi perubahan transparansi.
Parameter penting lain dari konstruktor CircularRevealAnimation
adalah child
, yang merupakan widget anak dari animasi kami, dan yang akan muncul atau hilang. Secara umum, di Flutter, banyak widget memiliki parameter turunan. Widget tersebut memungkinkan Anda untuk mengubah perilaku, rendering, atau tata letak widget anak. Atau tambahkan animasi, seperti yang terjadi dengan CircularRevealAnimation
.
Selain itu, untuk menentukan animasi, parameter seperti pusat pembukaan (atau penutup) animasi, serta radius minimum dan maksimum pembukaan, akan diperlukan. Parameter ini opsional dan dapat ditentukan sebagai null
atau tidak ditentukan sama sekali saat membuat animasi. Dalam hal ini, nilai default akan digunakan: pusat pengungkapan akan berada di tengah widget, jari-jari minimum akan dipotong menjadi nol, dan jari-jari maksimum akan sama dengan jarak dari pusat pengungkapan ke titik puncak widget yang paling jauh dari pusat pengungkapan.
Algoritma perhitungan radius maksimum default adalah sebagai berikut. Pertama, jarak horizontal dan vertikal dari pusat ke titik terjauh dari pusat pengungkapan dihitung, dan kemudian diagonal dihitung oleh teorema Pythagoras.
static double calcMaxRadius(Size size, Offset center) { final w = max(center.dx, size.width - center.dx); final h = max(center.dy, size.height - center.dy); return sqrt(w * w + h * h); }
Sekarang Anda perlu menerapkan pemotongan widget di dalam lingkaran selama rendering. Kelas ClipPath
akan membantu kami dalam hal ini, yang memungkinkan Anda untuk memotong widget berdasarkan templat yang sewenang-wenang. Sebagai parameter, widget ini dilewati clipper
(sedikit lebih lambat) dan child
adalah widget anak yang perlu dipangkas.
Parameter clipper
dari widget ClipPath
menentukan bagaimana widget anak akan dipangkas. Untuk membuat template pemangkasan Anda sendiri, buat kelas CircularRevealClipper
, yang mewarisi kelas CustomClipper<Path>
dan mendefinisikan kembali metode Path getClip(Size size)
. Metode ini mengembalikan Path
membatasi area tanam. Dalam kasus kami, wilayah ini adalah lingkaran dengan pusat yang diberikan. Untuk menghitung jari-jari lingkaran, Anda perlu mengetahui nilai animasi saat ini. Nilai ini diteruskan ke CircularRevealClipper
sebagai parameter fraction
. Jari-jari lingkaran dihitung menggunakan interpolasi linier antara jari-jari minimum dan maksimum.
Setelah itu, mari kita beralih ke implementasi widget. Lebih mudah menggunakan AnimatedBuilder
untuk membuat animasi. Konstruktor AnimatedBuilder
menerima objek Animation<double>
dan builder
digunakan untuk membuat widget berdasarkan nilai animasi saat ini. Dalam builder
kami membuat ClipPath
dan meneruskan nilai animasi ( fraction
) saat ini ke CircularRevealClipper
.
class CircularRevealAnimation extends StatelessWidget { ... @override Widget build(BuildContext context) { return AnimatedBuilder( animation: animation, builder: (BuildContext context, Widget _) { return ClipPath( clipper: CircularRevealClipper( fraction: animation.value, center: center, minRadius: minRadius, maxRadius: maxRadius, ), child: this.child, ); }, ); } }
Ini melengkapi pembuatan CircularRevealAnimation
. Masih menggunakannya. Untuk melakukan ini, buat StatefulWidget
, AnimationController
dan berikan AnimationController
ke CircularRevealAnimation
.
Contoh penggunaan import 'package:flutter/material.dart'; import 'package:circular_reveal_animation/circular_reveal_animation.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'CRA Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(), ); } } class MyHomePage extends StatefulWidget { @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin { AnimationController animationController; Animation<double> animation; @override void initState() { super.initState(); animationController = AnimationController( vsync: this, duration: Duration(milliseconds: 1000), ); animation = CurvedAnimation( parent: animationController, curve: Curves.easeIn, ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("CRA Demo"), ), body: Padding( padding: const EdgeInsets.all(16.0), child: CircularRevealAnimation( minRadius: 12, maxRadius: 200, center: Offset(0, 300), child: Container(color: Colors.red), animation: animation, ), ), floatingActionButton: FloatingActionButton(onPressed: () { if (animationController.status == AnimationStatus.forward || animationController.status == AnimationStatus.completed) { animationController.reverse(); } else { animationController.forward(); } }), ); } }
Aplikasi demo Github .
Pembuatan perpustakaan
Untuk membuat perpustakaan Dart atau Flutter, Anda perlu menambahkan file pubspec.yaml
ke direktori yang sama dengan direktori lib
dengan file Dart. File ini berisi deskripsi perpustakaan, informasi tentang penulis dan dependensi.
Ini juga merupakan praktik yang baik untuk membuat file yang mendefinisikan API publik. File ini terletak di folder lib
dan termasuk nama perpustakaan dan daftar file yang perlu dimasukkan dalam API publik. Semua file Dart lainnya ditempatkan di direktori src
. Ini tidak hanya menyembunyikan file yang tidak termasuk dalam API publik, tetapi juga memungkinkan Anda untuk mengimpor pustaka menggunakan ekspresi import
tunggal. Isi file ini:
library circular_reveal_animation; export 'package:circular_reveal_animation/src/circular_reveal_animation.dart';
Anda dapat membaca lebih lanjut tentang membuat perpustakaan di Dart di sini .
Menerbitkan perpustakaan Dart ke pub.dev sangat sederhana. Yang perlu Anda lakukan adalah menjalankan perintah flutter packages pub publish
dari direktori root perpustakaan. Penerbitan dilakukan atas nama akun Google, jadi selama proses publikasi tautan akan diberikan yang harus dibuka di browser dan masuk ke Google. Selanjutnya, pembaruan hanya dapat dipublikasikan menggunakan akun yang atas nama versi pertama diposting.
Sebelum menerbitkan, Anda disarankan untuk memverifikasi bahwa perpustakaan benar menggunakan flutter packages pub publish --dry-run
.
Setelah menjalankan flutter packages pub publish
perpustakaan akan segera tersedia di pub.dev. Dan, seperti yang tertulis dalam dokumentasi, "Penerbitan selamanya" - nanti Anda hanya dapat mengunggah versi baru. Versi yang lebih lama juga akan tersedia.
Meskipun penerbitan perpustakaan terlihat sederhana, ia juga dapat memiliki jebakan. Misalnya, ketika menerbitkan versi pertama, saya dikeluarkan beberapa poin di peringkat karena deskripsi perpustakaan (di pubspec.yaml
) terlalu pendek.
Anda dapat membaca lebih lanjut tentang menerbitkan perpustakaan di sini .
Sebenarnya, perpustakaan circular_reveal_animation
di pub.dev dan github.com .
PS: Saya menggunakan ```java {...} ```
untuk menyorot kode Dart. Alangkah baiknya menambahkan highlight kode Dart di habr.com.