🧭 Layout · Intermediate

Slivers that scroll without jank — hướng dẫn Flutter

Slivers give precise control over when layout work happens during scroll. Bài này chuyển quan sát đó thành một mô hình nhỏ để bạn có thể kiểm thử, đo lường và giữ cho code dễ bảo trì khi app lớn lên.

Vấn đề cốt lõi

CustomScrollView, SliverList.builder, and avoiding layout thrash. Câu hỏi hữu ích không phải API hay pattern có đẹp riêng lẻ hay không, mà là state nằm ở đâu, boundary nào chịu trách nhiệm khi lỗi xảy ra, và người dùng phục hồi thế nào khi happy path biến mất.

Sơ đồ kiến trúc Flutter cho Slivers that scroll without jank

Mô hình thực tế

builder constructors keep off-screen children unbuilt.

Hãy bắt đầu bằng một owner rõ ràng cho behavior. Widget chỉ nên render và phát intent; IO, persistence, permission và retry nên nằm sau một interface nhỏ. Nhờ vậy bạn có seam để fake trong test và một chỗ ghi lại các dữ kiện cần theo dõi ở production.

Trong app Flutter, boundary thường có dạng:

  1. Widget phát intent như load, submit, refresh hoặc retry.
  2. Controller hoặc use case validate intent rồi gọi boundary.
  3. Boundary trả về data có kiểu hoặc failure có kiểu, không trả string chỉ dành cho log.
  4. UI render tường minh loading, empty, success và failure.

Hình dạng này không phụ thuộc bạn dùng setState, Bloc, Riverpod hay state layer khác. Quyết định quan trọng là ownership, không phải tên package.

Ví dụ tối thiểu

Thử nghiệm nhỏ nhất thường đủ để lộ trade-off:

CustomScrollView(slivers: [SliverAppBar(...), SliverList.builder(...)]);

Đặt ví dụ sau một interface theo feature, rồi thêm một test cho success và một test cho failure. Nếu ví dụ cần global state mới chạy được thì boundary có thể đang đặt sai chỗ.

Checklist trước khi ship

  • Xác định lifecycle: ai bắt đầu, ai hủy, và cái gì sống qua restart?
  • Đo kết quả người dùng thấy: frame time, bytes, latency, conversion hoặc thời gian hồi phục.
  • Giữ code platform-specific ở edge và ghi rõ yêu cầu OS/SDK tối thiểu.
  • Viết empty state và offline/error state trước khi chăm chút polish.
  • Log id và timing, nhưng phải redact token, dữ liệu cá nhân và payload đầy đủ.
  • Rollout có đường lui: feature flag, staged release hoặc fallback phía server.

Cạm bẫy nên xử lý sớm

NestedScrollView + pinned headers have bitten many teams — test carefully. Lỗi thường gặp là demo chạy đúng nhưng cancellation, retry, permission, accessibility và hành vi khi upgrade lại bị bỏ ngỏ. Khi app có user thật, đó không còn là edge case. Hãy test ít nhất một thiết bị chậm, một request bị ngắt mạng và một process restart nếu chủ đề này ảnh hưởng lifecycle hoặc storage.

Đánh giá kết quả

Ghi baseline trước khi sửa. So sánh cùng user journey trên cùng nhóm thiết bị, rồi xem cả median lẫn failure tệ nhất mà người dùng nhìn thấy. Một diff nhỏ có rollback rõ thường an toàn hơn rewrite toàn framework. Lưu quyết định và bằng chứng cạnh code để lần upgrade sau không phải tranh luận lại từ đầu.

Đọc thêm các chủ đề liên quan: Flutter localization with ARB and gen_l10n that translators likeAccessibility semantics checklist for Flutter forms.

#Flutter#Layout#Performance