Bạn muốn giữ một vùng giao diện trong cây để mở lại, nhưng vùng đó chứa animation chạy liên tục. Chỉ nhìn thấy màn hình không còn vẽ phần ấy chưa cho biết ticker đã im. Hãy tách quyết định về layout và hiển thị khỏi quyết định về nguồn cập nhật animation.
Dùng hai lớp cho hai trách nhiệm
Theo tài liệu Flutter được đối chiếu ngày 12/11/2026, Offstage vẫn layout child, nhưng khi offstage thì không paint, không hit test và không chiếm chỗ trong parent. Widget con vẫn có thể hoạt động. TickerMode điều khiển ticker trong subtree khi controller dùng ticker provider gắn với widget.
Đoạn sau là thành phần để đặt trong màn hình Material hiện có. Màn hình cha truyền visible khi đổi chế độ. Không gỡ AnimatedPanel khỏi cây nếu mục tiêu là giữ State qua lần ẩn.
import 'package:flutter/material.dart';
class AnimatedPanel extends StatefulWidget {
const AnimatedPanel({super.key, required this.visible});
final bool visible;
@override
State<AnimatedPanel> createState() => _AnimatedPanelState();
}
class _AnimatedPanelState extends State<AnimatedPanel> {
@override
Widget build(BuildContext context) {
return Offstage(
offstage: !widget.visible,
child: TickerMode(
enabled: widget.visible,
child: const _Spinner(),
),
);
}
}
class _Spinner extends StatefulWidget {
const _Spinner();
@override
State<_Spinner> createState() => _SpinnerState();
}
class _SpinnerState extends State<_Spinner>
with SingleTickerProviderStateMixin {
late final AnimationController controller;
@override
void initState() {
super.initState();
controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 2),
)..repeat();
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return RotationTransition(
turns: controller,
child: const Icon(Icons.refresh),
);
}
}
Provider nằm trong _Spinner, bên dưới TickerMode. Nếu controller được tạo ở một State bên ngoài rồi chỉ truyền animation vào child, cần kiểm tra lại nơi ticker nhận mode; vị trí widget vẽ không tự quyết định ticker của controller bên ngoài.
Cách kiểm tra
Gắn nút đổi visible ở cha, để vùng xoay hiện rồi ẩn. Kiểm tra vùng ẩn không chiếm chỗ và không nhận thao tác chạm. Khi cần xác minh ticker, theo dõi callback của controller trong một lượt debug ngắn; không chỉ nhìn ảnh đứng yên. Hiện lại và kiểm tra animation tiếp tục cập nhật. Đừng giả định rằng mute ticker sẽ đóng băng thời gian theo nghĩa nghiệp vụ.
Tài liệu cũng lưu ý child offstage vẫn có thể nhận focus. Nếu vùng thật chứa ô nhập, hãy kiểm tra điều hướng bàn phím và quản lý focus riêng. TickerMode không hủy Timer, subscription hoặc request. Nếu phần ẩn không cần giữ state, cân nhắc gỡ widget để đơn giản hóa vòng đời.
Giới hạn và kết luận
Mẫu đã được rà theo API, chưa chạy SDK, analyzer, thiết bị hay đo pin. Dùng Offstage khi cần giữ child nhưng không vẽ, và kiểm tra ticker tại đúng subtree khi animation không cần cập nhật trong lúc ẩn.


