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

Bài đăng

Đang hiển thị bài đăng từ Tháng 10, 2026

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...

Flutter web Wasm: deferred loading để first paint nhẹ hơn

Wasm mở khóa đồ họa gần native trên web — kèm kỷ luật đóng gói mới. Flutter 3.47 thêm deferred loading experimental cho build Wasm để tách app lớn thành module lazy. Lệnh build flutter build web --release --wasm flutter build web --release --wasm --enable-wasm-deferred-loading Điều kiện tiên quyết Rời dart:html sang package:web và JS interop hiện đại. Update package còn giả định interop chỉ dart2js. Chiến lược tách module Giữ login/shell ở module chính. Defer màn nặng (admin, editor, map). Đo time to interactive , không chỉ dung lượng tải. Cạm bẫy Library deferred không được bắt buộc ở frame đầu. UI nặng canvas/Skottie vẫn cần ngân sách asset chặt. Không phải tổ hợp browser/flag nào cũng giống nhau — test Chrome, Safari, Firefox. 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.

Shipping mobile apps for the Vietnam market in 2026 — hướng dẫn Flutter

Vietnam is mobile-first with unique payment and language needs. 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 Payments, localization, and store considerations. 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ế Localize UI copy and support local e-wallets where relevant. 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,...

UIScene lifecycle: yêu cầu khởi động iOS 27 mà app Flutter phải đạt

Lifecycle theo scene của Apple không còn tùy chọn với app build bằng SDK mới nhất. iOS 27 / Xcode 27 sẽ không launch app UIKit chưa dùng UIScene . Flutter 3.47 đổi gì Nâng minimum: iOS 15 , macOS 12 (từ 13 / 10.15). CLI migrate AppDelegate thường tự động. Việc tay còn lại nếu bạn customize lifecycle hook hoặc plugin còn giả định model cũ. Checklist migrate Update Xcode và Flutter về cặp được hỗ trợ. Clean build và đọc output migrate của CLI. Rà plugin có API chỉ UIApplicationDelegate. Test cold start, background→foreground, dialog quyền. Cạm bẫy “Chạy được trên simulator” là chưa đủ — test device với SDK mới. Deep link và notification handler thường nằm trong lifecycle code; test lại. 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.

Flutter web Wasm: deferred loading for smaller first paint

Wasm unlocks near-native graphics on the web — and a new packaging discipline. Flutter 3.47 adds experimental deferred loading for Wasm builds so you can split a large app into lazy modules. Build commands flutter build web --release --wasm # experimental deferred modules (main channel flag in 3.47 era) flutter build web --release --wasm --enable-wasm-deferred-loading Prerequisites Migrate off dart:html to package:web and modern JS interop. Update packages that still assume dart2js-only interop. Splitting strategy Keep login/shell in the main module. Defer heavy feature screens (admin, editors, maps). Measure time to interactive , not just download size. Pitfalls Deferred libraries must not be required during first frame. Wasm + canvas/Skottie-heavy UIs still need careful asset budgets. Not every browser/flag combination is equal — test Chrome, Safari, Firefox. Originally published on FlutterCook . Read the latest version there — that copy is the one...

Shipping mobile apps for the Vietnam market in 2026

Vietnam is mobile-first with unique payment and language needs. 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 Payments, localization, and store considerations. 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 Localize UI copy and support local e-wallets where relevant. 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...

UIScene lifecycle: the iOS 27 launch requirement Flutter apps must meet

Apple’s scene-based lifecycle is no longer optional for apps built with the latest SDKs. iOS 27 / Xcode 27 will fail launches for UIKit apps that do not adopt UIScene . What Flutter 3.47 changes Minimums rise: iOS 15 , macOS 12 (from 13 / 10.15). The CLI migrates typical AppDelegate setups automatically. Manual work remains if you customized lifecycle hooks or use plugins that still assume the old model. Migration checklist Update Xcode and Flutter to the supported pair. Clean build and look for CLI migration output. Audit plugins for UIApplicationDelegate-only APIs. Test cold start, background→foreground, and permission dialogs. Pitfalls “It launches in the simulator” is not enough — test a device with the new SDK. Deep links and notification handlers often live in lifecycle code; retest them. Originally published on FlutterCook . Read the latest version there — that copy is the one kept up to date.

UIScene lifecycle: the iOS 27 launch gate for Flutter apps — hướng dẫn Flutter

Apple’s scene-based lifecycle is mandatory for apps built with the latest SDKs. 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 Xcode 27 fails launches without UIScene. What Flutter migrates automatically. 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ế Flutter CLI migrates typical AppDelegate setups; custom hooks need a manual pass. 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 ...

A weekly mobile crash triage that actually reduces issues — hướng dẫn Flutter

Stability is a process. One weekly triage beats heroics at launch. 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 Triage boards, owner assignment, and release health gates. 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ế Gate releases on crash-free rate, not vibes. 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ô...

Threat modeling a mobile app in one workshop — hướng dẫn Flutter

Security by accident is how apps end up in breach blogs. 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 Assets, attackers, and mitigations that fit the product. 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ế One hour of STRIDE per feature area is enough to start. 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ôn...

UIScene lifecycle: the iOS 27 launch gate for Flutter apps

Apple’s scene-based lifecycle is mandatory for apps built with the latest SDKs. 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 Xcode 27 fails launches without UIScene. What Flutter migrates automatically. 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 Flutter CLI migrates typical AppDelegate setups; custom hooks need a manual pass. 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, ...

A weekly mobile crash triage that actually reduces issues

Stability is a process. One weekly triage beats heroics at launch. 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 Triage boards, owner assignment, and release health gates. 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 Gate releases on crash-free rate, not vibes. 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...

Threat modeling a mobile app in one workshop

Security by accident is how apps end up in breach blogs. 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 Assets, attackers, and mitigations that fit the product. 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 One hour of STRIDE per feature area is enough to start. 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 ...

Theming Material 3 trong Flutter: vai trò màu, không phải giá trị màu

Việc chuyển sang Material 3 trong Flutter tạo ra một mô-típ rất dễ đoán. Đội ngũ gọi ColorScheme.fromSeed(seedColor: brandPurple) , nhìn kết quả, kết luận “đây không phải màu thương hiệu của mình”, rồi quay lại truyền màu tường minh cho từng widget. Sáu tháng sau, ứng dụng có hai trăm giá trị hex cứng, dark mode là một danh sách hai trăm giá trị khác, và đổi màu thương hiệu tốn cả tuần. Thứ bị bỏ qua chính là ý tưởng mà Material 3 được xây dựng trên đó: bạn không tô widget bằng màu, bạn gán cho nó một vai trò . Một FilledButton không có nền màu tím — nó có nền primary với nội dung onPrimary . Khi điều đó đúng trên toàn ứng dụng, bảng màu trở thành một object duy nhất mà bạn có thể thay. Các vai trò và mục đích của từng cái ColorScheme có khoảng ba mươi thành viên. Đó không phải ba mươi lựa chọn độc lập; đó là các cặp và các họ. Vai trò Dùng cho Cặp “on” của nó primary Hành động chính, nút filled, trạng thái active onPrimary primaryCon...