TweenSequence: DJ Phối Nhạc Cho Animation Flutter Của Bạn!
Flutter

TweenSequence: DJ Phối Nhạc Cho Animation Flutter Của Bạn!

Author

Admin System

@root

Ngày xuất bản

23 Mar, 2026

Lượt xem

1 Lượt

"TweenSequenceState"

Chào các đệ tử của anh Creyt! Hôm nay, chúng ta sẽ "bóc tách" một khái niệm mà nhiều bạn trẻ hay lúng túng khi muốn tạo ra những animation "xịn xò" hơn là chỉ đi từ A đến B một cách nhàm chán. Đó chính là TweenSequence – và cái "ruột gan" của nó là TweenSequenceState. Nghe tên có vẻ hàn lâm, nhưng thật ra nó là một ông DJ cực chất, giúp bạn phối nhạc cho các hiệu ứng chuyển động trong app Flutter của mình đấy!

1. TweenSequence là gì và để làm gì? (Và TweenSequenceState là "trái tim" của nó)

Tưởng tượng thế này: Bạn muốn làm một cái animation. Nếu chỉ là đổi màu từ đỏ sang xanh, hay phóng to từ 0 lên 100, thì một cái Tween đơn giản là đủ. Nó như một chuyến bay thẳng từ Hà Nội vào Sài Gòn vậy. Easy game.

Nhưng đời đâu phải lúc nào cũng đơn giản đúng không? Giờ bạn muốn cái app của mình nó 'bay' thế này: Đầu tiên, cái nút nó rung nhẹ một tí, xong nhảy lên một đoạn, rồi phát sáng rực rỡ, xong mới hạ cánh xuống vị trí cuối cùng. Đó, một loạt các hành động nối tiếp nhau, mỗi hành động lại có một 'cá tính' riêng (tốc độ, kiểu chuyển động, thời gian). Nếu dùng Tween đơn lẻ, bạn sẽ phải ngồi canh thời gian, tính toán tùm lum, đau đầu lắm.

Đây chính là lúc TweenSequence xuất hiện như một "vị cứu tinh". Nó cho phép bạn xâu chuỗi nhiều Tween nhỏ lại với nhau thành một chuỗi animation liền mạch. Mỗi Tween nhỏ trong chuỗi được gọi là một TweenSequenceItem. Anh em cứ hình dung TweenSequence như một đạo diễn tài ba, sắp xếp từng cảnh quay (từng TweenSequenceItem) một cách hợp lý để tạo nên một bộ phim (animation) hoàn chỉnh.

Còn TweenSequenceState? À, đó chính là "bộ não" hay "trái tim" của ông đạo diễn TweenSequence này đấy. Nó là cái thứ đứng sau cánh gà, âm thầm quản lý từng giai đoạn của chuỗi, tính toán xem ở thời điểm hiện tại thì cái Tween nào đang chạy, nó chạy được bao nhiêu phần trăm rồi, và trả về giá trị tương ứng. Chúng ta thường không tương tác trực tiếp với TweenSequenceState mà là thông qua TweenSequence thôi, nhưng biết nó tồn tại và làm nhiệm vụ gì thì sẽ giúp bạn hiểu sâu hơn về cách animation của Flutter hoạt động.

2. Code Ví Dụ Minh Hoạ: "Nút Bấm Nhảy Múa"

Để dễ hình dung, anh em mình cùng làm một cái nút bấm nó 'nhảy múa' theo nhiều giai đoạn nhé. Nút này sẽ:

  1. Nhỏ lại một chút.
  2. Phóng to ra một chút.
  3. Đổi màu từ xanh sang vàng.
  4. Cuối cùng là trở về trạng thái ban đầu.

Chúng ta sẽ dùng TweenSequence để điều khiển cả kích thước và màu sắc.

import 'package:flutter/material.dart';

class TweenSequenceDemo extends StatefulWidget {
  const TweenSequenceDemo({super.key});

  @override
  State<TweenSequenceDemo> createState() => _TweenSequenceDemoState();
}

class _TweenSequenceDemoState extends State<TweenSequenceDemo> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _sizeAnimation;
  late Animation<Color?> _colorAnimation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(seconds: 4), // Tổng thời gian animation
    );

    // Định nghĩa sequence cho kích thước
    _sizeAnimation = TweenSequence<double>([
      TweenSequenceItem<double>(
        tween: Tween<double>(begin: 1.0, end: 0.8), // Giai đoạn 1: Nhỏ lại
        weight: 20.0, // Chiếm 20% tổng thời gian
      ),
      TweenSequenceItem<double>(
        tween: Tween<double>(begin: 0.8, end: 1.2), // Giai đoạn 2: Phóng to
        weight: 30.0, // Chiếm 30% tổng thời gian
      ),
      TweenSequenceItem<double>(
        tween: Tween<double>(begin: 1.2, end: 1.0), // Giai đoạn 3: Về kích thước ban đầu
        weight: 50.0, // Chiếm 50% tổng thời gian
      ),
    ]).animate(_controller);

    // Định nghĩa sequence cho màu sắc
    _colorAnimation = TweenSequence<Color?>([
      TweenSequenceItem<Color?>(
        tween: ColorTween(begin: Colors.blue, end: Colors.red), // Giai đoạn 1: Xanh -> Đỏ
        weight: 50.0, // Chiếm 50% tổng thời gian
      ),
      TweenSequenceItem<Color?>(
        tween: ColorTween(begin: Colors.red, end: Colors.green), // Giai đoạn 2: Đỏ -> Xanh lá
        weight: 50.0, // Chiếm 50% tổng thời gian
      ),
    ]).animate(_controller);

    // Lặp lại animation sau khi hoàn thành
    _controller.repeat(reverse: true);
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('TweenSequence Demo')),
      body: Center(
        child: AnimatedBuilder(
          animation: _controller,
          builder: (context, child) {
            return Transform.scale(
              scale: _sizeAnimation.value,
              child: Container(
                width: 100,
                height: 100,
                decoration: BoxDecoration(
                  color: _colorAnimation.value,
                  borderRadius: BorderRadius.circular(20),
                ),
                child: Center(
                  child: Text(
                    'Tap Me!',
                    style: TextStyle(
                      color: Colors.white,
                      fontSize: 16 * _sizeAnimation.value, // Font size cũng thay đổi theo scale
                    ),
                  ),
                ),
              ),
            );
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          if (_controller.isAnimating) {
            _controller.stop();
          } else {
            _controller.repeat(reverse: true);
          }
        },
        child: Icon(_controller.isAnimating ? Icons.pause : Icons.play_arrow),
      ),
    );
  }
}

void main() {
  runApp(const MaterialApp(home: TweenSequenceDemo()));
}

Trong ví dụ trên:

  • Chúng ta tạo ra hai chuỗi TweenSequence độc lập: một cho _sizeAnimation (kích thước) và một cho _colorAnimation (màu sắc).
  • Mỗi TweenSequenceItem có một tween riêng và một weight. weight ở đây không phải là trọng lượng, mà là "tỷ lệ thời gian" mà tween đó chiếm trong tổng thời gian của TweenSequence. Tổng weight của tất cả TweenSequenceItem trong một TweenSequence sẽ được dùng để tính toán tỷ lệ. Ví dụ, nếu tổng weight là 100, thì weight: 20 có nghĩa là tween đó chạy trong 20% tổng thời gian của _controller.
Illustration

3. Mẹo (Best Practices) từ ông Creyt để "quẩy" với TweenSequence

  • weight là chìa khóa, không phải thời gian tuyệt đối: Nhớ nhé, weight là tỷ lệ, không phải số giây. Tổng weight của tất cả TweenSequenceItem trong một TweenSequence sẽ được chuẩn hóa. Ví dụ, nếu bạn có 3 item với weight là 1, 2, 1, thì tổng là 4. Item 1 sẽ chiếm 1/4 thời gian, item 2 chiếm 2/4, item 3 chiếm 1/4. Đừng cố gắng làm tổng weight luôn bằng 100 nếu không cần thiết, cứ để nó tự tính toán.
  • Kết hợp Curve cho mỗi TweenSequenceItem: Mỗi Tween trong TweenSequenceItem có thể có Curve riêng của nó (ví dụ: Curve.easeIn, Curve.bounceOut). Điều này giúp bạn tạo ra các hiệu ứng chuyển động rất "mượt mà" và "có hồn" hơn. Đừng chỉ dùng Linear, hãy thử nghiệm!
  • Dùng AnimatedBuilder: Như ví dụ trên, AnimatedBuilder là cách "chuẩn bài" để lắng nghe sự thay đổi của AnimationController và rebuild widget một cách hiệu quả, tránh setState toàn bộ widget tree không cần thiết.
  • Suy nghĩ "theo chuỗi": Khi nào bạn thấy cần một hiệu ứng mà nó diễn ra theo từng bước, từng giai đoạn rõ ràng, thì TweenSequence chính là ứng cử viên sáng giá. Còn nếu chỉ là một chuyển động đơn giản từ A đến B, thì Tween thường (không có Sequence) sẽ nhẹ nhàng và dễ quản lý hơn.

4. Ứng Dụng Thực Tế: Ai đã dùng TweenSequence rồi?

Nói về ứng dụng thực tế thì nhiều vô kể, anh em cứ để ý mấy cái app hay game đẹp đẽ mà xem:

  • Màn hình Intro/Loading: Khi app khởi động, các icon có thể từ từ hiện ra, phóng to, đổi màu theo một trình tự nhất định.
  • Onboarding Flow: Mấy cái màn hình giới thiệu tính năng lúc mới cài app ấy. Các phần tử UI có thể di chuyển, biến đổi theo từng bước, dẫn dắt người dùng.
  • Game UI/Animations: Trong game, khi nhân vật tung chiêu, nhận sát thương, hay đơn giản là đứng yên (idle animation), các hiệu ứng có thể là một chuỗi các chuyển động nhỏ nối tiếp nhau.
  • Phản hồi người dùng (User Feedback): Khi nhấn một nút, nút đó không chỉ đổi màu mà có thể "nhảy" lên một tí, rung nhẹ, hoặc phát sáng theo một chuỗi. Giúp người dùng cảm thấy "có tương tác" hơn.
  • TikTok-style Transitions: Mấy hiệu ứng chuyển cảnh "mượt mà" và phức tạp giữa các video, các story trên Instagram/Facebook cũng có thể được xây dựng bằng cách xâu chuỗi nhiều animation nhỏ.

5. Thử Nghiệm và Nên Dùng Cho Case Nào? (Kinh nghiệm xương máu của Creyt)

Anh Creyt đã từng "vật lộn" với việc tạo ra các animation phức tạp mà không dùng TweenSequence rồi. Hồi đó, cứ phải ngồi tính toán interval thủ công, dùng CurvedAnimation với các beginend khác nhau, xong rồi addListener tùm lum để cập nhật trạng thái. Kết quả là code dài dòng, khó đọc, và dễ phát sinh bug khi cần chỉnh sửa thời gian.

Từ khi biết đến TweenSequence, mọi thứ trở nên "dễ thở" hơn hẳn. Anh em nên dùng TweenSequence khi:

  • Animation có nhiều "chặng" rõ ràng: Mỗi chặng có một hành vi riêng (tăng tốc, giảm tốc, đổi màu, di chuyển...).
  • Cần sự phối hợp nhịp nhàng giữa các hiệu ứng: Ví dụ, một vật thể di chuyển xong thì mới đổi màu, đổi màu xong mới xoay. TweenSequence giúp bạn định nghĩa trình tự này một cách trực quan.
  • Muốn code "sạch" và dễ bảo trì hơn: Thay vì nhiều Tween độc lập và CurvedAnimation lồng ghép, TweenSequence gom chúng lại thành một khối logic.

Tuy nhiên, đừng lạm dụng nó nhé! Nếu animation của bạn chỉ là một chuyển động đơn giản, hoặc các hiệu ứng diễn ra song song mà không cần trình tự, thì việc dùng TweenSequence có thể hơi "quá đà". Đôi khi, một Tween kết hợp với CurvedAnimation đã là đủ rồi. Quan trọng là chọn đúng công cụ cho đúng việc, giống như việc bạn chọn đúng loại cà phê cho buổi sáng vậy: đôi khi chỉ cần đen đá, đôi khi lại cần một ly Latte cầu kỳ.

Vậy đó, TweenSequence không chỉ là một công cụ, nó là một tư duy giúp bạn "điều phối" các animation phức tạp một cách thanh lịch. Hãy thực hành và biến những ý tưởng animation "điên rồ" nhất của bạn thành hiện thực nhé các đệ!

Thuộc Series: Flutter

Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!

#tech #cyberpunk #laravel
Chỉnh sửa bài viết

Bình luận (0)

Vui lòng Đăng Nhập để Bình luận

Hỗ trợ Markdown cơ bản
Nguyễn Văn A
1 ngày trước

Tính năng này đỉnh quá ad ơi, chờ mãi mới thấy một blog Tiếng Việt có UI/UX xịn như vầy!