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

Bài đăng

Hiển thị các bài đăng có nhãn Hướng dẫn

video_player: primitive video Flutter dựa trên platform

video_player: primitive video Flutter dựa trên platform video_player đáng chú ý vì nó giải quyết controller, khởi tạo, buffering, subtitle và lifecycle. 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 controller = VideoPlayerController . networkUrl ( Uri . parse (url)); await controller. initialize (); controller. play (); return AspectRatio ( aspectRatio : controller.value.aspectRatio, child : VideoPlayer (controller), ); 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...

Very Good CLI: scaffolding Flutter có opinion

Very Good CLI: scaffolding Flutter có opinion very_good_cli đáng chú ý vì nó giải quyết template project, flavor, mặc định testing và quy ước CI. 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 dart pub global activate very_good_cli very_good create flutter_app my_app cd my_app very_good packages get very_good test -- coverage 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ợ. Ghi lại owner, upgrade cadence và kế hoạch rollback trong repo. P...

Venera: layout reader tùy chỉnh trong Flutter

App đọc trông như “chỉ là ảnh” cho tới khi page mode, RTL, zoom và pre-cache va nhau. Venera là nghiên cứu hay về layout tùy chỉnh + quản lý thư viện. Học theo Page model độc lập với page widget. Pre-cache trang kề mạnh kèm memory cap. Thư viện là local DB + nguồn remote tùy chọn. Cạm bẫy Pinch-zoom và lật trang đấu nhau; định nghĩa gesture arena sớm. Decode off UI isolate. Tham chiếu tốt khi xây viewer media scroll dà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.

url_launcher: platform intent với hợp đồng fallback

url_launcher: platform intent với hợp đồng fallback url_launcher đáng chú ý vì nó giải quyết canLaunchUrl, launch mode, web URL và handler không khả dụng. 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 uri = Uri . parse ( 'mailto:support@example.com' ); if ( await canLaunchUrl (uri)) { await launchUrl (uri, mode : LaunchMode .externalApplication); } else { showUnsupportedAction (); } 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 platfor...

supabase_flutter: auth, Postgres, realtime và storage

supabase_flutter: auth, Postgres, realtime và storage supabase_flutter đáng chú ý vì nó giải quyết khởi tạo client có kiểu, auth event, RLS và lưu session. 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 await Supabase . initialize ( url : const String . fromEnvironment ( 'SUPABASE_URL' ), publishableKey : const String . fromEnvironment ( 'SUPABASE_KEY' ), ); final supabase = Supabase .instance.client; await supabase.auth. signInWithPassword ( email : email, password : password, ); Checklist trước khi ship Đọc README và changelog của package đúng version đang pin. Viết một...

Spotube: client nhạc Flutter ưu tiên privacy

Spotube chứng minh bạn ship được trải nghiệm nhạc bóng bẩy đa mobile/desktop từ một codebase Flutter — không cần sở hữu backend catalog. Tín hiệu stack Riverpod cho state và DI. drift cho dữ liệu thư viện bền vững. media_kit (và plugin liên quan) cho playback đa nền tảng. Release store gồm F-Droid/Flathub — packaging là một phần sản phẩm. Đáng học Tách tìm catalog khỏi thư viện local . Playback là service headless, UI chỉ là shell mỏng. Persist queue và vị trí — app nhạc bị resume liên tục. Cạm bẫy API audio nền tảng khác nhau nhiều (background mode, lock screen, Bluetooth). Ràng buộc pháp lý/ToS với catalog bên thứ ba không phải vấn đề Flutter, nhưng giết app nhanh hơn một frame jank. Dùng Spotube như bản đồ mặt phẳng plugin mà app media thật cầ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.

skeletonizer: loading UI từ chính widget tree thật

skeletonizer: loading UI từ chính widget tree thật skeletonizer đáng chú ý vì nó giải quyết fake data, annotation skeleton, layout ổn định và accessibility. 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 Skeletonizer ( enabled : state.isLoading, child : ListView . builder ( itemCount : state.isLoading ? 6 : state.items.length, itemBuilder : (_, index) => ProductTile ( product : state.isLoading ? Product . fake () : state.items[index], ), ), ); 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...

shadcn_flutter: UI primitive có thể ghép cho Flutter

shadcn_flutter: UI primitive có thể ghép cho Flutter shadcn_flutter đáng chú ý vì nó giải quyết primitive ít áp đặt style, phong cách New York, Material interop và theme. 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 runApp ( ShadcnApp ( theme : ThemeData (colorScheme : ColorSchemes . lightZinc ()), home : Scaffold ( body : Center ( child : PrimaryButton (child : const Text ( 'Continue' )), ), ), ), ); 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...

shadcn-flutter: port design system, không chỉ theme

Copy design system web vào Flutter thất bại nếu chỉ copy màu. shadcn-flutter hữu ích vì ép câu chuyện phân lớp: token → primitive → composite → app của bạn. Học theo Sở hữu source (mô hình copy-in) để sửa không đau fork. Giữ accessibility và focus ring là primitive hạng nhất. Tài liệu hóa variant; đừng giấu trong một mega-widget. Cạm bẫy Giả định CSS web (cascade, :hover) không map 1:1. Ngân sách polish pointer desktop tách khỏi touch mobile. Dùng khi default Material đấu với brand 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.

Serverpod: full-stack Dart phía sau Flutter

Serverpod là backend “không, bạn không cần học ba ngôn ngữ”: server Dart, client Flutter sinh tự động, tooling database kèm theo. Vì sao team chọn Type dùng chung hết lệch JSON. Auth và scaffolding database giảm lo trang trắng. Một toolchain cho hiring và CI. Cạm bẫy Lock-in full-stack là thật — giữ domain logic di động nếu có thể tách sau. Không để secret trong path code client sinh. Cân nhắc Serverpod khi team Flutter-first và muốn backend mạch lạc không đổi ngữ cảnh Node/Go. 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.

Sentry Flutter: lỗi đi kèm context của release

Sentry Flutter: lỗi đi kèm context của release sentry_flutter đáng chú ý vì nó giải quyết crash report, trace hiệu năng, breadcrumb và source map. 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 await SentryFlutter . init ( (options) { options.dsn = const String . fromEnvironment ( 'SENTRY_DSN' ); options.tracesSampleRate = 0.1 ; options.environment = const String . fromEnvironment ( 'APP_ENV' ); }, appRunner : () => runApp ( const App ()), ); 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...

Saber: ghi chú viết tay và file local-first

App viết tay là vấn đề sâu tới RenderObject: input nét, blend highlighter, export và định dạng file đều quan trọng. Bài học File local trước; sync sau. Note phải mở được offline. Model nét là data (điểm/áp lực), không phải screenshot. Composite highlighter hai lớp là bài học graphics cổ điển trong sản phẩm. Cạm bẫy Latency stylus cảm nhận được ở ~1 frame. Lock-in định dạng file làm user giận hơn thiếu cloud. Học Saber khi xây công cụ Flutter lõi canvas . 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.

RustDesk: lõi Rust, vỏ Flutter

Không phải mọi vấn đề pixel là vấn đề Dart. RustDesk giữ capture/encode/networking trong Rust và dùng Flutter làm shell tương tác — dạng hybrid bạn nên hiểu trước khi rewrite module native bằng Dart thuần. Phân chia trách nhiệm Mục Nơi Capture màn, codec, relay Lõi Rust UX kết nối, settings, session UI Flutter Tích hợp OS Platform channel / FFI Học theo Vẽ biên cứng: vòng UI nóng vs vòng data nóng. Ưu tiên FFI cho throughput; channel cho event thưa. Giữ session state ngoài cây widget. Cạm bẫy Build hybrid nhân đôi ma trận CI. ABI và packaging installer desktop không đơn giản. App nặng UI có thể chỉ cần Flutter. App nặng pipeline hãy copy biên giới của RustDesk, chưa chắc đã copy Rust. 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.

Riverpod: state bất đồng bộ có thể test mà không dính widget

Riverpod: state bất đồng bộ có thể test mà không dính widget riverpod đáng chú ý vì nó giải quyết provider, trạng thái async, cache và override dependency. 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 usersProvider = FutureProvider < List < User >>((ref) async { final api = ref. watch (apiProvider); return api. fetchUsers (); }); class UsersView extends ConsumerWidget { const UsersView ({ super .key}); @override Widget build ( BuildContext context, WidgetRef ref) => ref. watch (usersProvider). when ( data : (users) => UserList (users), ...

Rive: animation vector tương tác bằng state machine

Rive: animation vector tương tác bằng state machine rive đáng chú ý vì nó giải quyết artboard, state machine, input và hiệu năng runtime. 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 controller = StateMachineController . fromArtboard ( artboard, 'LoginMachine' , ); if (controller != null ) { artboard. addController (controller); final success = controller. findInput < bool >( 'success' ); success ? .value = true ; } 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ề backgro...

Pigeon: platform channel có kiểu sinh từ một schema

Pigeon: platform channel có kiểu sinh từ một schema pigeon đáng chú ý vì nó giải quyết codegen Dart/native, contract API, task queue và versioning. 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 @HostApi () abstract class SecureBiometrics { bool isAvailable (); @async bool authenticate ( String reason); } // dart run pigeon --input pigeons/biometrics.dart 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ợ. Ghi lại owner, upgrade...

permission_handler: luồng xin quyền runtime có thể giải thích

permission_handler: luồng xin quyền runtime có thể giải thích permission_handler đáng chú ý vì nó giải quyết status, rationale, mở settings fallback và khai báo platform. 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 status = await Permission .camera. request (); if (status.isGranted) { openCamera (); } else if (status.isPermanentlyDenied) { await openAppSettings (); } 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ợ. Ghi ...

Patrol: integration test có thể đi ra ngoài sandbox Flutter

Patrol: integration test có thể đi ra ngoài sandbox Flutter patrol đáng chú ý vì nó giải quyết permission native, notification, deep link và tự động hóa thiết bị. 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 patrolTest ( 'camera permission flow' , ($) async { await $. native . grantPermissionWhenInUse (); await $. pumpWidgetAndSettle ( const CameraApp ()); await $(#takePhoto). tap (); expect (find. byType ( PhotoPreview ), findsOneWidget); }); 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ề backgr...

Mocktail: mock Dart dễ đọc mà không cần file generated

Mocktail: mock Dart dễ đọc mà không cần file generated mocktail đáng chú ý vì nó giải quyết fake, stub, verify, fallback value và ranh giới 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 class MockUserApi extends Mock implements UserApi {} final api = MockUserApi (); when (() => api. fetchUser ( '42' )). thenAnswer ((_) async => user); final result = await repository. load ( '42' ); verify (() => api. fetchUser ( '42' )). called ( 1 ); 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à...

Melos: biến Flutter monorepo thành ranh giới sản phẩm

Melos: biến Flutter monorepo thành ranh giới sản phẩm melos đáng chú ý vì nó giải quyết script workspace, package graph, test chọn lọc và release. 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 name : flutter_workspace packages : - apps /** - packages/** scripts: analyze: melos exec -- flutter analyze test: melos exec --fail-fast -- flutter test changed: melos exec --since=main -- flutter test 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 platfor...