Flutter – Cách tạo animated icon

Trong bài viết này, chúng ta sẽ học đc làm thế nào để tạo một animated icon bằng cách dùng AnimatedIcon widget và AnimatedIcons trong Flutter framework.

1. Create empty screen

import 'package:flutter/material.dart';

void main() => runApp(AnimatedIconDemo());

class AnimatedIconDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Coflutter - AnimatedIcon ',
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Coflutter - AnimatedIcon'),
          backgroundColor: const Color(0xffae00f0),
        ),
        body: AnimatedIconDemoScreen(),
      ),
    );
  }
}

class AnimatedIconDemoScreen extends StatefulWidget {
  @override
  _AnimatedIconDemoScreenState createState() =>
                 _AnimatedIconDemoScreenState();
}

class _AnimatedIconDemoScreenState extends
State<AnimatedIconDemoScreen> {

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

Đoạn code trên đơn giản là tạo 1 màn hình trống chứa một appbar với title “AnimatedIcon”

animated icon

2. Prepare and build AnimatedIcon

AnimatedIcon widget yêu cầu 2 thuộc tính bắt buộc và cung cấp thêm 1 vài thuộc tính tuỳ chọn:

  • (Required) AnimatedIconData icon: icon hiển thị. Hiện tại danh sách icons được định nghĩa sẵn cho phép chúng ta sử dụng.
  • (Required) Animation<double> progress: quản lý animation cho animated icon.
  • (Optional) color: màu sắc của icon.
  • (Optional) size: kích thước của icon.

Theo đó, animated icon sẽ được viết như sau:

AnimatedIcon(
    size: 100,
    color: Colors.blue,
    icon: AnimatedIcons.close_menu,
    progress: _animationController,
)

// With

_animationController =
        AnimationController(vsync: this, 
        duration: Duration(milliseconds: 500));

Cùng cập nhật code (from step 1) bằng việc thêm AnimatedIcon vào màn hình:

// ... Keep other old code

// Update _AnimatedIconDemoScreenState class
class _AnimatedIconDemoScreenState 
    extends State<AnimatedIconDemoScreen>
    // New: Ticker for animation
    with SingleTickerProviderStateMixin {
  
  // New
  AnimationController _animationController;

  // New: Initialize animation controller
  @override
  void initState() {
    super.initState();

    // Create controller
    _animationController =
        AnimationController(vsync: this, 
        duration: Duration(milliseconds: 500));
  }

  // New: Release controller
  @override
  void dispose() {
    _animationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // New: Add animated icon at the center of screen
    return Center(
      child: AnimatedIcon(
        size: 100,
        color: Colors.blue,
        icon: AnimatedIcons.close_menu,
        progress: _animationController,
      ),
    );
  }
}

Ở chính giữa màn hình, chúng ta thấy kết quả 1 icon nhưng chưa thể kiểm chứng đc hiệu ứng của icon. Vậy nên bước tiếp theo chúng ta cần thêm hiệu ứng chuyển động khi user tapping vào icon.

3. Thêm hiệu ứng animation

Ở đây chúng ta sẽ bọc icon vào bên trong 1 InkWell widget và xử lý sự kiện onTap, kết hợp một biến bool để quản lý trạng thái hiện tại của icon:

class _AnimatedIconDemoScreenState extends
          State<AnimatedIconDemoScreen>
          with SingleTickerProviderStateMixin {
  AnimationController _animationController;
  // New: icon states: show menu vs. show close.
  bool isShowingMenu = false;

  @override
  void initState() {
    super.initState();

    _animationController =
        AnimationController(vsync: this, 
          duration: Duration(milliseconds: 500));
  }

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

  @override
  Widget build(BuildContext context) {
    return Center(
      // New: Wrap icon to handle onTap event
      child: InkWell(
        child: AnimatedIcon(
          size: 100,
          color: Colors.blue,
          icon: AnimatedIcons.close_menu,
          progress: _animationController,
        ),
        // New: handle onTap event
        onTap: () {
          // Update the state 
          isShowingMenu = !isShowingMenu;
          if (isShowingMenu) {
            // Trigger animation (start: close -> menu)
            _animationController.forward();
          } else {
            // Trigger animation (revert: menu -> close)
            _animationController.reverse();
          }
        },
      ),
    );
  }
}

Hoàn thành!

Dưới đây là toàn bộ code trong trường hợp bạn mong muốn trải nghiệm:

import 'package:flutter/material.dart';

void main() => runApp(AnimatedIconDemo());

class AnimatedIconDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'AnimatedIcon ',
      home: Scaffold(
        appBar: AppBar(
          title: const Text('AnimatedIcon'),
          backgroundColor: const Color(0xffae00f0),
        ),
        body: AnimatedIconDemoScreen(),
      ),
    );
  }
}

class AnimatedIconDemoScreen extends StatefulWidget {
  @override
  _AnimatedIconDemoScreenState createState() => _AnimatedIconDemoScreenState();
}

class _AnimatedIconDemoScreenState extends
State<AnimatedIconDemoScreen>
    with SingleTickerProviderStateMixin {
  AnimationController _animationController;
  // New: icon states: show menu vs. show close.
  bool isShowingMenu = false;

  @override
  void initState() {
    super.initState();

    _animationController =
        AnimationController(vsync: this,
            duration: Duration(milliseconds: 500));
  }

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

  @override
  Widget build(BuildContext context) {
    return Center(
      // New: Wrap icon to handle onTap event
      child: InkWell(
        child: AnimatedIcon(
          size: 100,
          color: Colors.blue,
          icon: AnimatedIcons.close_menu,
          progress: _animationController,
        ),
        // New: handle onTap event
        onTap: () {
          // Update the state
          isShowingMenu = !isShowingMenu;
          if (isShowingMenu) {
            // Trigger animation (start: close -> menu)
            _animationController.forward();
          } else {
            // Trigger animation (revert: menu -> close)
            _animationController.reverse();
          }
        },
      ),
    );
  }
}

Related Posts