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

Bài đăng

LocalSend: truyền file kiểu AirDrop không cần cloud

LocalSend hỏi một câu sản phẩm sắc: hai thiết bị đổi file không cần tài khoản server được không? Có — nếu bạn thiết kế discovery LAN, HTTPS và consent rõ ràng. Điểm thiết kế Peer protocol trên HTTPS cục bộ (thường gắn với cổng 53317 ). UI Flutter với phần Rust/native cho đường nhạy hiệu năng. Không có control plane cloud — privacy là feature. Học theo Discovery + consent UX là nửa sản phẩm. Transfer phải resume được và hiện rõ; lỗi im lặng phá niềm tin. Giữ CLI headless cho power user và automation. Cạm bẫy Giới hạn background mobile giết transfer dài ngây thơ. Mạng hỗn hợp (AP isolation, VPN, captive portal) sinh support load — hãy tài liệu hóa. Học LocalSend khi cần pattern local-first , không chỉ một package để copy. 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

local_auth: biometric là step-up, không phải nơi lưu password

local_auth: biometric là step-up, không phải nơi lưu password local_auth đáng chú ý vì nó giải quyết hỗ trợ thiết bị, prompt biometric, credential fallback và UX lỗi. 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 auth = LocalAuthentication (); final supported = await auth. isDeviceSupported (); if (supported) { final ok = await auth. authenticate ( localizedReason : 'Unlock your saved account' , options : const AuthenticationOptions (biometricOnly : false ), ); if (ok) unlock (); } Checklist trước khi ship Đọc README và changelog của package đúng version đang pin. ...

just_audio: audio có thể điều khiển bằng stream

just_audio: audio có thể điều khiển bằng stream just_audio đáng chú ý vì nó giải quyết queue, stream position, playlist, tốc độ và tích hợp background. 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 player = AudioPlayer (); await player. setAudioSource ( ConcatenatingAudioSource (children : [ AudioSource . uri ( Uri . parse (trackUrl)), ]), ); player. play (); player.positionStream. listen (updateProgress); 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, iOS, web hoặc d...

LocalSend: AirDrop-style transfer with zero cloud

LocalSend answers a sharp product question: can two devices exchange files without an account or a server? Yes — if you design for LAN discovery, HTTPS, and explicit consent. Design highlights Peer protocol over local HTTPS (commonly associated with port 53317 ). Flutter UI with Rust/native pieces for performance-critical paths. No cloud control plane — privacy is the feature. Steal this Discovery + consent UX is half the product. Make transfers resumable and visible; silent failures destroy trust. Keep a CLI headless mode for power users and automation. Pitfalls Mobile OS background limits kill naive long transfers. Mixed networks (AP isolation, VPN, captive portals) will generate support load — document them. Study LocalSend when you need local-first product patterns, not just a package to copy. Originally published on FlutterCook . Read the latest version there — that copy is the one kept up to date.

local_auth: biometrics as a step-up, not a password store

local_auth: biometrics as a step-up, not a password store local_auth is useful because it focuses on device support, biometric prompts, fallback credentials, and failure UX. 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 auth = LocalAuthentication (); final supported = await auth. isDeviceSupported (); if (supported) { final ok = await auth. authenticate ( localizedReason : 'Unlock your saved account' , options : const AuthenticationOptions (biometricOnly : false ), ); if (ok) unlock (); } Production checklist Read the README and change...

just_audio: controllable audio with streams

just_audio: controllable audio with streams just_audio is useful because it focuses on queues, position streams, playlists, speed, and background integration. 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 player = AudioPlayer (); await player. setAudioSource ( ConcatenatingAudioSource (children : [ AudioSource . uri ( Uri . parse (trackUrl)), ]), ); player. play (); player.positionStream. listen (updateProgress); Production checklist Read the README and changelog for the exact version you pin. Add one test for lifecycle, failure, and app background/foreground...

json_serializable: JSON nhàm chán nhưng bền qua refactor

json_serializable: JSON nhàm chán nhưng bền qua refactor json_serializable đáng chú ý vì nó giải quyết codec generated, converter, checked mode và boundary API model. 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 @JsonSerializable () class User { const User ({ required this .id, required this .displayName}); final String id; final String displayName; factory User . fromJson ( Map < String , dynamic > json) => _$UserFromJson (json); Map < String , dynamic > toJson () => _$UserToJson ( this ); } Checklist trước khi ship Đọc README và changelog của package đúng version đ...

Jaspr: Dart server-driven cho web

Jaspr mang mental model component lên Dart phía server. Nếu bạn thích cấu trúc Flutter nhưng cần HTML/SEO, đây là hệ sinh thái kề cận — đáng chú ý khi góp phần vào hiện diện web của chính Flutter. Bài học Model Dart dùng chung giữa API, UI server và client Flutter giảm lệch DTO. SSR/hydration là vấn đề đóng gói nhiều như vấn đề UI. Server component khuyến khích trang nhàm chán, cache được. Cạm bẫy Đừng giả định widget Flutter chạy nguyên xi. Constraint layout và lifecycle khác mobile. Cân nhắc Jaspr cho site nội dung và dashboard nơi SEO quan trọng và Dart đã là ngôn ngữ của bạ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.

Isar: object database local nhanh với index có thể query

Isar: object database local nhanh với index có thể query isar đáng chú ý vì nó giải quyết collection, index, transaction và watcher reactive. 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 @collection class Note { Id id = Isar .autoIncrement; late String title; @Index () late DateTime updatedAt; } await isar. writeTxn (() async { await isar.notes. put (note); }); 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, iOS, web hoặc desktop đúng platform mà app hỗ trợ. Gh...

json_serializable: boring JSON that survives refactors

json_serializable: boring JSON that survives refactors json_serializable is useful because it focuses on generated codecs, converters, checked mode, and API model boundaries. 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 @JsonSerializable () class User { const User ({ required this .id, required this .displayName}); final String id; final String displayName; factory User . fromJson ( Map < String , dynamic > json) => _$UserFromJson (json); Map < String , dynamic > toJson () => _$UserToJson ( this ); } Production checklist Read the README ...

Jaspr: server-driven Dart for the web

Jaspr brings a component mental model to Dart on the server. If you like Flutter’s structure but need HTML/SEO, this is the adjacent ecosystem — notably powering parts of Flutter’s own web presence. Lessons Shared Dart models between API, server UI, and Flutter clients reduce DTO drift. SSR/hydration is a packaging problem as much as a UI problem. Server components encourage boring, cacheable pages. Pitfalls Do not assume Flutter widgets work unchanged. Layout constraints and lifecycle differ from mobile. Consider Jaspr for content sites and dashboards where SEO matters and Dart is already your language. Originally published on FlutterCook . Read the latest version there — that copy is the one kept up to date.

Isar: fast local objects with queryable indexes

Isar: fast local objects with queryable indexes isar is useful because it focuses on collections, indexes, transactions, and reactive watchers. 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 @collection class Note { Id id = Isar .autoIncrement; late String title; @Index () late DateTime updatedAt; } await isar. writeTxn (() async { await isar.notes. put (note); }); Production checklist Read the README and changelog for the exact version you pin. Add one test for lifecycle, failure, and app background/foreground behavior. Verify every platform your produ...

intl: format data theo locale một cách có chủ đích

intl: format data theo locale một cách có chủ đích intl đáng chú ý vì nó giải quyết date, number, plural, khởi tạo locale và format có thể test. 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 amount = NumberFormat . currency ( locale : 'vi_VN' , symbol : '₫' , ). format ( 1250000 ); final date = DateFormat . yMMMMd ( 'en_US' ). format (order.createdAt); 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, iOS, web hoặc desktop đúng platform mà app hỗ trợ. ...

Immich: bài học kiến trúc từ stack ảnh Flutter 100k sao

Immich là app Flutter hiếm hoi dân self-host đều biết: backup kiểu Google Photos, album, tìm kiếm ML, và client mobile phải sống sót giữa mạng chập chờn cùng thư viện khổng lồ. Stack thực tế Client Flutter iOS/Android (cùng surface web/desktop trong monorepo). Server NestJS với API client sinh tự động — OpenAPI giữ mobile và server trung thực. Service Python ML cho tìm kiếm kiểu CLIP và nhận diện. Docker là đường cài đặt chính thức. Pattern đáng học API client sinh tự động. Đừng viết tay DTO cho domain lớn. Upload theo nền. Coi sync là feature sản phẩm, không phải việc thêm. Thumbnail do server giữ. Mobile không nên re-encode ảnh 48MP trên phone tầm trung. Ghi chú Flutter Gallery lớn phạt GridView ngây thơ. App kiểu Immich cần recycle view mạnh, chiến lược placeholder và isolate decode cẩn thận. Hãy học cách hàng đợi upload sống sót qua restart app. Nếu copy một cách ngây thơ Kéo cả monorepo vào app nhỏ. Bỏ authz vì giả định “mạng cục bộ”. Ship tìm kiếm ML ...

image_picker: API nhỏ cho các nguồn media phức tạp

image_picker: API nhỏ cho các nguồn media phức tạp image_picker đáng chú ý vì nó giải quyết intent camera/gallery, khôi phục lost data, nén ảnh và permission. 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 picker = ImagePicker (); final image = await picker. pickImage ( source : ImageSource .gallery, maxWidth : 2000 , imageQuality : 85 , ); if (image != null ) upload ( await image. readAsBytes ()); 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, iOS, web hoặc deskto...

intl: format locale-sensitive data deliberately

intl: format locale-sensitive data deliberately intl is useful because it focuses on dates, numbers, plurals, locale initialization, and testable formats. 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 amount = NumberFormat . currency ( locale : 'vi_VN' , symbol : '₫' , ). format ( 1250000 ); final date = DateFormat . yMMMMd ( 'en_US' ). format (order.createdAt); Production checklist Read the README and changelog for the exact version you pin. Add one test for lifecycle, failure, and app background/foreground behavior. Verify every platform...

Immich: architecture lessons from a 100k-star Flutter photo stack

Immich is the rare Flutter app most self-hosters have heard of: Google-Photos-like backup, albums, ML search, and a mobile client that must survive flaky networks and huge libraries. What the stack actually is Flutter mobile clients for iOS/Android (plus web/desktop surfaces elsewhere in the monorepo). NestJS server with a generated API client — OpenAPI keeps mobile and server honest. Python ML services for CLIP-style search and recognition. Docker as the blessed install path for self-hosters. Patterns worth stealing Generated API clients. Do not hand-write DTOs for a large domain. Background-first upload. Treat sync as a product feature, not an afterthought. Server-owned thumbnails. Mobile should not re-encode a 48MP original on a mid-range phone. Flutter-specific notes Large galleries punish naive GridView s. Immich-style apps need aggressive view recycling, placeholder strategy, and careful isolate use for decode. Study how upload queues persist across app res...

image_picker: a small API over messy media sources

image_picker: a small API over messy media sources image_picker is useful because it focuses on camera/gallery intent, lost data recovery, compression, and permissions. 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 picker = ImagePicker (); final image = await picker. pickImage ( source : ImageSource .gallery, maxWidth : 2000 , imageQuality : 85 , ); if (image != null ) upload ( await image. readAsBytes ()); Production checklist Read the README and changelog for the exact version you pin. Add one test for lifecycle, failure, and app background/foreground b...