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

Bài đăng

Firebase AI Logic trong Flutter: Gemini không cần backend riêng

Bạn không phải lúc nào cũng cần proxy Node/Go để gọi LLM từ Flutter. Firebase AI Logic ( firebase_ai ) cung cấp client typed cho Gemini, sẵn Auth, App Check và quota. Vì sao team chọn Gọi multimodal phía client (ảnh → log dinh dưỡng có cấu trúc) không phải dựng infra. Server Prompt Templates giữ system prompt và tool definition ngoài binary. Cùng project Firebase bạn đã dùng cho Crashlytics/Auth. Dáng client tối thiểu import 'package:firebase_ai/firebase_ai.dart' ; final model = FirebaseAI . googleAI (). generativeModel (model : 'gemini-2.5-flash' ); final response = await model. generateContent ([ Content . text ( 'Mô tả screenshot UI này cho bug report.' ), Content . data ( 'image/png' , pngBytes), ]); print (response.text); (API surface thay đổi theo version — ghim package và đọc docs hiện tại.) Checklist bảo mật Bật App Check. Yêu cầu Auth cho model đắt. Server template cho prompt chứa business rule. Client không g...
Các bài đăng gần đây

Xử lý lỗi trong Flutter: bắt được thứ thật sự tới tay người dùng

Tỉ lệ không-sập 99,8% nghe rất đẹp cho tới khi bạn nhận ra nó chỉ đếm những cú sập mà công cụ báo cáo của bạn được nối để nhìn thấy. Trong Flutter, một lỗi có thể thoát ra qua bốn cánh cửa khác nhau, và phần lớn ứng dụng chỉ canh một hoặc hai. Bốn cánh cửa Cánh cửa Bắt được gì Bỏ sót gì nếu không nối FlutterError.onError Lỗi bên trong framework: build, layout, paint, callback cử chỉ Màn hình đỏ, lỗi bố cục âm thầm PlatformDispatcher.instance.onError Lỗi bất đồng bộ chưa bắt ở zone gốc Phần lớn lỗi Future Isolate.current.addErrorListener Lỗi trong các isolate bạn sinh ra Mọi lỗi tính toán chạy nền Bộ xử lý sập native Sập ở tầng nền tảng, mã native của plugin Bất cứ thứ gì giết tiến trình Đây là cả bốn, nối một lần lúc khởi động: Future < void > main () async { WidgetsFlutterBinding . ensureInitialized (); await Firebase . initializeApp (); final crashlytics = FirebaseCrashlytics .instance; // 1. Lỗi của framework. FlutterError...

Flutter trên desktop: cửa sổ giờ là một phần của ứng dụng bạn

Một ứng dụng Flutter chạy được trên desktop và một ứng dụng Flutter thuộc về desktop là hai phần mềm khác nhau. Cái thứ nhất là bố cục điện thoại kéo giãn ra 1920 pixel. Cái thứ hai biết cửa sổ của mình có thể bị co lại tới mức phi lý, biết người dùng mong Cmd+W đóng một tab, biết chuột phải phải bật menu ngữ cảnh, và biết cuộn bằng trackpad không phải cùng một cử chỉ với kéo bằng ngón tay. Chẳng có gì trong số này là khó. Nó chỉ là danh sách những thứ mà di động chưa bao giờ bắt bạn nghĩ tới. Cửa sổ có vòng đời do bạn điều khiển Future < void > main () async { WidgetsFlutterBinding . ensureInitialized (); await windowManager. ensureInitialized (); const options = WindowOptions ( size : Size ( 1200 , 800 ), minimumSize : Size ( 720 , 480 ), center : true , titleBarStyle : TitleBarStyle .normal, ); await windowManager. waitUntilReadyToShow (options, () async { await windowManager. show (); await windowManager. focus ()...

Firebase AI Logic in Flutter: Gemini without a custom backend

You do not always need a Node/Go proxy to call an LLM from a Flutter app. Firebase AI Logic ( firebase_ai ) gives you a typed client for Gemini with Firebase Auth, App Check, and quota controls already in the path. Why teams pick it Client-side multimodal calls (photos → structured nutrition logs, for example) without standing up infra. Server Prompt Templates keep system prompts and tool definitions out of the binary. Works with the same Firebase project you already use for Crashlytics/Auth. Minimal client shape import 'package:firebase_ai/firebase_ai.dart' ; final model = FirebaseAI . googleAI (). generativeModel (model : 'gemini-2.5-flash' ); final response = await model. generateContent ([ Content . text ( 'Describe this UI screenshot for a bug report.' ), Content . data ( 'image/png' , pngBytes), ]); print (response.text); (Exact API surface evolves — pin the package version and read the current docs.) Security checkli...

Flutter error handling: catching what actually reaches users

A crash-free rate of 99.8% sounds excellent until you realise it only counts crashes your reporting tool was wired to see. In Flutter, an error can escape through four different doors, and most apps only guard one or two of them. The four doors Door Catches Missed if unwired FlutterError.onError Errors inside the framework: build, layout, paint, gesture callbacks Red screens, silent layout failures PlatformDispatcher.instance.onError Uncaught async errors in the root zone Most Future failures Isolate.current.addErrorListener Errors in isolates you spawned Every background-compute failure Native crash handler Platform-level crashes, plugin native code Anything that kills the process Here is all four, wired once at startup: Future < void > main () async { WidgetsFlutterBinding . ensureInitialized (); await Firebase . initializeApp (); final crashlytics = FirebaseCrashlytics .instance; // 1. Framework errors. FlutterError .onEr...

Flutter on desktop: the window is part of your app now

A Flutter app that runs on desktop and a Flutter app that belongs on desktop are different pieces of software. The first is a phone layout stretched to 1920 pixels wide. The second knows its window can be resized to something absurd, that the user expects Cmd+W to close a tab, that a right-click should produce a context menu, and that scrolling with a trackpad is not the same gesture as dragging with a finger. None of this is hard. It is just a list of things mobile never made you think about. The window has a lifecycle you control Future < void > main () async { WidgetsFlutterBinding . ensureInitialized (); await windowManager. ensureInitialized (); const options = WindowOptions ( size : Size ( 1200 , 800 ), minimumSize : Size ( 720 , 480 ), center : true , titleBarStyle : TitleBarStyle .normal, ); await windowManager. waitUntilReadyToShow (options, () async { await windowManager. show (); await windowManager. focus ...

Product flavor trên Windows và Linux trong Flutter 3.47

Build dev/staging/prod từng là chuyện riêng mobile. Flutter 3.47 mang flavor lên Windows và Linux . Asset theo flavor trong pubspec flutter : assets : - path : assets/flavor_a/images flavors : - flavor_a - path : assets/flavor_b/images flavors : - flavor_b Build flutter build windows --flavor flavor_a flutter build linux --flavor flavor_a Vì sao team desktop quan tâm App ID và icon tách biệt cho bản internal vs store. API base URL khác nhau không cần hack string trong Dart. CI matrix sinh được installer cạnh nhau. Cạm bẫy Ghép flavor với dart-define hoặc env compile-time — flavor tự nó không inject secret. Packaging desktop (MSIX, deb/rpm, AppImage) vẫn có luật identity riêng. 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.

Tiêm phụ thuộc trong Flutter mà không cần nghi thức rườm rà

Tiêm phụ thuộc có một cái tên đáng sợ cho một ý tưởng hết sức bình thường: một lớp nên được trao thứ nó cần thay vì tự dựng hoặc tự đi tìm. Toàn bộ khái niệm chỉ có vậy. Mọi thứ còn lại — container, locator, provider, mã sinh tự động — chỉ là bộ máy để giao hàng. Lý do đáng quan tâm là kiểm thử. Viết ApiClient() bên trong một repository nghĩa là mọi test của repository đó đều gọi mạng thật. Truyền ApiClient vào nghĩa là mọi test đều truyền được bản giả. Đó là toàn bộ phần thưởng, và thế là đủ. Tiêm qua constructor, không cần gói nào final class UserRepository { const UserRepository ( this ._api, this ._cache); final ApiClient _api; final UserCache _cache; Future < User > fetch ( String id) async { final cached = _cache. get (id); if (cached != null ) return cached; final user = await _api. getUser (id); _cache. put (user); return user; } } Kiểm thử nó không cần framework nào: test ( 'trả về user từ cache mà...