Hướng Nghiệp Dữ Liệu

Tích hợp Gemini AI vào app Flutter bằng ví dụ thực tế

Đăng bởi Admin
Tích hợp Gemini AI vào app Flutter bằng ví dụ thực tế

Tích hợp Gemini AI vào app Flutter — kiến trúc, code và ví dụ thực tế

App Flutter biết gọi API thì đã nhiều; app Flutter biết suy luận mới là thứ tạo khác biệt. Đó là lý do phần "Tích hợp AI" nằm ở giai đoạn 4 trong lộ trình học Flutter và cũng là phần giúp portfolio của bạn nổi bật so với hàng trăm app CRUD giống nhau.

Bài này là bài cuối cùng trong series 4 bài về Flutter. Ba bài trước đã dựng nền tảng Dart, tư duy widget và vòng đời State; bài này dùng tất cả những gì đã học để làm một việc cụ thể: đưa Gemini vào app thật, đúng cách và không tự làm lộ API key của mình.

Kết thúc bài bạn sẽ có: một kiến trúc an toàn cho API key, một lớp service gọi Gemini, một màn hình chatbot chạy được, cách streaming phản hồi, cách ép AI trả JSON để dùng cho logic, và biết chính xác chi phí/quota để không bị "cháy ví".

Bốn cách đưa AI vào app Flutter — chọn đúng ngay từ đầu

Trước khi viết dòng code nào, hãy chọn kiến trúc. Chọn sai từ đầu thì sửa rất tốn kém, nhất là khi app đã phát hành.

Cách Ưu điểm Nhược điểm Nên dùng khi
Gọi REST trực tiếp từ app (có key trong app) Nhanh nhất, không cần backend 🔴 Lộ API key: ai tải APK cũng lấy được key, bị đốt quota Chỉ để học thử trong 1 buổi
Qua backend proxy (khuyến nghị) Key giấu ở server, kiểm soát quota, log, có thể đổi model không cần phát hành lại app Phải viết thêm một hàm server Mọi app thật
Qua Firebase AI Logic Google lo phần xác thực, App Check chống lạm dụng Phụ thuộc hệ Firebase, ít linh hoạt hơn App đã dùng Firebase
Chạy model ngay trên máy (on-device) Không tốn phí, không cần mạng Model nhỏ, chất lượng thấp hơn nhiều Tác vụ đơn giản, cần offline

Nếu bạn chỉ đọc được một dòng của bài này, hãy đọc dòng sau: không bao giờ để API key trong app phát hành lên Store. Phần tiếp theo giải thích vì sao, và cách làm đúng.

Nếu bạn quan tâm hướng đi qua Firebase (dùng Firebase AI Logic để Google lo phần xác thực, chống lạm dụng), đọc thêm Tích hợp AI vào ứng dụng Flutter với Gemini Code Assist và Firebase AI Logic. Bài bạn đang đọc tập trung vào kiến trúc proxy và code app đầy đủ.

Vì sao API key để trong app là sai — hiểu rõ trước khi làm

Nhiều hướng dẫn trên mạng làm thế này:

[object Object]

Ba lý do khiến cách này hỏng trong thực tế:

  1. File .env không an toàn như bạn nghĩ. Với app Flutter, biến môi trường được nhúng vào file build. Người khác giải nén APK/IPA là đọc được.
  2. Làm mờ chuỗi không phải là mã hoá. Chia key thành 3 đoạn rồi nối lại, đảo ký tự hay lưu Base64 chỉ chống được người không biết gì.
  3. Hậu quả thật là tiền bạc. Key lộ → bị dùng ở nơi khác → hoá đơn tăng, quota cạn, app của bạn ngừng chạy vì 429. Google có thể khoá key, và bạn phải phát hành lại app — nhưng vẫn phải vì key mới đã bị lộ ngay khi build.

Cách đúng: app gọi server của bạn, server giữ key và gọi Gemini. App không bao giờ biết key.

[object Object]

Lợi ích phụ rất đáng giá: backend có thể ghi log, giới hạn số lần gọi / thiết bị / tài khoản, cache câu hỏi trùng, và đổi model mà không cần phát hành lại app.

Bước 1 — Chuẩn bị Gemini API key và chọn model

  1. Vào Google AI Studio, tạo API key cho môi trường server (không phải key nhúng app).
  2. Chọn model theo nhu cầu. Dòng flash thường là lựa chọn hợp lý cho app di động vì nhanh và rẻ; dòng pro dùng cho tác vụ suy luận sâu. Tên model Google cập nhật khá nhanh, hãy kiểm tra lại tên chính xác tại thời điểm bạn làm — đây cũng là lý do bạn nên để tên model ở phía server (đổi được ngay, không cần phát hành app).
  3. Ghi lại quota miễn phí và giá theo token. Ước lượng thô: mỗi lượt chat 1 câu hỏi ngắn + trả lời khoảng vài trăm token; 1.000 người dùng × 10 lượt/ngày là con số bạn phải tính trước, không phải sau khi bị khoá thẻ.

Bước 2 — Backend proxy tối giản (Node.js, chạy trên Vercel)

Đây là toàn bộ phần server bạn cần. Key nằm ở biến môi trường, không nằm trong code.

[object Object]

Bốn điểm đáng chú ý trong đoạn code này, và lý do chúng quan trọng:

  • Kiểm tra đầu vào ở server (prompt.length > 4000) — chặn người dùng gửi nội dung khổng lồ để đốt quota.
  • Không trả chi tiết lỗi của Google về client — chi tiết lỗi có thể lộ thông tin cấu hình. Ghi log ở server, trả về thông báo chung.
  • systemInstruction tách riêng khỏi prompt người dùng — đây là cách đúng để định hình hành vi AI, thay vì nhồi mọi thứ vào một chuỗi.
  • Trả về đúng một trường text — app chỉ cần biết kết quả, không cần biết cấu trúc nội bộ của Gemini. Nhờ vậy bạn đổi nhà cung cấp AI mà không phải sửa app.

Thêm một lớp chống lạm dụng rất đơn giản nhưng hiệu quả: giới hạn số lượt theo thiết bị/ngày (ví dụ 30 lượt) bằng một bộ đếm trong bộ nhớ hoặc Redis. Đừng bỏ qua bước này nếu app phát hành công khai.

Bước 3 — Lớp service trong Flutter

Bây giờ đến phần Dart, dùng httpdart:convert. Cấu trúc này tách biệt rõ: model — service — UI.

[object Object]
[object Object]

Lưu ý kỹ thuật đáng chú ý:

  • systemInstruction được đặt trong service, không trong UI — mọi màn hình dùng chung một "tính cách" AI, dễ sửa một chỗ.
  • timeout là bắt buộc. Không có timeout, mạng yếu sẽ khiến người dùng chờ vô hạn và tưởng app treo.
  • Ném AiException thay vì trả null — buộc tầng UI phải xử lý lỗi, thay vì hiển thị màn hình trắng.

Bước 4 — Màn hình chatbot hoàn chỉnh

Đây là nơi mọi thứ ở bài 3 được dùng lại: StatefulWidget, ListView, TextEditingController + dispose, setState đúng cách.

[object Object]

Ba chi tiết nhỏ nhưng quyết định chất lượng:

  1. SelectableText thay vì Text — người dùng thường muốn copy code AI trả về.
  2. if (!mounted) return; sau mỗi await — đúng như bài 3 đã nói; thiếu dòng này là nguyên nhân của crash setState() called after dispose().
  3. Hiển thị lỗi như một tin nhắn thay vì hộp thoại — người dùng thấy mạch hội thoại liên tục.

Streaming: cảm giác nhanh hơn dù cùng tốc độ

Nếu chờ AI trả xong cả câu trả lời dài, người dùng sẽ thấy loading 5–10 giây. Streaming hiển thị từng phần văn bản ngay khi model sinh ra, giúp app cảm giác nhanh hơn nhiều.

Ở phía server, gọi endpoint streaming (streamGenerateContent) và chuyển tiếp từng mẩu về client. Ở phía Flutter, dùng http.Client().send() để đọc dần response:

[object Object]

Trong UI, thay vì thêm một tin nhắn khi xong, bạn nối dần vào tin nhắn cuối cùng:

[object Object]

Lưu ý: streaming làm code phức tạp hơn (phải xử lý mẩu lỗi, phải huỷ khi người dùng rời màn hình). Hãy làm bản không streaming trước cho chạy đúng, rồi nâng cấp sau khi app đã ổn.

Ép AI trả JSON để dùng cho logic, không chỉ để đọc

Đây là kỹ thuật biến AI từ "chat cho vui" thành một phần của logic app. Ví dụ: người dùng dán một đoạn mô tả công việc, app trích ra các trường có cấu trúc.

Prompt (đặt ở server):

[object Object]

Phía Flutter, hãy phòng thủ vì AI có thể trả về markdown hoặc thêm chữ:

[object Object]

Ba mẹo tăng độ ổn định khi dùng JSON:

  1. Đặt temperature thấp (0–0.3) cho tác vụ trích xuất — càng sáng tạo càng dễ sai định dạng.
  2. Nêu rõ kiểu dữ liệu từng trường trong prompt như ví dụ trên.
  3. Luôn có đường lui: nếu parse lỗi, thử lại 1 lần với câu nhắc "Chỉ trả về JSON, không giải thích", rồi mới báo lỗi cho người dùng.

Ba use case thực tế đáng làm nhất trong app Flutter

1. Trợ lý hỏi đáp trong app (chatbot). Phù hợp app học tập, tài liệu nội bộ, hỗ trợ khách hàng. Đây là use case dễ demo nhất, và cũng là use case dễ lạm dụng nhất — nên giới hạn số lượt mỗi ngày.

2. Tóm tắt nội dung. Người dùng dán một bài dài; app trả về 5 gạch đầu dòng. Prompt hiệu quả: "Tóm tắt thành tối đa 5 gạch đầu dòng, mỗi dòng dưới 20 từ, giữ số liệu quan trọng." Đây là use case tiết kiệm thời gian rõ rệt, người dùng thấy giá trị ngay.

3. Trích xuất dữ liệu có cấu trúc. Như ví dụ JSON ở trên: từ văn bản tự do → trường dữ liệu để lưu vào database. Đây là use case "ăn tiền" nhất trong app doanh nghiệp vì nó tự động hoá nhập liệu.

Một use case nữa rất hợp với hệ sinh thái học tập: gợi ý bài học tiếp theo dựa trên tiến độ người dùng. Chỉ cần truyền tiến độ dưới dạng dữ liệu và yêu cầu AI trả về 3 lựa chọn kèm lý do ngắn.

Prompt engineering mức đủ dùng cho app

Bạn không cần là chuyên gia prompt, chỉ cần 5 nguyên tắc:

  1. System instruction mô tả vai trò và giới hạn. Ví dụ: "Bạn là trợ lý Flutter, từ chối câu hỏi ngoài lập trình."
  2. Nói rõ định dạng đầu ra. Muốn JSON thì đưa mẫu JSON; muốn 5 gạch đầu dòng thì nói rõ số lượng.
  3. Đặt giới hạn độ dài. "Tối đa 150 từ" giúp tiết kiệm token và ổn định UI.
  4. Đưa 1–2 ví dụ (few-shot) khi tác vụ có định dạng phức tạp. Ví dụ mẫu luôn hiệu quả hơn lời giải thích dài.
  5. Chỉnh temperature theo việc: trích xuất/JSON nên 0–0.3; viết nội dung sáng tạo 0.7–0.9.

Và một điều rất quan trọng về trải nghiệm: luôn cho người dùng thấy AI có thể sai và có cách sửa lại câu hỏi, thay vì trình bày kết quả như chân lý.

Chi phí, quota và cách không bị "cháy ví"

Ba con số bạn phải biết trước khi phát hành:

  1. Token vào/ra mỗi lượt. Một câu hỏi ngắn + trả lời 200 từ thường vài trăm đến hơn nghìn token. Nhân với số lượt/ngày để ra chi phí.
  2. Rate limit. Khi bị vượt, API trả 429. App phải có thông báo "thử lại sau", không thử lại vô hạn — nếu không bạn tự tạo vòng lặp đốt quota.
  3. Giới hạn theo người dùng. Đặt hạn mức mỗi thiết bị/tài khoản/ngày ở phía server. Đây là "van an toàn" quan trọng nhất.

Bốn cách giảm chi phí, xếp theo mức hiệu quả:

  • Cache câu trả lời cho câu hỏi phổ biến (bảng prompt_hash → answer). App học tập thường có 30–40% câu hỏi trùng nhau.
  • Debounce: nếu làm tính năng "gợi ý khi đang gõ", chỉ gọi sau khi người dùng ngừng gõ 500ms.
  • Chọn model nhỏ cho tác vụ đơn giản, chỉ dùng model lớn cho việc khó. Để tên model ở server giúp bạn đổi bất cứ lúc nào.
  • Cắt ngắn ngữ cảnh: gửi 5 tin nhắn gần nhất thay vì cả lịch sử hội thoại.

Xử lý lỗi, mạng yếu và offline

App AI chạy trong điều kiện thật, không phải trong môi trường lý tưởng. Hãy xử lý đủ 5 tình huống:

Tình huống Cách xử lý đúng
Không có mạng Kiểm tra kết nối, thông báo rõ, cho phép thử lại; không mất nội dung đã gõ
Timeout timeout 30s, thông báo "AI phản hồi chậm", cho phép gửi lại
429 (quá quota) Thông báo "đã hết lượt hôm nay", không tự thử lại
5xx từ proxy Thử lại tối đa 1 lần, sau đó báo lỗi và ghi log
Nội dung AI không hợp lệ Parse an toàn, thử lại 1 lần, rồi hiển thị lỗi thân thiện

Một chi tiết dễ bị bỏ quên: đừng xoá nội dung người dùng đã gõ khi gặp lỗi. Người dùng ghét nhất là gõ xong 3 dòng rồi mất trắng vì app báo lỗi.

Kiểm thử lớp AI — không thể chờ mạng mỗi lần chạy test

AI trả về khác nhau mỗi lần, nên đừng test nội dung AI trả về. Hãy test phần bạn kiểm soát được:

[object Object]

Với service gọi mạng, hãy tiêm http.Client giả (mock) để trả về 200/429/500 và kiểm tra app xử lý đúng. Đây là lý do nên thiết kế AiService nhận http.Client từ ngoài thay vì tạo mới bên trong — vừa dễ test vừa dễ thay đổi sau này.

Bảo mật và quyền riêng tư: đừng bỏ qua

Khi app gửi dữ liệu người dùng lên AI, bạn đang xử lý dữ liệu của người khác. Bốn việc tối thiểu:

  1. Nói rõ trong app rằng nội dung sẽ được gửi tới dịch vụ AI bên thứ ba, và xin sự đồng ý nếu cần.
  2. Không gửi dữ liệu nhạy cảm (số điện thoại, email, thông tin thanh toán) trừ khi thật cần thiết — và nếu cần thì phải nói rõ.
  3. Không ghi log toàn bộ nội dung người dùng ở server; nếu ghi để debug, hãy cắt ngắn và có thời hạn.
  4. Giới hạn và kiểm tra đầu vào ở server để chặn prompt độc hại và nội dung quá lớn.

Lộ trình 4 bước đưa AI vào app thật

  1. Tuần 1 — chạy được: backend proxy + AiService + màn hình chat không streaming. Mục tiêu: gõ câu hỏi, nhận câu trả lời.
  2. Tuần 2 — dùng được: xử lý đủ 5 tình huống lỗi, giới hạn lượt theo người dùng, SelectableText, lưu lịch sử hội thoại cục bộ.
  3. Tuần 3 — mượt: streaming, cache câu hỏi phổ biến, debounce, chỉnh prompt theo phản hồi thật của người dùng.
  4. Tuần 4 — có giá trị: thêm use case trích xuất JSON và gợi ý theo tiến độ; đây là phần biến app demo thành app có ích.

Xong 4 tuần này, bạn có một app Flutter dùng AI đúng cách: kiến trúc an toàn, chi phí trong tầm kiểm soát, và một portfolio item mà nhà tuyển dụng nhìn thấy là hiểu ngay.

Câu hỏi thường gặp

Có cần backend riêng không, hay dùng thẳng API từ Flutter?

Để học thử trong 1 buổi thì dùng thẳng được, nhưng app phát hành lên Store thì phải có backend vì lý do bảo mật API key. Nếu bạn đã dùng Firebase, có thể dùng Firebase AI Logic để Google lo phần xác thực thay vì tự viết proxy.

Dùng package chính thức có tốt hơn gọi REST không?

Với app nhỏ, gọi REST qua http là đủ và giúp bạn hiểu bản chất. Package (google_generative_ai hoặc SDK của Firebase) tiện hơn khi bạn cần nhiều tính năng nâng cao. Điều quan trọng không phải package nào, mà là key có nằm ở server hay không.

Bao lâu thì được coi là "biết tích hợp AI vào Flutter"?

Nếu biết Flutter cơ bản, khoảng 1–2 ngày để làm được bản chat chạy được như trong bài; 2–4 tuần để làm bản đủ tốt cho người dùng thật (xử lý lỗi, quota, cache, streaming).

Số phiên bản model có cần ghi cứng trong app không?

Không nên. Hãy để tên model ở server (biến môi trường), như vậy khi Google ra model mới bạn chỉ đổi một biến, không cần phát hành lại app. Trong bài này mọi ví dụ đều đọc model từ phía server.

AI trả lời sai thì làm gì?

Ba việc theo thứ tự: (1) nói rõ với người dùng rằng kết quả do AI sinh và có thể sai; (2) cho phép sửa câu hỏi và hỏi lại dễ dàng; (3) cải thiện prompt bằng ví dụ (few-shot) cho những trường hợp sai phổ biến.

Bước tiếp theo

Bạn đã đi hết series 4 bài: chọn lộ trình, nền tảng Dart, tư duy widget, và tích hợp AI. Đường còn lại là làm dự án thật — vì chỉ dự án mới buộc bạn gặp đủ loại lỗi mà không video nào dạy hết.

Ba việc nên làm ngay:

  1. Làm bản chat ở Bước 4 của bài này, chạy được trên điện thoại thật (không chỉ emulator).
  2. Đọc lại Dart cho người mớiWidget trong Flutter khi gặp chỗ chưa chắc — hai bài đó là nền của mọi thứ trong bài này.
  3. Đọc thêm Tích hợp API trong Flutter nếu bạn muốn đi sâu phần gọi mạng, xử lý JSON và cấu trúc service.

Nếu bạn muốn đi nhanh hơn với người sửa bài trực tiếp và làm dự án thật từ đầu đến lúc phát hành, hãy xem khóa học Flutter thực chiến, lộ trình chuyên sâu hoặc lịch khai giảng.