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

Bài đăng

CupertinoMenuAnchor và menu Flutter hiện đại

Menu là nơi app Flutter hay “mở web” nhất. RawMenuAnchor là primitive chung; Cupertino và Material đều xây trên đó. Cupertino CupertinoMenuAnchor (community dẫn dắt, nổi bật davidhicks980) cho app iOS menu đúng kiểu UIKit: dismiss physics, nesting và focus khớp kỳ vọng nền tảng. Material MenuAnchor có animation Material 3 tùy chọn ( animated: true ) và SubmenuButton.hoverOpenDelay cho hover desktop. Bảng quyết định Mục tiêu Ưu tiên Sản phẩm iOS-first CupertinoMenuAnchor UI dày đặc desktop MenuAnchor + hoverOpenDelay Brand đa nền tảng Wrapper adaptive chọn theo platform Cạm bẫy Thứ tự callback close của RawMenuAnchor đã đổi — đọc breaking change trước khi upgrade. Đừng rebuild cả cây menu mỗi pointer event; giữ anchor ổn định. 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

Flutter view co theo nội dung trong native parent

Trước đây view Flutter nhúng cần size cố định từ native parent. Điều đó khiến Flutter bên trong UIScrollView hay RecyclerView rất đau. Bật content sizing iOS: FlutterViewController.isAutoResizable = true Android: đặt width/height của FlutterView là content_wrap và bật content sizing theo docs. Ràng buộc root widget Root Flutter phải chịu được unbounded height (hoặc width). Tránh ListView top-level kỳ vọng viewport bị chặn; ưu tiên layout intrinsic-height hoặc shrink-wrapped. Cạm bẫy Animation giả định viewport cỡ phone có thể overflow trong cell list native. Đo jank: nhiều engine/view Flutter nhúng vẫn tốn memory. Đồng bộ keyboard inset từ phía native. 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.

Một pipeline CI cho Flutter thật sự bắt được lỗi

Kho mã Flutter nào rồi cũng mọc ra một file .github/workflows/ci.yml chứa flutter test . Nó pass, mọi người thấy yên tâm, rồi một bản build phát hành hỏng trên máy build vì lý do mà không laptop nào có thể lộ ra. CI đáng đồng tiền khi nó bắt được những lỗi mà môi trường phát triển cục bộ về mặt cấu trúc không thể bắt: mã sinh cũ, một phụ thuộc chỉ resolve được nhờ pub cache trên máy bạn, một thay đổi định dạng chưa ai chạy, một golden đã trôi. Đây là pipeline dựng quanh những thứ đó. Hình dạng tổng thể Bốn job, chia hai đợt: Job Chạy khi Bắt được analyze mọi push và PR lệch định dạng, hồi quy lint, mã không dùng test mọi push và PR lỗi unit, widget và golden build PR vào main và tag lỗi biên dịch chỉ xảy ra trên máy sạch release chỉ tag ký, tải artifact lên analyze và test chạy song song và nhanh. build chậm nên bị chặn cổng. Sự phân chia đó quan trọng: một pipeline mà mỗi lần push đều phải chờ tám phút build Android là pipeline mà người ta sẽ học...

CupertinoMenuAnchor and modern Flutter menus

Menus are where Flutter apps most often feel “webby.” RawMenuAnchor is the shared primitive; Cupertino and Material both build on it. Cupertino CupertinoMenuAnchor (community-led, notably davidhicks980) gives iOS apps a menu that behaves like UIKit: dismiss physics, nesting, and focus that match platform expectations. Material MenuAnchor gains optional Material 3 animations ( animated: true ) and SubmenuButton.hoverOpenDelay for desktop hover behavior. Decision table Target Prefer iOS-first product CupertinoMenuAnchor Desktop dense UI MenuAnchor + hoverOpenDelay Cross-platform brand Adaptive wrapper choosing by platform Pitfalls Callback close order changed on RawMenuAnchor — read the breaking-change note before upgrading. Do not rebuild the entire menu tree on every pointer event; keep anchors stable. Originally published on FlutterCook . Read the latest version there — that copy is the one kept up to date.

Content-sized Flutter views in native parents

Historically an embedded Flutter view needed a fixed size from its native parent. That made Flutter-inside- UIScrollView or RecyclerView painful. Enable content sizing iOS: FlutterViewController.isAutoResizable = true Android: set width/height of FlutterView to content_wrap and enable content sizing in the manifest/docs flow. Root widget constraints Your Flutter root must tolerate unbounded height (or width). Avoid a top-level ListView that expects a bounded viewport; prefer intrinsic-height layouts or a single-column with shrink-wrapped content. Pitfalls Animations that assume a phone-sized viewport can overflow inside a native list cell. Measure jank: multiple embedded Flutter engines/views still cost memory. Coordinate with keyboard insets from the native side. Originally published on FlutterCook . Read the latest version there — that copy is the one kept up to date.

A Flutter CI pipeline that catches real problems

Every Flutter repository eventually grows a .github/workflows/ci.yml containing flutter test . It passes, everyone feels covered, and then a release build fails on the build machine for a reason nobody’s laptop could have surfaced. CI earns its keep by catching the failures that local development structurally cannot: stale generated code, a dependency that only resolves because of your local pub cache, a formatting change nobody ran, a golden that drifted. This is a pipeline built around those. The shape of it Four jobs, in two waves: Job Runs Catches analyze every push and PR format drift, lint regressions, unused code test every push and PR unit, widget, and golden failures build PRs to main and tags compile failures that only occur on a clean machine release tags only signing, artifact upload analyze and test run in parallel and are fast. build is slow and gated. That split matters: a pipeline where every push waits eight minutes for an Android...

BuildContext chính là element: đọc hiểu những thông báo lỗi có nhắc tới nó

BuildContext là tham số ai cũng gõ cả ngàn lần mà chẳng bao giờ hỏi nó là cái gì. Nó có mặt trong mọi hàm build , bị đòi bởi Theme.of , Navigator.of , showDialog , MediaQuery.sizeOf — rồi một ngày nó ném ra một lỗi chẳng hiểu nổi, kiểu Scaffold.of() thất bại ngay bên trong một widget rõ ràng đang được bọc bởi Scaffold . Dòng khai báo giải đáp toàn bộ. Trong mã nguồn framework: abstract class Element extends DiagnosticableTree implements BuildContext . Một BuildContext chính là một Element , chỉ được phơi ra qua một giao diện hẹp để bạn không thể dùng nó sửa cây. Khi một hàm build nhận context, nó đang được trao chính element của nó — đúng vị trí của nó trong cây đang sống. Mọi thứ khó hiểu về BuildContext trở nên hiển nhiên khi bạn đọc nó là “nút của tôi trong cây” thay vì “ứng dụng”. Việc tra cứu đi ngược lên từ nút của bạn Theme.of(context) , MediaQuery.of(context) , Navigator.of(context) và đồng bọn đều làm cùng một việc: bắt đầu từ element đó và đi lên chuỗi tổ tiên c...

Chạy nền trong Flutter: hệ điều hành thật sự cho phép bạn chạy gì

“Đồng bộ dữ liệu người dùng mười lăm phút một lần” nghe như bài toán lập lịch. Thực ra đó là cuộc thương lượng với hai hệ điều hành đã dành cả thập kỷ để giỏi hơn trong việc nói không. Trước khi chọn gói, đáng để nói chính xác từng nền tảng thật sự cho gì, vì khoảng cách giữa chúng quyết định tính năng của bạn được phép hứa điều gì. Bạn thật sự được phép làm gì Khả năng Android iOS Chạy nền định kỳ Có, tối thiểu ~15 phút, còn tuỳ Doze BGAppRefreshTask — hệ thống quyết định khi nào, có thể là không bao giờ Việc hoãn lại chạy một lần Có, kèm ràng buộc BGProcessingTask , thường vào ban đêm khi đang sạc Bảo đảm chạy khi ứng dụng đã đóng Chỉ qua foreground service kèm thông báo nhìn thấy được Không Việc chạy dài (nhiều phút) Foreground service Không — tác vụ nền có vài giây rồi bị đình chỉ Kích hoạt từ máy chủ Có, FCM mức ưu tiên cao Hạn chế; push ngầm bị bóp Dòng làm thay đổi thiết kế là dòng thứ ba. Trên iOS không có cách nào bảo đảm mã của bạn chạy...

Thu nhỏ ứng dụng Flutter: megabyte thật sự nằm ở đâu

Câu chuyện về kích thước ứng dụng thường bắt đầu sau khi một người có tiếng nói so sánh file APK với đối thủ và hỏi vì sao một app ghi chú việc cần làm lại nặng 40 MB. Phản ứng của đội kỹ thuật thường là bắt đầu xoá package — vốn vừa là cần gạt đau đớn nhất, vừa thường không phải cần gạt quan trọng. Ứng dụng Flutter có một sàn kích thước: engine, runtime Dart, Skia hoặc Impeller, và dữ liệu ICU. Cái sàn đó là thật và bạn không gỡ được. Mọi thứ nằm trên nó là của bạn, và trong hầu hết ứng dụng tôi từng xem, phần lớn cái “của bạn” đó là asset và mã native được đóng gói nhưng không dùng, chứ không phải Dart. Nên nước đi đầu tiên không phải là xoá gì cả. Mà là đo. Đo trước: --analyze-size flutter build apk --release --analyze-size flutter build appbundle --release --analyze-size flutter build ipa --release --analyze-size Lệnh này in ra một bản tóm tắt và ghi một file JSON. Hãy mở file đó bằng công cụ app size trong DevTools — nó cho bạn một treemap với các ô tỉ lệ theo s...