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-teman ➡ Membuat Efek Scrolling Parallax
Satu layar aplikasi mungkin berisi banyak animasi, namun memutar semua animasi tersebut pada saat yang sama dapat membuat pengguna kebingungan tapi memutar animasi satu persatu pun dapat memakan waktu. Jalan terbaik adalah mengatur animasi tersebut secara bergantian (Staggered Effect). Setiap animasi dimulai pada waktu yang berbeda, tetapi animasi tersebut tumpang tindih untuk membuat durasi yang lebih pendek. Pada artikel ini, kita akan membuat menu dengan konten animasi staggered dan memiliki tombol yang muncul di bagian bawahnya. Contohnya sebagai berikut:
Berikut langkah-langkah untuk membuatnya:
1. Buat Menu tanpa Animasi
Drawer menu menampilkan list judul, yang kemudian diikuti dengan tombol “Get Started” pada bagian bawah menu.
Selanjutnya kita definisikan statefull widget Menu
yang menampilkan list dan tombol pada lokasi statis.
class Menu extends StatefulWidget { @override _MenuState createState() => _MenuState(); } class _MenuState extends State<Menu> { static const _menuTitles = [ 'Declarative Style', 'Premade Widgets', 'Stateful Hot Reload', 'Native Performance', 'Great Community', ]; @override Widget build(BuildContext context) { return Container( color: Colors.white, child: Stack( fit: StackFit.expand, children: [ _buildFlutterLogo(), _buildContent(), ], ), ); } Widget _buildFlutterLogo() {...} Widget _buildContent() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 16), ..._buildListItems(), const Spacer(), _buildGetStartedButton(), ], ); } List<Widget> _buildListItems() { final listItems = <Widget>[]; for (var i = 0; i < _menuTitles.length; ++i) { listItems.add( Padding( padding: const EdgeInsets.symmetric(horizontal: 36.0, vertical: 16), child: Text( _menuTitles[i], textAlign: TextAlign.left, style: const TextStyle( fontSize: 24, fontWeight: FontWeight.w500, ), ), ), ); } return listItems; } Widget _buildGetStartedButton() { return SizedBox( width: double.infinity, child: Padding( padding: const EdgeInsets.all(24.0), child: RaisedButton( shape: const StadiumBorder(), color: Colors.blue, padding: const EdgeInsets.symmetric(horizontal: 48, vertical: 14), onPressed: () {}, child: const Text( 'Get Started', style: TextStyle( color: Colors.white, fontSize: 22, ), ), ), ), ); } }
2. Menyiapkan Animasi
Untuk mengontrol waktu dari animasi kita membutuhkan AnimationController
. Oleh karena itu kita tambahkan SingleTickerProviderStateMixin
pada class MenuState
kemudian deklarasikan dan buat instance AnimationController
.
class _MenuState extends State<Menu> with SingleTickerProviderStateMixin { late AnimationController _staggeredController; @override void initState() { super.initState(); _staggeredController = AnimationController( vsync: this, ); } @override void dispose() { _staggeredController.dispose(); super.dispose(); } }
Lamanya delay setiap animasi tergantung berapa lama kita mengaturnya, maka disini kita akan mendefinisikan delay animasi, durasi animasi individual, dan total durasi animasi.
class _MenuState extends State<Menu> with SingleTickerProviderStateMixin { static const _initialDelayTime = Duration(milliseconds: 50); static const _itemSlideTime = Duration(milliseconds: 250); static const _staggerTime = Duration(milliseconds: 50); static const _buttonDelayTime = Duration(milliseconds: 150); static const _buttonTime = Duration(milliseconds: 500); final _animationDuration = _initialDelayTime + (_staggerTime * _menuTitles.length) + _buttonDelayTime + _buttonTime; }
Dalam hal ini, semua animasi memiliki delay hingga 50 milidetik. Setelah itu, list item mulai muncul. Kemunculan setiap list item tertunda selama 50 milidetik setelah list item sebelumnya mulai meluncur masuk. Setiap list item membutuhkan waktu 250 milidetik untuk bergeser dari kanan ke kiri. Setelah list item terakhir mulai meluncur masuk, tombol di bagian bawah menunggu 150 milidetik lagi untuk muncul sehingga animasi tombol membutuhkan waktu 500 milidetik.
Dengan ditentukannya setiap delay dan durasi animasi, kemudian durasi total dihitung sehingga dapat digunakan untuk menghitung waktu animasi individual. Berikut waktu animasi yang ditunjukkan pada diagram berikut:
Flutter menyediakan kelas Interval dan interval sendiri membutuhkan persentase waktu mulai dan persentase waktu berakhir. Interval tersebut kemudian dapat digunakan untuk menganimasikan nilai antara waktu mulai dan akhir tersebut. Misalnya, untuk animasi yang membutuhkan waktu 1 detik, interval animasi tersebut dari 0,2 hingga 0,5 akan dimulai pada 200 milidetik (20%) dan berakhir pada 500 milidetik (50%).
Selanjutnya deklarasikan dan hitung Interval
setiap list item dan Interval
tombol bawah.
class _MenuState extends State<Menu> with SingleTickerProviderStateMixin { final List<Interval> _itemSlideIntervals = []; late Interval _buttonInterval; @override void initState() { super.initState(); _createAnimationIntervals(); _staggeredController = AnimationController( vsync: this, duration: _animationDuration, ); } void _createAnimationIntervals() { for (var i = 0; i < _menuTitles.length; ++i) { final startTime = _initialDelayTime + (_staggerTime * i); final endTime = startTime + _itemSlideTime; _itemSlideIntervals.add( Interval( startTime.inMilliseconds / _animationDuration.inMilliseconds, endTime.inMilliseconds / _animationDuration.inMilliseconds, ), ); } final buttonStartTime = Duration(milliseconds: (_menuTitles.length * 50)) + _buttonDelayTime; final buttonEndTime = buttonStartTime + _buttonTime; _buttonInterval = Interval( buttonStartTime.inMilliseconds / _animationDuration.inMilliseconds, buttonEndTime.inMilliseconds / _animationDuration.inMilliseconds, ); } }
3. Memberikan Animasi pada List Item dan Tombol
Efek animasi staggered akan berjalan ketika menu muncul dan cara memulai animasi tersebut menggunakan initState()
.
@override void initState() { super.initState(); _createAnimationIntervals(); _staggeredController = AnimationController( vsync: this, duration: _animationDuration, )..forward(); }
Setiap list item Each bergeser dari kanan ke kiri dan fade in pada saat bersamaan. Oleh karena itu kita akan menggunakan Interval
pada list item’s dan easeOut
curve untuk menganimasikan opacity dan menerjemahkan setiap list item.
List<Widget> _buildListItems() { final listItems = <Widget>[]; for (var i = 0; i < _menuTitles.length; ++i) { listItems.add( AnimatedBuilder( animation: _staggeredController, builder: (context, child) { final animationPercent = Curves.easeOut.transform( _itemSlideIntervals[i].transform(_staggeredController.value), ); final opacity = animationPercent; final slideDistance = (1.0 - animationPercent) * 150; return Opacity( opacity: opacity, child: Transform.translate( offset: Offset(slideDistance, 0), child: child, ), ); }, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 36.0, vertical: 16), child: Text( _menuTitles[i], textAlign: TextAlign.left, style: const TextStyle( fontSize: 24, fontWeight: FontWeight.w500, ), ), ), ), ); } return listItems; }
Selanjutnya gunakan pendekatan yang sama untuk menganimasikan opacity dan scale dari tombol bawah. Kali ini kita gunakan elasticOut
curve untuk memberi springy effect pada tombol.
Widget _buildGetStartedButton() { return SizedBox( width: double.infinity, child: Padding( padding: const EdgeInsets.all(24.0), child: AnimatedBuilder( animation: _staggeredController, builder: (context, child) { final animationPercent = Curves.elasticOut.transform( _buttonInterval.transform(_staggeredController.value)); final opacity = animationPercent.clamp(0.0, 1.0); final scale = (animationPercent * 0.5) + 0.5; return Opacity( opacity: opacity, child: Transform.scale( scale: scale, child: child, ), ); }, child: RaisedButton( shape: const StadiumBorder(), color: Colors.blue, padding: const EdgeInsets.symmetric(horizontal: 48, vertical: 14), onPressed: () {}, child: const Text( 'Get Started', style: TextStyle( color: Colors.white, fontSize: 22, ), ), ), ), ), ); }
Berikut cuplikan kode dan simulasinya, jika teman-teman menggunakan VSCode jalankan projectnya dengan menekan F5, klik hot reload (⚡) atau klik tombol ▶, berikut tampilannya :
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//