Chuyển đến nội dung chính

Bài đăng

Hiển thị các bài đăng có nhãn DevTools

Widget Previews đã stable: sửa UI không cần chạy cả app

Chạy cả app chỉ để sửa một nút rất chậm. Widget Preview render widget riêng lẻ cách ly. Sau các bản experimental, nó stable trong Flutter 3.47 . Stable nghĩa là gì Khởi động nhanh nhờ cache local .widget_preview/ . PreviewThemeData để layer theme tuần tự (ma trận test theo seed/contrast). Tự đồng bộ asset web/ khi preview widget web. Workflow đáng tiền Tách widget đang sửa thành class preview được. Thêm preview cho mặc định / dark / chữ to / data rỗng. Lặp tới khi preview nhàm chán — rồi mới ghép vào app. Đây cũng là vòng nhanh nhất cho design-system trên material_ui / catalog tùy chỉnh. Cạm bẫy Preview đụng dart:io hoặc platform channel sẽ không chạy — giữ UI thuần. Đừng nhét business logic vào preview; chỉ fixture. Bài viết gốc đăng tại FlutterCook . Bản trên đó là bản được cập nhật mới nhất.

Widget Previews are stable: iterate on UI without booting the whole app

Booting a full app to tweak a button is slow. Widget Preview renders individual widgets in isolation. After experimental cycles, it is stable in Flutter 3.47 . What stable means here Faster startup via a local .widget_preview/ cache. PreviewThemeData for sequential theme layering (matrix tests across seeds/contrast). Automatic web/ asset sync when previewing web widgets. Workflow that pays off Extract the widget you are changing into a previewable class. Add previews for default / dark / large-text / empty-data. Iterate until the preview is boring — then wire it into the app. This is also the fastest loop for design-system work on material_ui / custom catalogs. Pitfalls Previews that hit dart:io or platform channels will not run — isolate pure UI. Do not put business logic in previews; put fixtures there. Originally published on FlutterCook . Read the latest version there — that copy is the one kept up to date.

Khởi động nguội trong Flutter: đo khoảng thời gian trước frame đầu tiên

“App mở mất ba giây” là một lời phàn nàn, không phải một phép đo. Ba giây tính từ đâu — từ cú chạm, từ lúc tiến trình sinh ra, từ lúc engine dựng lên, hay từ khoảnh khắc main() của bạn chạy? Mỗi mốc là một vấn đề khác nhau với cách sửa khác nhau, và tối ưu nhầm chỗ chính là cách một đội tiêu cả sprint để đưa thời gian khởi động từ 2,9 giây xuống 2,8 giây. Khởi động nguội của ứng dụng Flutter có bốn giai đoạn, và chúng tách bạch được. Bốn giai đoạn 1. Khởi tạo tiến trình. Hệ điều hành tạo tiến trình, nạp file thực thi cùng các thư viện chia sẻ, rồi trao quyền cho phần runner của nền tảng. Không có gì trong code Dart của bạn ảnh hưởng tới giai đoạn này. Thứ có ảnh hưởng là kích thước binary (xem bài về kích thước ứng dụng) và, trên Android, số thư viện phải liên kết. 2. Khởi tạo engine. Engine Flutter khởi động, Dart VM dựng lên, và — ở bản release — snapshot AOT được ánh xạ vào bộ nhớ. Việc đăng ký plugin ở phía nền tảng diễn ra tại đây. 3. Từ main() của Dart tới runApp() . Co...

Flutter cold start: measuring the time before your first frame

“The app takes three seconds to open” is a complaint, not a measurement. Three seconds from what — the tap, the process spawning, the engine coming up, or the moment your main() runs? Each of those is a different problem with a different fix, and optimising the wrong one is how teams spend a sprint moving startup from 2.9 s to 2.8 s. Cold start in a Flutter app has four phases, and they are separable. The four phases 1. Process launch. The OS creates the process, loads the executable and its shared libraries, and hands control to the platform runner. Nothing in your Dart code affects this. What does affect it is binary size (see the app size discussion) and, on Android, the number of libraries to link. 2. Engine initialisation. The Flutter engine starts, the Dart VM comes up, and — in release builds — the AOT snapshot is mapped in. Plugin registration happens on the platform side here. 3. Dart main() to runApp() . Your code. This is the phase you control completely, and the o...

Flutter Property Editor: inspect widgets without print debugging — hướng dẫn Flutter

Property Editor exposes editable fields for the selected widget in DevTools. 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 Edit properties live from DevTools while iterating on a screen. 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. Mô hình thực tế Faster than hot-reload cycles when tuning padding, colors, and flags. 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: Widget phát intent như load, submit, refresh hoặc retry. Controller hoặc use case validate intent rồi gọi boundary. Boundary trả v...

Flutter Property Editor: inspect widgets without print debugging

Property Editor exposes editable fields for the selected widget in DevTools. This article turns that observation into a small implementation model you can test, measure, and keep boring when the app grows. The problem in one sentence Edit properties live from DevTools while iterating on a screen. The useful question is not whether the API or pattern looks elegant in isolation. It is where state lives, which boundary owns failure, and how a user recovers when the happy path disappears. A practical model Faster than hot-reload cycles when tuning padding, colors, and flags. Start with one explicit owner for the behavior. Keep widgets responsible for rendering and user intent; keep IO, persistence, permissions, and retries behind a small interface. That gives you a seam for a fake in tests and a place to record the facts that matter in production. For a Flutter app, the boundary usually looks like this: The widget emits an intent such as load, submit, refresh, or retry. A control...

DevTools Network and Memory views you should actually use — hướng dẫn Flutter

Most production fires are leaks or chatty APIs. DevTools finds both. 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 Finding leaked images and chatty APIs in ten minutes. 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. Mô hình thực tế Snapshot memory before and after a navigation loop. 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: Widget phát intent như load, submit, refresh hoặc retry. Controller hoặc use case validate intent rồi gọi boundary. Boundary trả về data có kiểu hoặc failure có kiểu,...

DevTools Network and Memory views you should actually use

Most production fires are leaks or chatty APIs. DevTools finds both. This article turns that observation into a small implementation model you can test, measure, and keep boring when the app grows. The problem in one sentence Finding leaked images and chatty APIs in ten minutes. The useful question is not whether the API or pattern looks elegant in isolation. It is where state lives, which boundary owns failure, and how a user recovers when the happy path disappears. A practical model Snapshot memory before and after a navigation loop. Start with one explicit owner for the behavior. Keep widgets responsible for rendering and user intent; keep IO, persistence, permissions, and retries behind a small interface. That gives you a seam for a fake in tests and a place to record the facts that matter in production. For a Flutter app, the boundary usually looks like this: The widget emits an intent such as load, submit, refresh, or retry. A controller or use case validates the intent...

Flutter Property Editor: inspect widgets without print debugging — hướng dẫn Flutter

Property Editor exposes editable fields for the selected widget in DevTools. 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 Edit properties live from DevTools while iterating on a screen. 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. Mô hình thực tế Faster than hot-reload cycles when tuning padding, colors, and flags. 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: Widget phát intent như load, submit, refresh hoặc retry. Controller hoặc use case validate intent rồi gọi boundary. Boundary trả v...

Tìm rò rỉ bộ nhớ trong Flutter: năm loại đối tượng không bao giờ được dispose

Rò rỉ bộ nhớ trong Flutter hiếm khi tự khai báo. Ứng dụng chạy được, test xanh, rồi ai đó chuyển qua lại giữa hai màn hình bốn mươi lần và tiến trình đứng ở mức 900 MB. Trên một máy Android tầm trung, kết cục là bị hệ thống giết vì hết bộ nhớ, và bạn nhận được báo cáo “app tự đóng” mà không có stack trace nào. Dart có bộ thu gom rác, nên rò rỉ ở đây chỉ có đúng một nghĩa: vẫn còn thứ gì đó giữ tham chiếu tới một đối tượng bạn đã dùng xong . Toàn bộ cuộc điều tra là đi tìm cái “thứ gì đó” ấy. Năm mô-típ Trên thực tế, gần như mọi vụ rò rỉ tôi từng truy trong ứng dụng Flutter đều thuộc một trong số này. 1. Controller không bao giờ được dispose. AnimationController , TextEditingController , ScrollController , TabController , PageController — cái nào cũng giữ listener và, với trường hợp animation, còn giữ một ticker đã đăng ký với bộ lập lịch. class _EditorState extends State < Editor > with SingleTickerProviderStateMixin { late final _text = TextEditingController ()...

Vì sao ListView của bạn chậm, và bốn cách sửa thật sự có tác dụng

Đội Flutter nào cũng gặp chuyện này. Danh sách chạy ngon với hai mươi mục lúc phát triển, phát hành, rồi một người dùng có tám trăm mục đã lưu báo rằng cuộn bị khựng và ứng dụng cảm giác nặng nề. Phản xạ đầu tiên là đổ lỗi cho ListView , cho Flutter, hoặc cho cái máy. ListView không có lỗi. Trong mọi danh sách chậm tôi từng phân tích, nguyên nhân là một trong bốn thứ, và phân biệt chúng mất khoảng mười phút. Trước tiên: xem luồng nào đang trễ Trước khi đổi bất cứ thứ gì, hãy chạy ở chế độ profile trên thiết bị thật và mở performance view của DevTools. Mỗi frame được vẽ thành hai thanh: Luồng UI dài → build và layout đang đắt. itemBuilder của bạn làm quá nhiều việc. Luồng raster dài → vẽ đang đắt. Bóng đổ, làm mờ, lớp opacity, saveLayer, ảnh lớn. Phân biệt đó loại bỏ ngay một nửa số cách sửa khả dĩ. Thêm RepaintBoundary vào một danh sách mà nút thắt nằm ở luồng UI chẳng có tác dụng gì; đơn giản hoá cấu trúc widget trong một danh sách mà luồng raster đang bão hoà vì Backdro...

Flutter cold start: measuring the time before your first frame

“The app takes three seconds to open” is a complaint, not a measurement. Three seconds from what — the tap, the process spawning, the engine coming up, or the moment your main() runs? Each of those is a different problem with a different fix, and optimising the wrong one is how teams spend a sprint moving startup from 2.9 s to 2.8 s. Cold start in a Flutter app has four phases, and they are separable. The four phases 1. Process launch. The OS creates the process, loads the executable and its shared libraries, and hands control to the platform runner. Nothing in your Dart code affects this. What does affect it is binary size (see the app size discussion) and, on Android, the number of libraries to link. 2. Engine initialisation. The Flutter engine starts, the Dart VM comes up, and — in release builds — the AOT snapshot is mapped in. Plugin registration happens on the platform side here. 3. Dart main() to runApp() . Your code. This is the phase you control completely, and the o...