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

Bài đăng

Bloc: kiến trúc nhàm chán nhưng scale được

Xu hướng xoay vòng; team production vẫn ship với Bloc vì nó dự đoán được . UI gửi event; bloc emit state; widget rebuild từ state. Vì sao scale được Event log debug được. Test bloc không cần pump cả app. Biên sở hữu feature rõ ràng. Mẹo dùng hiện đại Ưu tiên builder/selector của flutter_bloc hơn rebuild cả trang. Giữ bloc không dính side-effect điều hướng BuildContext . Ghép tầng repository — bloc không phải data layer. Cạm bẫy God-bloc ôm cả app. Chia theo feature và lifecycle. Cũng đừng map 1:1 mọi thay đổi field thành event. Nếu team cần architecture mặc định năm 2026, Bloc vẫn là câu trả lời bảo vệ được. 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.
Các bài đăng gần đây

Appwrite Flutter: backend service với client rõ ràng

Appwrite Flutter: backend service với client rõ ràng appwrite đáng chú ý vì nó giải quyết cấu hình endpoint/project, auth, database, storage và function. Bài này trình bày cách đặt package vào đúng boundary của một ứng dụng Flutter production thay vì chỉ copy một snippet vào màn hình. Package này sở hữu phần nào? Giữ API của package ở một adapter hoặc feature boundary có tên rõ ràng. Đưa lifecycle, lỗi và trạng thái loading vào contract mà UI có thể kiểm thử. Chỉ expose capability cần thiết; đừng để toàn app phụ thuộc vào chi tiết triển khai. Snippet bắt đầu final client = Client () . setEndpoint ( 'https://cloud.appwrite.io/v1' ) . setProject (projectId); final account = Account (client); await account. createEmailPasswordSession ( email : email, password : password, ); Checklist trước khi ship Đọc README và changelog của package đúng version đang pin. Viết một test cho lifecycle, lỗi và hành vi khi app về background. Kiểm tra Android, iO...

App authenticator Flutter an toàn cần đúng gì

App authenticator là UI nhỏ với hệ quả bảo mật lớn. Dù bạn study Aegis hay client TOTP khác, bài học liên quan Flutter vẫn vậy. Không thể thương lượng Secret mã hóa lúc lưu (platform keystore/keychain khi có). Vệ sinh screenshot/clipboard và auto-lock. Backup export được mã hóa — và restore đã test. Ghi chú UI Danh sách OTP lớn cần virtualization và feedback copy-paste rõ. Nhãn accessibility nên gồm tên tài khoản, không chỉ mã. Cạm bẫy Cất seed trong SharedPreferences thường là cách app “bảo mật” fail review và mất niềm tin. Kể cả project học tập, hãy tập default an toàn. 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.

Bloc: the boring architecture that scales

Trends rotate; production teams still ship with Bloc because it is predictable . UI sends events; blocs emit states; widgets rebuild from state. Why it scales Debuggable event logs. Test blocs without pumping the entire app. Clear ownership boundaries between features. Modern usage tips Prefer flutter_bloc builders/selectors over rebuilding whole pages. Keep blocs free of BuildContext navigation side effects. Pair with a repository layer — bloc is not your data layer. Pitfalls God-blocs that hold the whole app. Split by feature and lifecycle. Also do not map 1:1 every field change into a bloc event. If your team needs a default architecture in 2026, Bloc is still a defensible answer. Originally published on FlutterCook . Read the latest version there — that copy is the one kept up to date.

Appwrite Flutter: backend services with explicit clients

Appwrite Flutter: backend services with explicit clients appwrite is useful because it focuses on endpoint/project configuration, auth, databases, storage, and functions. The important engineering move is to place the package behind a clear boundary, so your Flutter UI depends on a stable capability rather than a vendor-shaped API. What the library should own Keep package calls inside a named adapter or feature boundary. Make lifecycle, errors, and loading state part of a testable contract. Expose only the capability the app needs; hide implementation details from the whole tree. A focused starting point final client = Client () . setEndpoint ( 'https://cloud.appwrite.io/v1' ) . setProject (projectId); final account = Account (client); await account. createEmailPasswordSession ( email : email, password : password, ); Production checklist Read the README and changelog for the exact version you pin. Add one test for lifecycle, failure, and ap...

What a secure Flutter authenticator must get right

Authenticator apps are small UIs with outsized security consequences. Whether you study Aegis or another TOTP client, the Flutter-relevant lessons are the same. Non-negotiables Secrets encrypted at rest (platform keystore/keychain when available). Screenshot/clipboard hygiene and auto-lock. Backup export that is encrypted — and tested restore. UI notes Large OTP lists need virtualization and urgency in copy-paste feedback. Accessibility labels should include the account name, not only the code. Pitfalls Storing seeds in plain SharedPreferences is how “security” apps fail reviews and user trust. Even for a learning project, practice secure defaults. Originally published on FlutterCook . Read the latest version there — that copy is the one kept up to date.

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 Previewer is stable: isolate UI without booting the app — hướng dẫn Flutter

Booting a full app to tweak a button is slow. Preview renders widgets in isolation. 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 Faster startup, PreviewThemeData matrices, and web asset sync in 3.47. 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ế Default/dark/large-text/empty-data previews catch issues before wiring into the app. 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 ...

SEO for Flutter web: when to prerender and when not to — hướng dẫn Flutter

Flutter web is not a CMS. SEO-critical marketing pages often want HTML. 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 Documented limits, hybrid HTML, and marketing page strategies. 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ế Use Flutter for app surfaces; host blog/docs as static HTML. 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 ...

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.

Widget Previewer is stable: isolate UI without booting the app

Booting a full app to tweak a button is slow. Preview renders widgets in isolation. 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 Faster startup, PreviewThemeData matrices, and web asset sync in 3.47. 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 Default/dark/large-text/empty-data previews catch issues before wiring into the app. 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, ...

SEO for Flutter web: when to prerender and when not to

Flutter web is not a CMS. SEO-critical marketing pages often want HTML. 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 Documented limits, hybrid HTML, and marketing page strategies. 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 Use Flutter for app surfaces; host blog/docs as static HTML. 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...

Embedding Flutter into an existing marketing site — hướng dẫn Flutter

You can add a Flutter product surface without replacing the CMS site. 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 Element embedding, lifecycle, and SEO-safe boundaries. 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ế Keep crawlable content in HTML; put the interactive app in Flutter. 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ặ...

Wear companions: watchOS and Wear OS with Flutter phones — hướng dẫn Flutter

Watches need ultra-focused jobs and aggressive sync batching. 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 Sync patterns and tiny UI constraints. 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ế Phone remains the system of record for most apps. 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, không trả string chỉ dà...

Wasm deferred loading: split Flutter web for first paint — hướng dẫn Flutter

Wasm unlocks near-native graphics and a new packaging discipline. 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 Opt into —wasm and —enable-wasm-deferred-loading for smaller boot bundles. 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ế Keep login/shell in the main module; defer admin and heavy editors. 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ề ...

Embedding Flutter into an existing marketing site

You can add a Flutter product surface without replacing the CMS site. 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 Element embedding, lifecycle, and SEO-safe boundaries. 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 Keep crawlable content in HTML; put the interactive app in Flutter. 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 va...

Wear companions: watchOS and Wear OS with Flutter phones

Watches need ultra-focused jobs and aggressive sync batching. 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 Sync patterns and tiny UI constraints. 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 Phone remains the system of record for most apps. 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 and calls the boundary....

Wasm deferred loading: split Flutter web for first paint

Wasm unlocks near-native graphics and a new packaging discipline. 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 Opt into —wasm and —enable-wasm-deferred-loading for smaller boot bundles. 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 Keep login/shell in the main module; defer admin and heavy editors. 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 controlle...