
Chào các dân chơi hệ code, lại là Creyt đây! Hôm nay, chúng ta sẽ cùng nhau 'bóc phốt' một ông trùm thầm lặng nhưng cực kỳ quan trọng trong thế giới animation của Flutter: TickerProvider. Nghe tên thì có vẻ hàn lâm, nhưng thật ra nó lại là 'cái đồng hồ' đếm nhịp cho mọi chuyển động mượt mà trên app của mấy đứa đấy.
1. TickerProvider là gì mà 'hot' thế?
Thử tưởng tượng thế này nhé: Mấy đứa đang xem một buổi hòa nhạc giao hưởng hoành tráng. Mỗi nhạc công (tức là mỗi animation trong app của mấy đứa) đều cần chơi nhạc đúng nhịp, đúng phách để tạo nên một bản nhạc tuyệt vời. Nếu không có nhạc trưởng (chính là TickerProvider của chúng ta), mỗi nhạc công sẽ chơi theo ý mình, mạnh ai nấy đánh, và kết quả là... một mớ hỗn độn không ai muốn nghe.
Trong Flutter, khi mấy đứa muốn tạo ra một animation có thể điều khiển được (kiểu như xoay một cái icon, di chuyển một cái widget từ A sang B, hay làm mờ dần một ảnh), mấy đứa sẽ dùng đến AnimationController. Mà cái AnimationController này, để biết khi nào thì nó cần 'nhảy một bước' (tức là cập nhật trạng thái animation), nó cần một cái 'đồng hồ' đếm nhịp. Đó chính là Ticker.
TickerProvider chính là 'nhà cung cấp' những cái Ticker này. Nó đảm bảo rằng tất cả các animation trong một widget đều được đồng bộ hóa, nhận tín hiệu 'tick' đều đặn trên mỗi frame hình (thường là 60 lần/giây) để animation của mấy đứa trông mượt mà như bơ. Không có nó, animation của mấy đứa sẽ 'đứng hình' hoặc giật cục như phim 2 hình/giây vậy.
Nói tóm lại, TickerProvider là một interface (giao diện) mà một State object cần implement để có thể cung cấp Ticker cho các AnimationController. Nó giúp Flutter biết khi nào cần vẽ lại UI để animation trông tự nhiên nhất.
2. Code Ví Dụ Minh Hoạ: 'Nghệ thuật' của sự mượt mà
Trong Flutter, chúng ta thường dùng hai 'biến thể' của TickerProvider:
SingleTickerProviderStateMixin: Dùng khiStatecủa mấy đứa chỉ có mộtAnimationController. Đây là trường hợp phổ biến nhất.TickerProviderStateMixin: Dùng khiStatecủa mấy đứa có nhiềuAnimationControllerđộc lập.
Giờ mình sẽ dùng SingleTickerProviderStateMixin để làm một cái hộp xoay tròn 'mlem mlem' nhé:
import 'package:flutter/material.dart';
class AnimatedRotationBox extends StatefulWidget {
const AnimatedRotationBox({super.key});
@override
State<AnimatedRotationBox> createState() => _AnimatedRotationBoxState();
}
// Đây rồi, 'nhạc trưởng' của chúng ta: SingleTickerProviderStateMixin!
class _AnimatedRotationBoxState extends State<AnimatedRotationBox>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this, // 'vsync' chính là TickerProvider của chúng ta
duration: const Duration(seconds: 2), // Xoay trong 2 giây
)..repeat(); // Lặp lại vô tận
}
@override
void dispose() {
_controller.dispose(); // Quan trọng: Nhớ dọn dẹp 'nhạc trưởng' khi không dùng nữa!
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Transform.rotate(
angle: _controller.value * 2 * 3.14159, // Xoay 360 độ
child: Container(
width: 100,
height: 100,
color: Colors.blue,
child: const Center(
child: Text(
'Xoay!',
style: TextStyle(color: Colors.white),
),
),
),
);
},
);
}
}
// Để chạy thử, mấy đứa có thể dùng MaterialApp và Scaffold như này:
// void main() {
// runApp(const MyApp());
// }
// class MyApp extends StatelessWidget {
// const MyApp({super.key});
// @override
// Widget build(BuildContext context) {
// return MaterialApp(
// home: Scaffold(
// appBar: AppBar(title: const Text('TickerProvider Demo')),
// body: const Center(
// child: AnimatedRotationBox(),
// ),
// ),
// );
// }
// }
Trong ví dụ trên:
- Chúng ta thêm
with SingleTickerProviderStateMixinvào_AnimatedRotationBoxState. Điều này biến_AnimatedRotationBoxStatethành mộtTickerProvider. - Khi khởi tạo
AnimationController, chúng ta truyềnthisvào tham sốvsync.vsyncchính là 'cái đồng hồ' màAnimationControllerdùng để biết khi nào cần 'tick'. AnimatedBuildersẽ lắng nghe sự thay đổi của_controllervà rebuild widget con (ở đây làTransform.rotate) mỗi khi_controllercập nhật giá trị, tạo ra hiệu ứng xoay tròn mượt mà.

3. Mẹo Vặt (Best Practices) từ 'lão làng' Creyt
- Đúng người đúng việc: Luôn chọn đúng mixin. Nếu chỉ có một
AnimationController, dùngSingleTickerProviderStateMixinđể tiết kiệm tài nguyên. Nếu có nhiều, dùngTickerProviderStateMixin. - Dọn dẹp là vàng: Luôn luôn gọi
_controller.dispose()trongdispose()method củaState. Nếu không,AnimationControllersẽ tiếp tục chạy ngầm và giữ tham chiếu đến widget của mấy đứa ngay cả khi nó không còn hiển thị, gây ra lỗi rò rỉ bộ nhớ (memory leak) và ngốn pin điện thoại như uống nước lã. - Hiểu rõ
vsync: Tham sốvsynctrongAnimationControllerlà nơi mấy đứa truyền vào instance củaTickerProvider. Nó giống như việc mấy đứa đưa choAnimationControllercái điều khiển từ xa để nó biết khi nào cần 'kích hoạt' animation vậy. - Khi nào thì dùng? Nếu mấy đứa cần điều khiển tường minh một animation (ví dụ: bắt đầu, dừng, lặp lại, đảo ngược, điều chỉnh tốc độ, kết hợp nhiều animation), thì
AnimationControllervàTickerProviderlà lựa chọn số 1. Còn mấy cái animation đơn giản kiểuAnimatedContainerhayHerothì Flutter đã lo cho mấy đứa rồi, không cầnTickerProvidertrực tiếp đâu.
4. Ứng dụng thực tế: Mấy đứa thấy nó ở đâu?
Thực ra, TickerProvider 'len lỏi' trong rất nhiều ứng dụng mà mấy đứa dùng hàng ngày:
- TikTok/Instagram Stories: Các hiệu ứng chuyển cảnh mượt mà khi vuốt giữa các story, các animation khi bấm nút like, thả tim.
- Ứng dụng thương mại điện tử: Hiệu ứng giỏ hàng bay lên khi thêm sản phẩm, các loading spinner khi chờ dữ liệu.
- Game mobile: Các animation của nhân vật, hiệu ứng skill, chuyển động của UI.
- Bất kỳ ứng dụng nào có UI/UX 'xịn sò': Từ navigation drawer trượt ra, các tab chuyển đổi mượt mà, đến các custom loading indicator phức tạp.
Nói chung, cứ cái gì mà nó 'nhúc nhích' có chủ đích và trông 'mượt như lụa' trên app Flutter, thì khả năng cao là có 'bàn tay' của TickerProvider nhúng vào đấy.
5. Thử nghiệm và Nên dùng cho case nào?
Creyt đã từng 'vật lộn' với mấy cái animation giật cục hồi mới vào nghề, cho đến khi 'ngộ' ra chân lý về TickerProvider. Mấy đứa cứ thử tưởng tượng, nếu không có nó, mỗi AnimationController sẽ tự tạo một cái Ticker riêng, và nếu có hàng chục cái animation cùng chạy, nó sẽ giống như hàng chục cái đồng hồ báo thức kêu loạn xạ trong đầu mấy đứa vậy – vừa tốn tài nguyên, vừa loạn. TickerProvider giải quyết vấn đề này bằng cách cung cấp một 'nhịp đập' chung, hiệu quả hơn rất nhiều.
Nên dùng TickerProvider khi:
- Mấy đứa cần tạo các explicit animations (animation tường minh) với
AnimationController. - Mấy đứa muốn kiểm soát chặt chẽ vòng đời của animation: bắt đầu, dừng, lặp, đảo ngược, thay đổi tốc độ, lắng nghe sự kiện hoàn thành, v.v.
- Mấy đứa đang xây dựng các custom widget có animation phức tạp, không thể dùng các
AnimatedWidgetcó sẵn của Flutter. - Mấy đứa cần đồng bộ hóa nhiều animation khác nhau để chúng chạy mượt mà cùng nhau.
Không cần dùng TickerProvider trực tiếp khi:
- Mấy đứa chỉ cần các implicit animations (animation ngầm định) như
AnimatedContainer,AnimatedOpacity,TweenAnimationBuilder... Flutter đã lo phầnTickerProvidercho mấy đứa rồi. - Mấy đứa chỉ hiển thị ảnh tĩnh, text tĩnh, không có bất kỳ chuyển động nào.
Hy vọng với bài giảng 'sát sườn' này, mấy đứa đã hiểu rõ hơn về TickerProvider và không còn 'sợ hãi' khi đối mặt với animation trong Flutter nữa. Nhớ nhé, muốn app mượt mà, phải có 'nhạc trưởng' TickerProvider!
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é!