Animasi pada Flutter

Pemrograman Mobile 2

 

 

Muhamad Saad Nurul Ishlah, M.Comp.

Dept. Sistem Informasi & Dept. Ilmu Komputer, Universitas Pakuan

Agenda Kuliah

  • Drawing-based vs Code-based Animation
  • Implicit Animation
  • Explicit Animation
  • Memilih pendekatan Animasi yang sesuai
  • Package animasi 

Drawing-based vs Code-based Animation

Drawing-based Animation

  • Drawing-based Animation
    • Animated graphics
    • Vectors
    • Karakter
    • Sesuatu yang "digambar" dan kemudian dianimasikan

https://rive.app/community/3411-7160-safe-icon/

Code-based Animation

  • Code-based Animation
    • Widget
    • Layout
    • Styles
    • dll

https://medium.com/flutter-community/flutter-animations-comprehensive-guide-cb93b246ca5d

Animasi Implisit

(Implicit Animation)

Implicit Animation

  • Animasi implisit dengan widget yang siap digunakan
    • Widget animasi bawaan
  • Animasi implisit dengan TweenAnimationBuilder
    • Menambahkan Animasi pada widget apapun

Implicit Animation - Ready-to-use Widgets`

  • Widget disebut AnimatedFoo
  • Foo menunjuk pada Properti yang dapat dianimasikan
  • Contoh:
    • Container > AnimatedContainer
    • Padding > AnimatedContainer
    • Positioned > AnimatedPositioned
    • dll

Implicit Animation - Ready-to-use Widgets

  • Animasi di sebelah menggunakan
    • AnimatedContainer
    • AnimatedPositioned
    • AnimatedDefaultTextStyle

Implicit Animation - Ready-to-use Widgets

AnimatedPositioned(
  top: selectedItemIndex * itemHeight,
  left: 0,
  right: 0,
  duration: const Duration(milliseconds: 200),
  curve: Curves.easeInOut,
  child: //...
),


//...
AnimatedContainer(
  duration: const Duration(milliseconds: 200),
  curve: Curves.easeInOut,
  decoration: BoxDecoration(
    color: selectedItemIndex == i ? yellow : pink,
    border: Border.all(
      color: selectedItemIndex == i
          ? Colors.white
          : Colors.transparent,
      width: 2,
    ),
  ),
  child: AnimatedDefaultTextStyle(
    duration: const Duration(milliseconds: 200),
    style: TextStyle(
      color: selectedItemIndex == i
          ? Colors.black
          : Colors.white,
    ),
    child: const Text('Featured!'),
  ),
),

// dibungkus dalam "InkWell"
onTap: () => setState(() => selectedItemIndex = i),

Implicit Animation - Ready-to-use Widgets

Daftar Widget AnimatedFoo

  • AnimatedAlign

  • AnimatedContainer

  • AnimatedDefaultTextStyle

  • AnimatedOpacity

  • AnimatedPadding

 

  • AnimatedPhysicalModel

  • AnimatedPositioned

  • AnimatedPositionDirectional

  • AnimatedSize

Bagaimana jika ingin menganimasikan widget lain yang tidak terdapat pada list?

Implicit Animation - TweenAnimationBuilder

  • TweenAnimationBuilder memungkinkan untuk melakukan animasi terhadap property dari widget apapun menggunakan kelas Tween
  • Tween diambil dari "Between" - In Between Animation
  • Memberikan nilai awal dan akhir - begin dan end

Implicit Animation - TweenAnimationBuilder

TweenAnimationBuilder(
  duration: const Duration(milliseconds: 200),
  tween: Tween<double>(begin: 0.01, end: _sliderValue),
  child: Container(
    decoration: BoxDecoration(
      //...
    ),
    child: Slider(
      value: _sliderValue,
      min: 0.01,
      onChanged: (value) {
        setState(() => _sliderValue = value);
      },
    ),
  ),
  builder: (BuildContext context, double? value, Widget? child) {
    return ClipRect(
      child: BackdropFilter(
        filter: ImageFilter.blur(
          sigmaX: 40 * (value ?? 0.01),
          sigmaY: 40 * (value ?? 0.01),
        ),
        child: child,
      ),
    );
  },
);

Animasi Eksplisit

(Explicit Animation)

Explicit Animation

  • Implicit Animation (AnimatedFoo & TweenAnimationBuilder) memulai animasi secara otomatis dengan mengubah nilai property tertentu
  • Explicit Animation memulai animasi jika diminta secara eksplisit (explicit)
  • AnimationController digunakan untuk memulai dan mengontrol animasi
  • Explicit Animation juga memiliki widget yang siap digunakan (ready-to-use/bawaan) dan widget yang perlu kustomisasi

 

AnimationController

  • AnimationController memungkinkan kita untuk mengontrol animasi
  • memerlukan property vsync (TickerProvider)
  • Ticker pada dasarnya melacak rendering frame Flutter dan memungkinkan pengontrol untuk mengikuti ticker itu dan melakukan animasi melaluinya dalam durasi yang ditentukan, sementara secara linier menghasilkan nilai antara nilai lowerBound dan upperBound yang secara default 0 & 1.

 

AnimationController({
  double? value,
  this.duration,
  this.reverseDuration,
  this.debugLabel,
  this.lowerBound = 0.0,
  this.upperBound = 1.0,
  this.animationBehavior = AnimationBehavior.normal,
  required TickerProvider vsync,
})

AnimationController

Callback Function Fungsi
forward() menjalankan animasi secara maju
reverse() menjalankan animasi secara mundur
stop() menghentikan animasi
repeat() mengulang animasi selama terlihat
reset() mengembalikan animasi ke nilai lowerBound
Menset nilai animasi
Mengakses beberapa nilai getter untuk mengetahui state dari animasi, seperti: isAnimating, isComplete, isDismissed, dll

Explicit Animation - Widget Bawaan

  • Widget bawaan dengan pola FooTransition
  • Foo menunjuk pada property yang dapat dianimasikan
  • Contoh:
    • AlignTransition
    • PositionedTransition

Explicit Animation - Widget Bawaan

class ExplicitAnimations extends StatefulWidget {
  const ExplicitAnimations({Key? key}) : super(key: key);

  @override
  State<ExplicitAnimations> createState() => _ExplicitAnimationsState();
}

class _ExplicitAnimationsState extends State<ExplicitAnimations>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late final Animation<AlignmentGeometry> _alignAnimation;
  late final Animation<double> _rotationAnimation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(milliseconds: 800),
      vsync: this,
    )..repeat(reverse: true);

    _alignAnimation = Tween<AlignmentGeometry>(
      begin: Alignment.centerLeft,
      end: Alignment.centerRight,
    ).animate(
      CurvedAnimation(
        parent: _controller,
        curve: Curves.easeInOutCubic,
      ),
    );

    _rotationAnimation = Tween<double>(begin: 0, end: 2).animate(
      CurvedAnimation(
        parent: _controller,
        curve: Curves.easeInOutCubic,
      ),
    );
  }

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

  @override
  Widget build(BuildContext context) {
    return BlurContainer(
      containerHeight: 200,
      child: AlignTransition(
        alignment: _alignAnimation,
        child: RotationTransition(
          turns: _rotationAnimation,
          child: const Rectangle(
            color1: pink,
            color2: pinkDark,
            width: 50,
            height: 50,
          ),
        ),
      ),
    );
  }
}

Explicit Animation - Bawaan

Daftar Widget FooTransition

  • AlignTransition

  • DecoratedBoxTransition

  • DefaultTextStyleTransition

  • FadeTransition

  • PositionedTransition

  • RelativePositionedTransition

 

  • RotationTransition

  • ScaleTransition

  • SizeTransition

  • SlideTransition

  • StatusTransitionWidget

Bagaimana jika ingin menganimasikan widget lain yang tidak terdapat pada list?

Explicit Animation

Tidak terdapat GradientTransition, tapi bisa dibuat dengan AnimatedBuilder & AnimatedWidget

Explicit Animation - AnimationBuilder

class AnimatedBuilderExample extends StatefulWidget {
  const AnimatedBuilderExample({Key? key}) : super(key: key);

  @override
  _AnimatedBuilderExampleState createState() => _AnimatedBuilderExampleState();
}

class _AnimatedBuilderExampleState extends State<AnimatedBuilderExample>
    with SingleTickerProviderStateMixin {
  late final AnimationController _controller;

  @override
  void initState() {
    _controller = AnimationController(
      duration: const Duration(milliseconds: 500),
      vsync: this,
    )..repeat(reverse: true);
    super.initState();
  }

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

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _controller,
      builder: (BuildContext context, Widget? child) {
        return Container(
          height: 100,
          decoration: BoxDecoration(
            gradient: LinearGradient(
              colors: const [purple, pink, yellow],
              stops: [0, _controller.value, 1],
            ),
            borderRadius: BorderRadius.circular(15),
            border: Border.all(color: Colors.white),
          ),
        );
      },
    );
  }
}

Explicit Animation - AnimatedWidget

  • AnimatedWidget merupakan Widget yang dibuat dengan menurunkan StatefulWidget

 

Explicit Animation - AnimatedWidget

  • AnimatedWidget merupakan Widget yang dibuat dengan menurunkan StatefulWidget
  • GradientTransition menggunakan AnimatedWidget

 

Explicit Animation - AnimationWidget

class GradientTransition extends AnimatedWidget {
  final Animation<double> stop;

  const GradientTransition({
    Key? key,
    required this.stop,
  }) : super(key: key, listenable: stop);

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 100,
      decoration: BoxDecoration(
        gradient: LinearGradient(
          colors: const [purple, pink, yellow],
          stops: [0, stop.value, 1],
        ),
      ),
    );
  }
}

// Kemudian gunakan dengan mempassing AnimationController
GradientTransition(stop: _controller),

Demo Kode

Contoh Implementasi Animasi

  • GitHub: https://github.com/flutter/samples/tree/main/animations 
  • Dartpad: https://dartpad.dev/?id=c9bb2627d4f38319bab7dc456c5092cd

Memilih Pendekatan Animasi yang Sesuai

Memilih Drawing atau Code

  • Apakah bentuk Animasi seperti gambar grafik (2D, 3D)?
    • Gunakan Drawing-based animation 3rd party package
    • Rive, Lottie, dll 
  • Apakah animasi berkaitan dengan layout, widget, style widget, warna, border, text, dll?
    • Gunakan Code-based animation

 

Memilih Implicit atau Explicit

  • Apakah animasi berulang ketika visible?
  • Apakah animasi berbentuk discontinuous animation (animasi tidak kembali ke tempat mulai)?
  • Apakah terdapat multiple widget yang dianimasikan?

 

Jika jawaban untuk semua pertanyaan tersebut adalah ya, maka gunakan Explicit Animation

 

Memilih Widget Bawaan atau Kustom

  • Apakah sudah terdapat widget bawaan untuk menganimasikan?
    • Jika ya, gunakan widget bawaan
      • AnimatedFoo
      • FooTransition
    • Jika tidak, gunakan widget kustom
      • AnimationController
      • AnimationBuilder
      • AnimatedWidget

 

Memilih Widget Bawaan atau Kustom

Memilih Implicit atau Explicit

Memilih Implicit atau Explicit

Diskusi

Ada pertanyaan?

Referensi

Terima Kasih

Animasi pada Flutter

By M. Saad Nurul Ishlah

Animasi pada Flutter

  • 160