Chuyển tới nội dung chính

11 bài viết được gắn thẻ "flutter"

Xem tất cả thẻ

So sánh Flutter và React Native: Đâu là lựa chọn tốt hơn?

· 8 phút để đọc

Flutter và React Native là hai framework phổ biến để phát triển ứng dụng di động đa nền tảng. Cả hai đều có những ưu và nhược điểm riêng. Hãy cùng so sánh chi tiết để giúp bạn đưa ra lựa chọn phù hợp cho dự án của mình.


1️⃣ Ngôn Ngữ Lập Trình​

  • Flutter: Sử dụng Dart, một ngôn ngữ do Google phát triển.
  • React Native: Sử dụng JavaScript (hoặc TypeScript), phổ biến và có cộng đồng hỗ trợ rộng lớn.

📌 Kết luận: Nếu bạn đã quen với JavaScript, React Native sẽ dễ tiếp cận hơn. Nếu muốn hiệu suất cao, hãy chọn Flutter.


2️⃣ Hiệu Suất​

🔹 Flutter​

✅ Sử dụng Skia Engine để render UI giúp ứng dụng mượt mà.
✅ Biên dịch mã Dart trực tiếp thành mã máy (native code), tối ưu hiệu suất.

🔹 React Native​

⚠️ Chạy JavaScript thông qua JavaScriptCore, không biên dịch trực tiếp thành mã máy.
⚠️ Giao tiếp với native components thông qua Bridge, có thể gây độ trễ.

📌 Kết luận: Flutter có hiệu suất tốt hơn vì không cần thông qua Bridge.


3️⃣ UI Components​

🔹 Flutter​

✅ Cung cấp bộ widget riêng biệt, không phụ thuộc vào native components.
✅ Giao diện đồng nhất trên iOS & Android.

🔹 React Native​

⚠️ Dựa vào native components của hệ điều hành, có thể khác biệt giữa iOS và Android.
⚠️ Cần thư viện bên thứ ba để có UI đồng nhất.

📌 Kết luận: Flutter tốt hơn nếu bạn muốn giao diện giống nhau trên mọi nền tảng.


4️⃣ Hỗ Trợ Cộng Đồng và Tài Liệu​

Tiêu chíFlutterReact Native
Cộng đồngĐang phát triển nhanhLớn và lâu đời
Tài liệuChi tiết, dễ hiểuĐầy đủ nhưng đôi khi khó cập nhật
Thư việnÍt hơn do mớiNhiều thư viện bên thứ ba

📌 Kết luận: React Native có lợi thế nhờ hệ sinh thái lớn hơn.


5️⃣ Thời Gian Phát Triển​

  • Cả hai framework đều hỗ trợ Hot Reload, giúp lập trình nhanh hơn.
  • Flutter có bộ widget sẵn có, giúp giảm thời gian phát triển giao diện.
  • React Native có thể mất thời gian khi tích hợp native modules.

📌 Kết luận: Nếu không cần nhiều native modules, Flutter sẽ nhanh hơn.


6️⃣ Hỗ Trợ Native Code​

  • Flutter sử dụng Platform Channels, nhưng hơi phức tạp.
  • React Native dễ tích hợp với native code hơn.

📌 Kết luận: React Native tốt hơn nếu dự án cần nhiều native modules.


7️⃣ Hệ Sinh Thái​

Tiêu chíFlutterReact Native
Gói thư việnĐang phát triểnLớn và đa dạng
Hỗ trợ WebCó (Flutter Web)Hạn chế
Hỗ trợ DesktopCóKhông

📌 Kết luận: Flutter hỗ trợ đa nền tảng tốt hơn.


8️⃣ Hỗ Trợ Từ Công Ty​

  • Flutter: Được Google phát triển và đầu tư mạnh.
  • React Native: Do Facebook phát triển nhưng không còn tập trung nhiều.

📌 Kết luận: Google có vẻ đầu tư dài hạn hơn vào Flutter.


🎯 Kết Luận​

Tiêu chíNên chọn Flutter nếu...Nên chọn React Native nếu...
Hiệu suấtCần tốc độ cao, không delayCó thể chấp nhận hiệu suất thấp hơn
Giao diệnMuốn UI đồng nhất trên iOS & AndroidMuốn dùng native components
Thời gian devMuốn nhanh chóng với widget có sẵnĐã có sẵn nhiều thư viện React Native
Hệ sinh tháiChấp nhận hệ sinh thái nhỏ hơnMuốn dùng nhiều thư viện JS có sẵn
Tích hợp nativeKhông cần nhiều native modulesCần tích hợp native code dễ dàng hơn

📌 Tóm lại:

  • Chọn Flutter nếu bạn muốn hiệu suất cao, UI đồng nhất, hỗ trợ Web/Desktop.
  • Chọn React Native nếu bạn quen JavaScript và cần hệ sinh thái lớn hơn.

🚀 Bạn sẽ chọn framework nào? Bình luận bên dưới nhé! 👇


So sánh chi tiết — không chỉ dựa trên cảm nhận​

Hiệu năng​

Tiêu chíFlutterReact Native
Cách renderVẽ trực tiếp lên canvas (Skia/Impeller)Gọi widget native qua bridge
Animation phức tạpMượt hơn, ổn định hơnPhụ thuộc vào bridge
Danh sách dàiRất tốtTốt, cần tối ưu
Khởi động appNhanh hơnChậm hơn
Kích thước appLớn hơn (~15–25 MB)Nhỏ hơn (~8–15 MB)
Hiệu năng tổng thểNhỉnh hơnCó thể đủ tốt

Lý do kỹ thuật: Flutter biên dịch sang mã máy (AOT), còn React Native cần cầu JavaScript ↔ Native. New Architecture (JSI/Fabric) đã thu hẹp khoảng cách đáng kể, nhưng Flutter vẫn ổn định hơn ở UI phức tạp.

Hệ sinh thái thư viện​

Tiêu chíFlutterReact Native
Số package~50.000~20.000
Chất lượng trung bìnhTốtRất tốt (từ hệ sinh thái JS)
Official packagesTốtCần cộng đồng nhiều hơn
Thư viện thanh toánRất tốtTốt
SDK bên thứ baTốt dầnNhiều hơn (nhiều SDK chỉ có RN)
Tìm lời giải trên mạngÍt hơnRất nhiều

⚠️ Điểm yếu thực tế của Flutter: một số SDK (thanh toán, analytics, quảng cáo) chỉ phát hành chính thức cho React Native. Với Flutter, bạn phải dùng package cộng đồng hoặc tự viết platform channel.

Tuyển dụng và thị trường​

Tiêu chíFlutterReact Native
Số tin tuyển dụngNhiều hơn ở Việt NamNhiều ở thị trường quốc tế
Mức lươngTương đươngTương đương
Dễ chuyển từ webKhó (Dart mới)Rất dễ (đã biết JS/TS)
Dễ học từ đầuTrung bìnhTrung bình

Năng suất phát triển​

Flutter:
✅ Hot reload rất nhanh
✅ UI nhất quán trên mọi nền tảng (một codebase, một giao diện)
✅ Widget library đầy đủ, không cần thư viện UI
❌ Dart ít phổ biến hơn JS
❌ Phải tự làm nhiều thứ native cần platform channel

React Native:
✅ Biết JS là làm được
✅ Rất nhiều thư viện UI có sẵn (NativeBase, Paper)
✅ Cộng đồng lớn, tài liệu nhiều
❌ UI khác nhau giữa iOS/Android (đôi khi là điểm cộng)
❌ Cấu hình native phức tạp hơn khi cần

Kotlin Multiplatform — lựa chọn thứ ba đang lên​

Tiêu chíFlutterReact NativeKotlin Multiplatform
Chia sẻ code~95%~85%60–90%
UIChia sẻ hoàn toànChia sẻ hoàn toànNative riêng (hoặc Compose)
Hiệu năngCaoTrung bình–caoNative
Đường cong họcTrung bìnhTrung bìnhCao (cần biết Kotlin + Swift)
Trưởng thànhCaoCaoĐang lên
Phù hợpStartup, app mớiTeam JSTeam mobile native có kinh nghiệm

Cách quyết định​

Tình huống của bạnNên chọn
App mới, muốn đồng nhất iOS/AndroidFlutter
Team đã mạnh JavaScript/ReactReact Native
Cần UI rất tuỳ biến, animation phức tạpFlutter
Cần tích hợp nhiều SDK native có sẵn cho RNReact Native
Phải hỗ trợ cả web và desktopFlutter
Đang làm native, muốn chia sẻ dần logicKotlin Multiplatform
Muốn tuyển người dễ nhất ở Việt NamFlutter
App game 2DFlutter (Flame) hoặc Unity

💡 Sự thật cần nói thẳng: với 90% app doanh nghiệp (form, danh sách, gọi API, thanh toán), cả hai đều làm tốt như nhau. Chọn cái nào team bạn quen hơn quan trọng hơn nhiều so với benchmark.

Lời khuyên học tập​

Nếu bạn muốn vào làm mobile nhanh nhất ở Việt Nam: học Flutter. Nhu cầu tuyển dụng cao, cộng đồng tiếng Việt nhiều, và bạn dùng được một codebase cho cả hai nền tảng.

Nếu bạn đã biết React/JavaScript: học React Native — bạn sẽ có việc trong vài tháng vì chuyển đổi kỹ năng rất nhanh.


🔗 Tham Khảo​

Một số hình ảnh buổi Khai giảng khóa học Flutter Level 3

· 2 phút để đọc

🚀 Hôm nay, Hướng Nghiệp Dữ Liệu chính thức khai giảng khóa học Flutter Level 3 với chủ đề cực kỳ hấp dẫn: Tích hợp Backend, API và AI trong lập trình Flutter. Đây là cột mốc quan trọng trong lộ trình trở thành Flutter Developer chuyên nghiệp của các bạn học viên.

Flutter Growth

🕢 Thời gian khai giảng: 07h30, ngày 08/03/2025
🗓️ Lịch học: Thứ 7 và Chủ nhật hàng tuần

💡 Nội dung khóa học​

  • Kết nối Backend: REST API, GraphQL, WebSocket, Firebase
  • Triển khai Authentication: JWT, OAuth2
  • Lưu trữ dữ liệu với Firestore, SQLite, Hive
  • Tích hợp AI: OCR, Face Recognition, Speech to Text, Chatbot AI, Object Detection

🎯 Mục tiêu khóa học​

  • Xây dựng ứng dụng Flutter kết nối Backend, API mượt mà
  • Tích hợp các tính năng AI tiên tiến vào mobile app
  • Phát triển ứng dụng real-time với Firebase Cloud Firestore & WebSocket
  • Quản lý trạng thái với Provider, Riverpod, BLoC
  • Triển khai backend với Node.js + Express
  • Đưa app lên Google Play & App Store

👩‍💻 Đối tượng học viên​

  • Lập trình viên Flutter muốn nâng cao kỹ năng
  • Mobile Developer phát triển ứng dụng kết nối API & Backend
  • Backend Developer học cách tích hợp API vào Flutter
  • AI Enthusiast khám phá ứng dụng AI trên mobile app
  • Sinh viên/Freelancer xây dựng ứng dụng thực tế với API & AI

📝 Đăng ký khóa học​

👉 Đăng ký tại đây http://zalo.me/0583587833

📚 Tài liệu khóa học​

Hẹn gặp các bạn vào ngày khai giảng! 🚀

Một số hình ảnh buổi Khai giảng khóa học Flutter Level 3

· 2 phút để đọc

🚀 Hôm nay, Hướng Nghiệp Dữ Liệu chính thức khai giảng khóa học Flutter Level 3 với chủ đề cực kỳ hấp dẫn: Tích hợp Backend, API và AI trong lập trình Flutter. Đây là cột mốc quan trọng trong lộ trình trở thành Flutter Developer chuyên nghiệp của các bạn học viên.

Hình ảnh Khai giảng

🕢 Thời gian khai giảng: 07h30, ngày 08/03/2025
🗓️ Lịch học: Thứ 7 và Chủ nhật hàng tuần

💡 Nội dung khóa học​

  • Kết nối Backend: REST API, GraphQL, WebSocket, Firebase
  • Triển khai Authentication: JWT, OAuth2
  • Lưu trữ dữ liệu với Firestore, SQLite, Hive
  • Tích hợp AI: OCR, Face Recognition, Speech to Text, Chatbot AI, Object Detection

🎯 Mục tiêu khóa học​

  • Xây dựng ứng dụng Flutter kết nối Backend, API mượt mà
  • Tích hợp các tính năng AI tiên tiến vào mobile app
  • Phát triển ứng dụng real-time với Firebase Cloud Firestore & WebSocket
  • Quản lý trạng thái với Provider, Riverpod, BLoC
  • Triển khai backend với Node.js + Express
  • Đưa app lên Google Play & App Store

👩‍💻 Đối tượng học viên​

  • Lập trình viên Flutter muốn nâng cao kỹ năng
  • Mobile Developer phát triển ứng dụng kết nối API & Backend
  • Backend Developer học cách tích hợp API vào Flutter
  • AI Enthusiast khám phá ứng dụng AI trên mobile app
  • Sinh viên/Freelancer xây dựng ứng dụng thực tế với API & AI

📝 Đăng ký khóa học​

👉 Đăng ký tại đây http://zalo.me/0583587833

📚 Tài liệu khóa học​

Hẹn gặp các bạn vào ngày khai giảng! 🚀

Khai giảng khóa học Flutter Level 3

· 2 phút để đọc

📚 Nội Dung Chi Tiết Khóa Học Flutter Level 3

Khóa học Flutter Level 3 được thiết kế nhằm giúp bạn trở thành một Flutter Developer chuyên nghiệp với khả năng xây dựng ứng dụng mobile mạnh mẽ, kết nối Backend, triển khai API và tích hợp công nghệ AI. Dưới đây là nội dung chi tiết mà bạn sẽ học:


Phần 1: Kết Nối Backend & Triển Khai API​

  • REST API: Tạo, đọc, cập nhật, xóa dữ liệu thông qua API.
  • GraphQL: Tối ưu truy vấn dữ liệu linh hoạt.
  • WebSocket: Xây dựng ứng dụng real-time.
  • Firebase: Kết nối Backend không cần server.

🔐 Phần 2: Authentication & Bảo Mật​

  • JWT (JSON Web Token): Xác thực người dùng.
  • OAuth2: Tích hợp đăng nhập Google, Facebook.
  • Firebase Auth: Quản lý người dùng.

Phần 3: Lưu Trữ Dữ Liệu​

  • SQLite: Lưu trữ cục bộ.
  • Hive: Database nhẹ, nhanh.
  • Firebase Cloud Firestore: Lưu trữ dữ liệu real-time.

Phần 4: Tích Hợp Công Nghệ AI​

  • OCR: Nhận diện ký tự từ hình ảnh.
  • Face Recognition: Nhận diện khuôn mặt.
  • Speech to Text: Chuyển giọng nói thành văn bản.
  • Chatbot AI: Xây dựng chatbot thông minh.
  • Object Detection: Nhận diện vật thể qua camera.

Phần 5: Xây Dựng & Triển Khai Ứng Dụng​

  • Quản lý trạng thái: Sử dụng Provider, Riverpod, BLoC.
  • Triển khai backend: Xây dựng API với Node.js + Express.
  • Tối ưu hiệu năng: Cải thiện tốc độ và trải nghiệm người dùng.
  • Deploy: Đưa ứng dụng lên Google Play & App Store.

🎓 Kết Quả Đạt Được​

Hoàn thành khóa học, bạn sẽ:

  • Thành thạo xây dựng ứng dụng Flutter kết nối API mượt mà.
  • Tự tin tích hợp AI vào mobile app.
  • Phát triển ứng dụng real-time chuyên nghiệp.
  • Đưa ứng dụng của mình lên store chính thức.

Hãy sẵn sàng cho hành trình chinh phục Flutter Level 3! 🚀


📚 Tài liệu khóa học​

Hẹn gặp các bạn vào ngày khai giảng! 🚀

Khai giảng khóa học Flutter Level 3

· 2 phút để đọc

🚀 Hôm nay, Hướng Nghiệp Dữ Liệu chính thức khai giảng khóa học Flutter Level 3 với chủ đề cực kỳ hấp dẫn: Tích hợp Backend, API và AI trong lập trình Flutter. Đây là cột mốc quan trọng trong lộ trình trở thành Flutter Developer chuyên nghiệp của các bạn học viên.

🕢 Thời gian khai giảng: 07h30, ngày 08/03/2025
🗓️ Lịch học: Thứ 7 và Chủ nhật hàng tuần

💡 Nội dung khóa học​

  • Kết nối Backend: REST API, GraphQL, WebSocket, Firebase
  • Triển khai Authentication: JWT, OAuth2
  • Lưu trữ dữ liệu với Firestore, SQLite, Hive
  • Tích hợp AI: OCR, Face Recognition, Speech to Text, Chatbot AI, Object Detection

🎯 Mục tiêu khóa học​

  • Xây dựng ứng dụng Flutter kết nối Backend, API mượt mà
  • Tích hợp các tính năng AI tiên tiến vào mobile app
  • Phát triển ứng dụng real-time với Firebase Cloud Firestore & WebSocket
  • Quản lý trạng thái với Provider, Riverpod, BLoC
  • Triển khai backend với Node.js + Express
  • Đưa app lên Google Play & App Store

👩‍💻 Đối tượng học viên​

  • Lập trình viên Flutter muốn nâng cao kỹ năng
  • Mobile Developer phát triển ứng dụng kết nối API & Backend
  • Backend Developer học cách tích hợp API vào Flutter
  • AI Enthusiast khám phá ứng dụng AI trên mobile app
  • Sinh viên/Freelancer xây dựng ứng dụng thực tế với API & AI

📝 Đăng ký khóa học​

👉 Đăng ký tại đây http://zalo.me/0583587833

📚 Tài liệu khóa học​

Hẹn gặp các bạn vào ngày khai giảng! 🚀

Kết Nối API RESTful trong Flutter: Hướng Dẫn Chi Tiết

· 8 phút để đọc

Kết nối API RESTful là một kỹ năng quan trọng trong phát triển ứng dụng di động, đặc biệt là khi bạn cần tương tác với các dịch vụ backend để lấy dữ liệu hoặc thực hiện các thao tác CRUD (Create, Read, Update, Delete). Trong bài viết này, chúng ta sẽ tìm hiểu cách kết nối API RESTful trong Flutter bằng thư viện http và dio. Flutter


1. Giới Thiệu về RESTful API​

REST (Representational State Transfer) là một kiến trúc phần mềm được sử dụng rộng rãi để thiết kế các hệ thống mạng, đặc biệt là các ứng dụng web. RESTful API là các API tuân thủ các nguyên tắc của REST.

Các phương thức HTTP phổ biến trong RESTful API:

  • GET: Lấy dữ liệu từ server.
  • POST: Gửi dữ liệu mới lên server.
  • PUT: Cập nhật dữ liệu đã có trên server.
  • DELETE: Xóa dữ liệu trên server.

2. Kết Nối API RESTful với Thư Viện http​

Thư viện http là một thư viện đơn giản và phổ biến trong Flutter để thực hiện các yêu cầu HTTP. Dưới đây là các bước để kết nối API RESTful với thư viện http.

Bước 1: Thêm Thư Viện http vào Project​

Thêm thư viện http vào file pubspec.yaml:

dependencies:
flutter:
sdk: flutter
http: ^0.13.3
Bước 2: Gọi API với Phương Thức GET
Dưới đây là ví dụ về cách gọi API với phương thức GET để lấy dữ liệu từ server:

dart
Copy
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

class GetDataExample extends StatefulWidget {
@override
_GetDataExampleState createState() => _GetDataExampleState();
}

class _GetDataExampleState extends State<GetDataExample> {
List<dynamic> data = [];

Future<void> fetchData() async {
final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts'));

if (response.statusCode == 200) {
setState(() {
data = json.decode(response.body);
});
} else {
throw Exception('Failed to load data');
}
}

@override
void initState() {
super.initState();
fetchData();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Fetch Data Example'),
),
body: ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(data[index]['title']),
subtitle: Text(data[index]['body']),
);
},
),
);
}
}
Bước 3: Gọi API với Phương Thức POST
Dưới đây là ví dụ về cách gọi API với phương thức POST để gửi dữ liệu lên server:

dart
Copy
Future<void> postData() async {
final response = await http.post(
Uri.parse('https://jsonplaceholder.typicode.com/posts'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, String>{
'title': 'foo',
'body': 'bar',
'userId': '1',
}),
);

if (response.statusCode == 201) {
print('Data posted successfully');
} else {
throw Exception('Failed to post data');
}
}
3. Kết Nối API RESTful với Thư Viện dio
Thư viện dio là một thư viện mạnh mẽ hơn so với http, hỗ trợ nhiều tính năng nâng cao như interceptors, timeout, retry request, và hỗ trợ upload file. Dưới đây là các bước để kết nối API RESTful với thư viện dio.

Bước 1: Thêm Thư Viện dio vào Project
Thêm thư viện dio vào file pubspec.yaml:

yaml
Copy
dependencies:
flutter:
sdk: flutter
dio: ^4.0.0
Bước 2: Gọi API với Phương Thức GET
Dưới đây là ví dụ về cách gọi API với phương thức GET bằng dio:

dart
Copy
import 'package:dio/dio.dart';

Future<void> fetchDataWithDio() async {
final dio = Dio();
final response = await dio.get('https://jsonplaceholder.typicode.com/posts');

if (response.statusCode == 200) {
print(response.data);
} else {
throw Exception('Failed to load data');
}
}
Bước 3: Gọi API với Phương Thức POST
Dưới đây là ví dụ về cách gọi API với phương thức POST bằng dio:

dart
Copy
Future<void> postDataWithDio() async {
final dio = Dio();
final response = await dio.post(
'https://jsonplaceholder.typicode.com/posts',
data: {
'title': 'foo',
'body': 'bar',
'userId': '1',
},
);

if (response.statusCode == 201) {
print('Data posted successfully');
} else {
throw Exception('Failed to post data');
}
}
4. Xử Lý Lỗi và Hiển Thị Thông Báo
Khi kết nối API, việc xử lý lỗi là rất quan trọng để đảm bảo ứng dụng của bạn hoạt động ổn định. Dưới đây là cách xử lý lỗi và hiển thị thông báo cho người dùng:

dart
Copy
Future<void> fetchData() async {
try {
final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts'));

if (response.statusCode == 200) {
setState(() {
data = json.decode(response.body);
});
} else {
throw Exception('Failed to load data');
}
} catch (e) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Error'),
content: Text('Failed to load data: $e'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('OK'),
),
],
),
);
}
}
## Đi sâu: phân trang, upload file, và xử lý lỗi

### Phân trang — bắt buộc với mọi danh sách thật

```dart
class DanhSachPhanTrang<T> {
final List<T> duLieu;
final int trang;
final int tongTrang;
final bool conNua;

const DanhSachPhanTrang({
required this.duLieu,
required this.trang,
required this.tongTrang,
required this.conNua,
});
}

Future<DanhSachPhanTrang<SanPham>> laySanPham({
required int trang,
int kichThuoc = 20,
}) async {
final json = await api.get('/san-pham', query: {
'page': '$trang',
'limit': '$kichThuoc',
});

final ds = (json['du_lieu'] as List)
.map((e) => SanPham.fromJson(e as Map<String, dynamic>))
.toList();

final tongSo = json['tong'] as int;
return DanhSachPhanTrang(
duLieu: ds,
trang: trang,
tongTrang: (tongSo / kichThuoc).ceil(),
conNua: trang * kichThuoc < tongSo,
);
}

Upload file — hai cách và khi nào dùng​

import 'package:http_parser/http_parser.dart';

// CÁCH 1: multipart/form-data — phổ biến nhất
Future<String> uploadAnh(File file) async {
final request = http.MultipartRequest(
'POST',
Uri.parse('$baseUrl/upload'),
)..headers['Authorization'] = 'Bearer $token'
..files.add(await http.MultipartFile.fromPath(
'file',
file.path,
// Đặt content-type tường minh — nhiều server từ chối nếu thiếu
contentType: MediaType('image', 'jpeg'),
));

final streamed = await request.send();
final phanHoi = await http.Response.fromStream(streamed);

if (phanHoi.statusCode != 200 && phanHoi.statusCode != 201) {
throw Exception('Upload thất bại: ${phanHoi.statusCode}');
}
return jsonDecode(phanHoi.body)['url'] as String;
}
CáchKhi nào dùngGhi chú
multipart/form-dataẢnh, tài liệu, file nhị phânChuẩn, mọi backend đều hỗ trợ
base64 trong JSONFile rất nhỏ (< 100 KB)Tốn 33% dung lượng, chậm
Upload trực tiếp lên S3/CloudinaryFile lớnCần lấy signed URL từ backend trước

Xử lý lỗi có backoff — tránh bị chặn​

Future<T> thuLaiVoiBackoff<T>(
Future<T> Function() tacVu, {
int soLanToiDa = 3,
}) async {
Duration cho = const Duration(seconds: 1);

for (int lan = 0; lan < soLanToiDa; lan++) {
try {
return await tacVu();
} on ApiException catch (e) {
final nenThuLai = e.statusCode == 429 ||
(e.statusCode != null && e.statusCode! >= 500);

if (!nenThuLai || lan == soLanToiDa - 1) rethrow;

await Future.delayed(cho);
cho *= 2; // backoff theo cấp số nhân: 1s → 2s → 4s
}
}
throw ApiException('Đã hết số lần thử lại');
}

💡 Vì sao cần backoff: khi server quá tải (trả 429 hoặc 503), gửi lại ngay lập tức làm tình hình tệ hơn. Cấp số nhân cho server thời gian hồi phục — và cũng là điều các API lớn yêu cầu trong tài liệu của họ.

Hiển thị lỗi cho người dùng — đừng bao giờ hiện stack trace​

String thongDiepThanThien(Object loi) {
if (loi is ApiException) {
if (loi.statusCode == 401) return 'Phiên đăng nhập đã hết hạn';
if (loi.statusCode == 403) return 'Bạn không có quyền thực hiện thao tác này';
if (loi.statusCode == 404) return 'Không tìm thấy dữ liệu';
if (loi.statusCode == 429) return 'Vui lòng chờ một lát rồi thử lại';
if (loi.statusCode != null && loi.statusCode! >= 500) {
return 'Máy chủ đang gặp sự cố. Vui lòng thử lại sau.';
}
return loi.message;
}
return 'Đã xảy ra lỗi. Vui lòng thử lại.';
}

Ba nguyên tắc thông báo lỗi:

  1. Nói cho người dùng biết họ nên làm gì, không phải lỗi là gì. "Không kết nối được máy chủ" tốt hơn "SocketException".
  2. Không bao giờ hiển thị chi tiết kỹ thuật — lộ thông tin hệ thống và gây bối rối.
  3. Luôn log lỗi gốc để lập trình viên còn gỡ được.

5. Kết Luận​

Kết nối API RESTful là một kỹ năng quan trọng trong phát triển ứng dụng di động với Flutter. Bằng cách sử dụng các thư viện như http và dio, bạn có thể dễ dàng tương tác với các dịch vụ backend để lấy dữ liệu, gửi dữ liệu, và thực hiện các thao tác CRUD. Trong khóa học Flutter Level 3 của chúng tôi, bạn sẽ được học cách kết nối API RESTful một cách chuyên sâu, cùng với các kỹ thuật nâng cao như authentication, phân trang, và bảo mật API.

Nhu Cầu Học Flutter Hiện Nay

· 5 phút để đọc

Flutter là một framework phát triển ứng dụng di động đa nền tảng được Google phát triển và ra mắt vào năm 2017. Từ đó đến nay, Flutter đã trở thành một trong những công nghệ phát triển ứng dụng di động phổ biến nhất, thu hút sự quan tâm của cả nhà phát triển và doanh nghiệp. Dưới đây là một số thông tin về nhu cầu học Flutter hiện nay:


1. Tăng Trưởng Mạnh Mẽ​

  • Theo báo cáo từ Stack Overflow Developer Survey 2023, Flutter là một trong những framework phổ biến nhất dành cho phát triển ứng dụng di động, đứng thứ 3 trong danh sách các công nghệ được yêu thích.
  • Flutter được sử dụng rộng rãi trong các dự án startup và doanh nghiệp lớn nhờ khả năng phát triển nhanh chóng và tiết kiệm chi phí.

Flutter Growth


2. Nhu Cầu Tuyển Dụng Cao​

  • Các công ty công nghệ lớn như Google, Alibaba, BMW, và nhiều công ty khác đang sử dụng Flutter để phát triển ứng dụng của họ.
  • Trên các trang tuyển dụng như LinkedIn, Indeed, và TopDev, số lượng công việc liên quan đến Flutter đang tăng đáng kể, đặc biệt là ở các thị trường như Mỹ, Châu Âu, và Châu Á.

3. Cộng Đồng Phát Triển Mạnh Mẽ​

  • Flutter có một cộng đồng phát triển lớn và năng động, với hàng trăm nghìn nhà phát triển trên toàn thế giới.
  • Các diễn đàn, nhóm Facebook, và các sự kiện hackathon liên quan đến Flutter ngày càng phổ biến.

4. Nhu Cầu Học Tập Tăng Cao​

  • Các Dịch vụ và Khóa học Flutter trên các nền tảng học trực tuyến như Udemy, Coursera, và Pluralsight đều có số lượng học viên đăng ký cao.
  • Tại Việt Nam, các trung tâm đào tạo công nghệ cũng đang mở rộng Các Dịch vụ và Khóa học Flutter để đáp ứng nhu cầu của thị trường.

Ưu Điểm Của Flutter

Flutter có nhiều ưu điểm nổi bật so với các công nghệ phát triển ứng dụng di động khác, giúp nó trở thành lựa chọn hàng đầu của nhiều nhà phát triển và doanh nghiệp. Dưới đây là một số ưu điểm chính của Flutter:


1. Đa Nền Tảng (Cross-Platform)​

  • Flutter cho phép phát triển ứng dụng chạy trên cả iOS và Android chỉ với một codebase duy nhất, giúp tiết kiệm thời gian và chi phí phát triển.

2. Hiệu Suất Cao​

  • Flutter sử dụng Dart làm ngôn ngữ lập trình và Skia làm engine đồ họa, giúp ứng dụng chạy mượt mà và có hiệu suất gần như native app.

3. Hot Reload​

  • Tính năng Hot Reload cho phép nhà phát triển xem ngay kết quả thay đổi code mà không cần khởi động lại ứng dụng, giúp tăng tốc độ phát triển và debug.

4. UI Đẹp và Linh Hoạt​

  • Flutter cung cấp một thư viện widget phong phú và có thể tùy chỉnh cao, giúp tạo ra giao diện người dùng đẹp mắt và nhất quán trên cả hai nền tảng.

5. Tích Hợp Dễ Dàng với Native Code​

  • Flutter cho phép tích hợp với native code (Java/Kotlin cho Android và Swift/Objective-C cho iOS), giúp tận dụng các tính năng đặc thù của từng nền tảng.

6. Hỗ Trợ Tốt cho Backend và API​

  • Flutter dễ dàng tích hợp với các dịch vụ backend như Firebase, REST API, GraphQL, và WebSocket, giúp xây dựng ứng dụng real-time và có khả năng mở rộng cao.

7. Cộng Đồng và Tài Liệu Phong Phú​

  • Flutter có tài liệu chính thức chi tiết và cộng đồng lớn mạnh, giúp nhà phát triển dễ dàng học hỏi và giải quyết vấn đề.

8. Hỗ Trợ AI và Machine Learning​

  • Flutter có thể tích hợp với các công nghệ AI như TensorFlow Lite, Google ML Kit, và OpenAI, giúp xây dựng các ứng dụng thông minh như nhận diện khuôn mặt, chatbot AI, và phân loại hình ảnh.

9. Dễ Dàng Triển Khai​

  • Flutter hỗ trợ build và deploy ứng dụng lên cả Google Play Store và Apple App Store một cách dễ dàng, với các công cụ CI/CD như Codemagic và GitHub Actions.

10. Chi Phí Thấp​

  • Với khả năng phát triển đa nền tảng, Flutter giúp giảm chi phí phát triển và bảo trì ứng dụng so với việc phát triển riêng biệt cho từng nền tảng.

Kết Luận

Flutter đang trở thành một trong những công nghệ hàng đầu trong lĩnh vực phát triển ứng dụng di động nhờ những ưu điểm vượt trội về hiệu suất, tính linh hoạt, và khả năng đa nền tảng. Với nhu cầu tuyển dụng cao và cộng đồng phát triển mạnh mẽ, học Flutter là một lựa chọn thông minh cho những ai muốn theo đuổi sự nghiệp trong lĩnh vực phát triển ứng dụng di động.

Quản Lý Trạng Thái (State Management) trong Flutter: Hướng Dẫn Chi Tiết

· 7 phút để đọc

1. Tại Sao Cần Quản Lý Trạng Thái?​

Trong Flutter, trạng thái (state) là dữ liệu có thể thay đổi trong quá trình chạy ứng dụng. Ví dụ:

  • Dữ liệu người dùng nhập vào form.
  • Danh sách các mục được hiển thị trên màn hình.
  • Trạng thái đăng nhập của người dùng.

Khi trạng thái thay đổi, giao diện người dùng (UI) cần được cập nhật để phản ánh những thay đổi đó. Quản lý trạng thái giúp bạn kiểm soát cách dữ liệu được truyền và cập nhật trong ứng dụng, đảm bảo UI luôn đồng bộ với trạng thái hiện tại.


2. Các Phương Pháp Quản Lý Trạng Thái Phổ Biến​

2.1. Provider​

Provider là một thư viện quản lý trạng thái đơn giản và mạnh mẽ, được khuyến nghị bởi chính nhóm phát triển Flutter. Nó giúp bạn quản lý trạng thái một cách hiệu quả mà không cần phải viết nhiều code boilerplate.

Cách Sử Dụng Provider​

1. Thêm Provider vào Project: Thêm thư viện provider vào file pubspec.yaml:

dependencies:
flutter:
sdk: flutter
provider: ^6.0.0

2. Tạo một Provider:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class Counter with ChangeNotifier {
int _count = 0;
int get count => _count;

void increment() {
_count++;
notifyListeners(); // Thông báo cho các widget lắng nghe
}
}

3. Sử Dụng Provider trong Widget:

class CounterApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => Counter(),
child: MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Provider Example')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Consumer<Counter>(
builder: (context, counter, child) {
return Text('Count: ${counter.count}');
},
),
ElevatedButton(
onPressed: () {
Provider.of<Counter>(context, listen: false).increment();
},
child: Text('Increment'),
),
],
),
),
),
),
);
}
}

2.2. Riverpod​

Riverpod là một phiên bản cải tiến của Provider, được thiết kế để khắc phục một số hạn chế của Provider. Riverpod cung cấp một cách tiếp cận linh hoạt và an toàn hơn để quản lý trạng thái.

1. Cách Sử Dụng Riverpod Thêm Riverpod vào Project: Thêm thư viện flutter_riverpod vào file pubspec.yaml:

dependencies: flutter: sdk: flutter flutter_riverpod: ^1.0.0

2. Tạo một Provider với Riverpod:

import 'package:flutter_riverpod/flutter_riverpod.dart';

final counterProvider = StateNotifierProvider<Counter, int>((ref) => Counter());

class Counter extends StateNotifier<int> {
Counter() : super(0);

void increment() {
state++;
}
}

3. Sử Dụng Riverpod trong Widget:

class CounterApp extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);

return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Riverpod Example')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Count: $count'),
ElevatedButton(
onPressed: () {
ref.read(counterProvider.notifier).increment();
},
child: Text('Increment'),
),
],
),
),
),
);
}
}

2.3. BLoC (Business Logic Component)​

1. BLoC là một mẫu kiến trúc giúp tách biệt logic nghiệp vụ (business logic) khỏi giao diện người dùng (UI). Nó sử dụng các luồng dữ liệu (streams) để quản lý trạng thái và cập nhật UI.

Cách Sử Dụng BLoC Thêm BLoC vào Project: Thêm thư viện flutter_bloc vào file pubspec.yaml:

dependencies: flutter: sdk: flutter flutter_bloc: ^8.0.0

2. Tạo một BLoC:

import 'package:flutter_bloc/flutter_bloc.dart';

class CounterCubit extends Cubit<int> {
CounterCubit() : super(0);

void increment() => emit(state + 1);
}

3. Sử Dụng BLoC trong Widget:

class CounterApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => CounterCubit(),
child: MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('BLoC Example')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
BlocBuilder<CounterCubit, int>(
builder: (context, count) {
return Text('Count: $count');
},
),
ElevatedButton(
onPressed: () {
context.read<CounterCubit>().increment();
},
child: Text('Increment'),
),
],
),
),
),
),
);
}
}

3. So Sánh Các Phương Pháp Quản Lý Trạng Thái​

Phương PhápƯu ĐiểmNhược Điểm
ProviderĐơn giản, dễ sử dụng, được khuyến nghị bởi Flutter.Có thể trở nên phức tạp khi ứng dụng lớn hơn.
RiverpodLinh hoạt, an toàn hơn Provider, không cần BuildContext.Cần thời gian để làm quen với cú pháp mới.
BLoCTách biệt rõ ràng giữa logic và UI, phù hợp cho ứng dụng phức tạp.Cần viết nhiều code boilerplate, khó tiếp cận cho người mới.

4. Ví dụ code cho từng phương pháp​

Cùng một bài toán — danh sách sản phẩm có tải/lỗi/dữ liệu — viết bằng ba cách.

Cách 1: Provider​

class SanPhamProvider extends ChangeNotifier {
List<SanPham> sanPham = [];
bool dangTai = false;
String? loi;

Future<void> tai() async {
dangTai = true;
loi = null;
notifyListeners();

try {
sanPham = await api.laySanPham();
} catch (e) {
loi = e.toString();
} finally {
dangTai = false;
notifyListeners();
}
}
}

// Dùng trong widget
Consumer<SanPhamProvider>(
builder: (context, provider, _) {
if (provider.dangTai) return const CircularProgressIndicator();
if (provider.loi != null) return Text('Lỗi: ${provider.loi}');
return ListView(children: provider.sanPham.map(...).toList());
},
)

Ưu điểm: dễ hiểu nhất. Nhược điểm: notifyListeners() gọi ba lần → có thể rebuild thừa.

Cách 2: Riverpod​

@riverpod
class DanhSachSanPham extends _$DanhSachSanPham {
@override
Future<List<SanPham>> build() async {
return api.laySanPham();
}

Future<void> lamMoi() async {
state = const AsyncValue.loading();
state = await AsyncValue.guard(() => api.laySanPham());
}
}

// Dùng trong widget
class ManHinhSanPham extends ConsumerWidget {
const ManHinhSanPham({super.key});

@override
Widget build(BuildContext context, WidgetRef ref) {
final async = ref.watch(danhSachSanPhamProvider);

return RefreshIndicator(
onRefresh: () => ref.read(danhSachSanPhamProvider.notifier).lamMoi(),
child: async.when(
loading: () => const Center(child: CircularProgressIndicator()),
error: (e, _) => Center(child: Text('Lỗi: $e')),
data: (ds) => ListView.builder(
itemCount: ds.length,
itemBuilder: (_, i) => ListTile(title: Text(ds[i].ten)),
),
),
);
}
}

Ưu điểm: AsyncValue buộc xử lý cả ba trạng thái; không cần BuildContext; test dễ hơn nhiều.

Cách 3: BLoC​

// Events
abstract class SuKienSanPham {}
class TaiSanPham extends SuKienSanPham {}

// States
abstract class TrangThaiSanPham {}
class DangTai extends TrangThaiSanPham {}
class DaTai extends TrangThaiSanPham {
final List<SanPham> sanPham;
DaTai(this.sanPham);
}
class LoiTai extends TrangThaiSanPham {
final String thongDiep;
LoiTai(this.thongDiep);
}

// Bloc
class SanPhamBloc extends Bloc<SuKienSanPham, TrangThaiSanPham> {
final ApiClient api;
SanPhamBloc(this.api) : super(DangTai()) {
on<TaiSanPham>((event, emit) async {
emit(DangTai());
try {
emit(DaTai(await api.laySanPham()));
} catch (e) {
emit(LoiTai(e.toString()));
}
});
}
}

// Dùng trong widget
BlocBuilder<SanPhamBloc, TrangThaiSanPham>(
builder: (context, state) {
return switch (state) {
DangTai() => const CircularProgressIndicator(),
LoiTai(:final thongDiep) => Text('Lỗi: $thongDiep'),
DaTai(:final sanPham) => ListView.builder(
itemCount: sanPham.length,
itemBuilder: (_, i) => ListTile(title: Text(sanPham[i].ten)),
),
};
},
)

Ưu điểm: luồng dữ liệu một chiều, dễ trace trong app lớn. Nhược điểm: nhiều code cho tính năng đơn giản.

5. Nên chọn cái nào?​

Tình huốngLựa chọn
Học Flutter, app nhỏProvider hoặc setState
App mới, dự án cá nhânRiverpod
Team lớn, app nhiều màn hình phức tạpBLoC
Công ty đã chuẩn hoá BLoCBLoC
Cần test nhiềuRiverpod (dễ test nhất)

💡 Lời khuyên thực tế: đừng chọn state management vì nó "trending". Hãy chọn theo quy mô app và quy ước của team. Chuyển đổi giữa các giải pháp tốn rất nhiều công — hãy quyết định sớm.

⚠️ Điều quan trọng hơn cả việc chọn thư viện: tách logic ra khỏi widget. Dù bạn dùng Provider, Riverpod hay BLoC, code vẫn tệ nếu bạn gọi API và xử lý nghiệp vụ ngay trong build().

RESTful API trong Khóa Học Flutter Level 3

· 10 phút để đọc

RESTful API trong Khóa Học Flutter Level 3​

Trong khóa học Flutter Level 3, việc tích hợp và làm việc với RESTful API là một trong những kỹ năng quan trọng mà học viên sẽ được học và thực hành. RESTful API là một giao thức phổ biến được sử dụng để giao tiếp giữa ứng dụng di động (Flutter) và backend server. Dưới đây là những nội dung chính liên quan đến RESTful API trong khóa học:

1. Tổng quan về RESTful API (Buổi 4)​

  • REST (Representational State Transfer) là một kiến trúc phần mềm được sử dụng để thiết kế các hệ thống mạng, đặc biệt là các ứng dụng web. RESTful API là các API tuân thủ các nguyên tắc của REST.
  • Các phương thức HTTP phổ biến trong RESTful API:
    • GET: Lấy dữ liệu từ server.
    • POST: Gửi dữ liệu mới lên server.
    • PUT: Cập nhật dữ liệu đã có trên server.
    • DELETE: Xóa dữ liệu trên server.
  • Cấu trúc API response: Thông thường, API response sẽ trả về dữ liệu dưới dạng JSON, bao gồm các trường như status, message, và data.

Bài tập: Học viên sẽ thực hành gọi một API công khai (ví dụ: JSONPlaceholder) và hiển thị dữ liệu lên màn hình Flutter.

2. Giao tiếp với RESTful API bằng thư viện http (Buổi 4)​

  • Thư viện http là một thư viện đơn giản và phổ biến trong Flutter để thực hiện các yêu cầu HTTP.
  • Học viên sẽ học cách:
    • Gửi các yêu cầu GET, POST, PUT, DELETE đến server.
    • Xử lý response từ server và chuyển đổi JSON thành các đối tượng Dart.
    • Xử lý lỗi khi gọi API, chẳng hạn như lỗi mạng hoặc lỗi từ server.

Bài tập: Học viên sẽ thực hành gọi API và hiển thị dữ liệu lên màn hình Flutter, đồng thời xử lý các trường hợp lỗi.

3. Sử dụng Dio để gọi API nâng cao (Buổi 5)​

  • Dio là một thư viện mạnh mẽ hơn so với http, hỗ trợ nhiều tính năng nâng cao như interceptors, timeout, retry request, và hỗ trợ upload file.
  • Học viên sẽ học cách:
    • Cấu hình Dio để thêm các header tùy chỉnh (ví dụ: Authorization token).
    • Sử dụng interceptors để tự động thêm token vào mỗi request.
    • Xử lý timeout và tự động retry request khi có lỗi mạng.

Bài tập: Học viên sẽ tích hợp Dio vào ứng dụng Flutter, thêm header cho request và xử lý các tình huống phức tạp hơn.

4. Authentication với API (JWT, OAuth2) (Buổi 6)​

  • JWT (JSON Web Token) là một chuẩn mã hóa để truyền thông tin an toàn giữa client và server. JWT thường được sử dụng để xác thực người dùng.
  • OAuth2 là một giao thức ủy quyền, cho phép ứng dụng truy cập vào tài nguyên của người dùng mà không cần chia sẻ thông tin đăng nhập.
  • Học viên sẽ học cách:
    • Đăng nhập và đăng ký qua API.
    • Lưu trữ token (JWT) trên thiết bị bằng SharedPreferences hoặc Hive.
    • Tự động refresh token khi token hết hạn.

Bài tập: Học viên sẽ xây dựng màn hình đăng nhập và tích hợp API authentication vào ứng dụng Flutter.

5. Pagination và Infinite Scroll với API (Buổi 9)​

  • Pagination là kỹ thuật phân trang dữ liệu, giúp ứng dụng load dữ liệu theo từng phần thay vì load toàn bộ dữ liệu một lúc.
  • Học viên sẽ học cách:
    • Sử dụng các tham số như offset, limit, hoặc cursor-based để phân trang dữ liệu.
    • Triển khai infinite scroll trong Flutter bằng ListView.builder.
    • Sử dụng flutter_bloc để quản lý trạng thái khi load dữ liệu phân trang.

Bài tập: Học viên sẽ thực hiện phân trang danh sách sản phẩm và tích hợp infinite scroll vào ứng dụng.

6. Bảo mật API & Xử lý lỗi nâng cao (Buổi 10)​

  • Bảo mật API là một yếu tố quan trọng khi phát triển ứng dụng di động. Học viên sẽ học các kỹ thuật bảo mật như:
    • Sử dụng HTTPS để mã hóa dữ liệu truyền giữa client và server.
    • SSL Pinning để ngăn chặn các cuộc tấn công Man-in-the-Middle (MITM).
  • Xử lý lỗi nâng cao: Học viên sẽ học cách xử lý các lỗi API phức tạp và hiển thị thông báo lỗi thân thiện với người dùng.

Bài tập: Học viên sẽ cài đặt SSL Pinning trên ứng dụng Flutter và xử lý các lỗi API một cách chuyên nghiệp.

Kết quả​

Qua các buổi học về RESTful API trong khóa học Flutter Level 3, học viên sẽ nắm vững cách tích hợp và làm việc với API trong ứng dụng Flutter. Từ việc gọi API cơ bản đến các kỹ thuật nâng cao như authentication, phân trang, và bảo mật, học viên sẽ có đủ kiến thức để xây dựng các ứng dụng di động tương tác với backend một cách hiệu quả và an toàn.

Kiến thức nền về REST API mà mọi lập trình viên Flutter cần​

Trước khi viết dòng code Flutter nào, bạn cần hiểu rõ giao tiếp giữa app và server.

HTTP method và tính chất​

MethodDùng choIdempotent?Có body?
GETĐọc dữ liệu✅ CóKhông
POSTTạo mới❌ KhôngCó
PUTThay thế toàn bộ✅ CóCó
PATCHCập nhật một phẩn❌ KhôngCó
DELETEXoá✅ CóKhông

Vì sao "idempotent" quan trọng: nếu request bị mất mạng và app gửi lại, GET/PUT/DELETE cho kết quả giống như gửi một lần. POST không — gửi lại có thể tạo hai bản ghi trùng. Đây là lý do thanh toán dùng idempotency key.

Mã trạng thái app phải xử lý​

MãÝ nghĩaApp nên làm gì
200Thành côngHiển thị dữ liệu
201Đã tạoHiển thị bản ghi mới
204Thành công, không có bodyKhông parse JSON
400Dữ liệu gửi lên saiHiển thị lỗi validate
401Chưa đăng nhập / token hết hạnChuyển về màn đăng nhập
403Không có quyềnThông báo không đủ quyền
404Không tìm thấyHiển thị "không có dữ liệu"
429Quá nhiều requestChờ và thử lại (backoff)
5xxLỗi serverThông báo chung, cho phép thử lại

⚠️ Lỗi phổ biến nhất trong code Flutter thực tế: chỉ kiểm tra statusCode == 200. Khi API trả 201 hoặc 204, app báo lỗi dù thực tất thành công. Hãy kiểm tra theo khoảng: statusCode >= 200 && statusCode < 300.

Cấu trúc một lớp API Service đúng chuẩn​

// lib/services/api_client.dart
import 'dart:convert';
import 'package:http/http.dart' as http;

class ApiException implements Exception {
final int? statusCode;
final String message;
ApiException(this.message, {this.statusCode});

@override
String toString() => message;
}

class ApiClient {
final String baseUrl;
final http.Client _client;
String? _token;

ApiClient({required this.baseUrl, http.Client? client})
: _client = client ?? http.Client();

void datToken(String? token) => _token = token;

Map<String, String> get _headers => {
'Content-Type': 'application/json; charset=utf-8',
'Accept': 'application/json',
if (_token != null) 'Authorization': 'Bearer $_token',
};

Future<dynamic> _xuLy(http.Response r) async {
// 204 hoặc body rỗng → không parse JSON
if (r.statusCode == 204 || r.body.isEmpty) return null;

// Decode UTF-8 tường minh — tiếng Việt sẽ lỗi nếu dùng r.body
final duLieu = jsonDecode(utf8.decode(r.bodyBytes));

if (r.statusCode >= 200 && r.statusCode < 300) return duLieu;

if (r.statusCode == 401) {
throw ApiException('Phiên đăng nhập đã hết hạn', statusCode: 401);
}
if (r.statusCode == 429) {
throw ApiException('Bạn thao tác quá nhanh, vui lòng thử lại sau',
statusCode: 429);
}

final thongDiep = duLieu is Map && duLieu['loi'] != null
? duLieu['loi'].toString()
: 'Lỗi không xác định (${r.statusCode})';
throw ApiException(thongDiep, statusCode: r.statusCode);
}

Future<dynamic> get(String path, {Map<String, String>? query}) async {
final uri = Uri.parse('$baseUrl$path').replace(queryParameters: query);
try {
return _xuLy(await _client.get(uri, headers: _headers)
.timeout(const Duration(seconds: 20)));
} on ApiException {
rethrow;
} catch (e) {
throw ApiException('Không kết nối được máy chủ. Kiểm tra mạng của bạn.');
}
}

Future<dynamic> post(String path, Map<String, dynamic> body) async {
try {
return _xuLy(await _client
.post(Uri.parse('$baseUrl$path'),
headers: _headers, body: jsonEncode(body))
.timeout(const Duration(seconds: 20)));
} on ApiException {
rethrow;
} catch (e) {
throw ApiException('Không kết nối được máy chủ. Kiểm tra mạng của bạn.');
}
}
}

Ba chi tiết kỹ thuật quan trọng trong đoạn code trên:

Chi tiếtVì sao
utf8.decode(r.bodyBytes)r.body đoán encoding từ header; nếu server trả sai, tiếng Việt bị lỗi font
.timeout(...)Không có timeout, request có thể treo vô hạn khi mạng yếu
rethrow cho ApiExceptionTránh bọc lỗi thành "không kết nối được" làm mất thông tin gốc

Parse JSON an toàn​

// lib/models/san_pham.dart
class SanPham {
final int id;
final String ten;
final double gia;
final String? hinhAnh;

const SanPham({
required this.id,
required this.ten,
required this.gia,
this.hinhAnh,
});

factory SanPham.fromJson(Map<String, dynamic> json) {
return SanPham(
id: (json['id'] as num).toInt(),
ten: json['ten'] as String? ?? 'Không có tên',
// Server có thể trả String hoặc num — xử lý cả hai
gia: json['gia'] is num
? (json['gia'] as num).toDouble()
: double.tryParse(json['gia'].toString()) ?? 0,
hinhAnh: json['hinh_anh'] as String?,
);
}

Map<String, dynamic> toJson() => {
'id': id,
'ten': ten,
'gia': gia,
'hinh_anh': hinhAnh,
};
}

⚠️ Bẫy as num với toInt(): JSON không phân biệt int và double. Server có thể trả 12 hoặc 12.0 cho cùng một trường. Dùng (json['x'] as num).toInt() an toàn cho cả hai; dùng json['x'] as int sẽ crash khi server trả 12.0.

Bốn lỗi triển khai thường gặp​

LỗiHậu quảCách sửa
Gọi API trong build()Gọi lại mỗi lần UI render → bão requestGọi trong initState hoặc state manager
Không xử lý trạng thái loading/lỗiApp treo hoặc màn hình trắngDùng FutureBuilder hoặc AsyncValue
Hard-code API key trong appBị lộ khi giải nén APKProxy qua backend
Không cancel request khi rời màn hìnhGhi đè state sau khi dispose → crashKiểm tra mounted trước setState

📚 Tài liệu khóa học​

Hẹn gặp các bạn vào ngày khai giảng! 🚀

TÍCH HỢP BACKEND + API + AI

· 7 phút để đọc

🎉 TÍCH HỢP BACKEND + API + AI

🚀 Hôm nay, Hướng Nghiệp Dữ Liệu chính thức khai giảng khóa học Flutter Level 3 với chủ đề cực kỳ hấp dẫn: Tích hợp Backend, API và AI trong lập trình Flutter. Đây là cột mốc quan trọng trong lộ trình trở thành Flutter Developer chuyên nghiệp của các bạn học viên.


🕢 THỜI GIAN KHAI GIẢNG:​

  • ⏰ Thời gian: 07h30, ngày 08/03/2025
  • 📅 Lịch học: Thứ 7 và Chủ nhật hàng tuần

💡 VỚI 27 BUỔI HỌC CHUYÊN SÂU, KHÓA HỌC SẼ GIÚP BẠN:​

  • ✅ Nắm vững cách kết nối Backend (REST API, GraphQL, WebSocket, Firebase).
  • ✅ Thành thạo các kỹ năng triển khai Authentication (JWT, OAuth2).
  • ✅ Lưu trữ dữ liệu với Firestore, SQLite, Hive.
  • ✅ Tích hợp AI (OCR, Face Recognition, Speech to Text, Chatbot AI, Object Detection).

🌐 TÌM HIỂU VỀ REST API:​

REST API (Representational State Transfer) là một phong cách kiến trúc được sử dụng rộng rãi trong việc xây dựng các dịch vụ web. REST API hoạt động dựa trên các phương thức HTTP phổ biến như:

  • GET: Truy xuất dữ liệu từ server.
  • POST: Gửi dữ liệu mới lên server.
  • PUT: Cập nhật dữ liệu hiện có.
  • DELETE: Xóa dữ liệu.

🔑 Đặc điểm của REST API:​

  • Stateless: Không lưu trạng thái giữa các request.
  • Client-Server: Phân chia rõ ràng giữa client và server.
  • Cacheable: Hỗ trợ caching để tối ưu hiệu năng.
  • Layered System: Kiến trúc phân lớp giúp hệ thống linh hoạt và dễ mở rộng.

🛠️ Cách REST API hoạt động với Flutter:​

Trong khóa học này, bạn sẽ học cách:

  • Gọi REST API bằng thư viện http và dio.
  • Xử lý response, parse JSON thành model.
  • Xử lý lỗi và hiển thị thông báo thân thiện.
  • Tích hợp REST API vào các màn hình Flutter với quản lý trạng thái.

🎯 MỤC TIÊU KHÓA HỌC:​

  • 🚀 Xây dựng ứng dụng Flutter kết nối Backend, API mượt mà.
  • 🚀 Tích hợp các tính năng AI tiên tiến vào mobile app.
  • 🚀 Phát triển ứng dụng real-time với Firebase Cloud Firestore & WebSocket.
  • 🚀 Thành thạo quản lý trạng thái với Provider, Riverpod, BLoC.
  • 🚀 Triển khai backend cơ bản với Node.js + Express.
  • 🚀 Tối ưu hiệu năng và đưa ứng dụng lên Google Play & App Store.

👩‍💻👨‍💻 ĐỐI TƯỢNG HỌC VIÊN:​

  • Lập trình viên Flutter muốn nâng cao kỹ năng.
  • Mobile Developer phát triển ứng dụng kết nối API & Backend.
  • Backend Developer học cách tích hợp API vào Flutter.
  • AI Enthusiast khám phá ứng dụng AI trên mobile app.
  • Sinh viên/Freelancer xây dựng ứng dụng thực tế với API & AI.

Tích hợp AI vào ứng dụng Flutter — ba kiến trúc thực tế​

Tích hợp AI không chỉ là "gọi một API". Có ba cách tiếp cận với đánh đổi rất khác nhau.

Kiến trúc 1 — App gọi thẳng API của nhà cung cấp​

Flutter App ──(API key)──> OpenAI / Gemini / Claude
Future<String> hoiAI(String cauHoi) async {
final r = await http.post(
Uri.parse('https://api.openai.com/v1/chat/completions'),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer $apiKey',
},
body: jsonEncode({
'model': 'gpt-4o-mini',
'messages': [
{'role': 'system', 'content': 'Trả lời ngắn gọn bằng tiếng Việt.'},
{'role': 'user', 'content': cauHoi},
],
}),
);
return jsonDecode(r.body)['choices'][0]['message']['content'];
}
Ưu điểmNhược điểm
Nhanh để làm prototype🔴 API key nằm trong app — ai cũng lấy được
Không cần backendKhông kiểm soát được chi phí
Không cache, không rate limit

🔴 Cảnh báo: giải nén file APK và tìm chuỗi sk- mất chưa đến một phút. Nếu bạn làm cách này và đẩy lên store, key sẽ bị lấy và dùng hết quota trong vài ngày. Chỉ dùng cho prototype nội bộ.

Kiến trúc 2 — Qua backend trung gian (khuyến nghị)​

Flutter App ──> Backend của bạn ──> OpenAI / Gemini
│
├── giữ API key
├── cache câu trả lời
├── rate limit theo user
└── log chi phí
// App chỉ gọi backend của bạn — không bao giờ biết API key
Future<String> hoiAI(String cauHoi) async {
final json = await api.post('/ai/hoi', {'cau_hoi': cauHoi});
return json['tra_loi'] as String;
}

Backend (FastAPI) giữ key và kiểm soát chi phí:

from fastapi import FastAPI, Depends, HTTPException
from pydantic import BaseModel, Field
from openai import OpenAI
import redis

app = FastAPI()
client = OpenAI()
r = redis.from_url("redis://localhost")

QUOTA_MOI_NGAY = 50


class CauHoi(BaseModel):
cau_hoi: str = Field(min_length=1, max_length=2000)


async def kiem_tra_quota(user_id: int):
khoa = f"quota:ai:{user_id}"
so_lan = r.incr(khoa)
if so_lan == 1:
r.expire(khoa, 86400)
if so_lan > QUOTA_MOI_NGAY:
raise HTTPException(429, f"Đã dùng hết {QUOTA_MOI_NGAY} lượt hôm nay")


@app.post("/ai/hoi")
async def hoi(du_lieu: CauHoi, _=Depends(kiem_tra_quota)):
# Cache theo nội dung — cùng câu hỏi không tốn tiền hai lần
khoa_cache = f"ai:cache:{hash(du_lieu.cau_hoi)}"
if (da_cache := r.get(khoa_cache)):
return {"tra_loi": da_cache.decode(), "tu_cache": True}

phan_hoi = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "Trả lời ngắn gọn bằng tiếng Việt."},
{"role": "user", "content": du_lieu.cau_hoi},
],
)
tra_loi = phan_hoi.choices[0].message.content
r.setex(khoa_cache, 3600, tra_loi)
return {"tra_loi": tra_loi, "tu_cache": False}

Ba lợi ích cụ thể: key được bảo vệ; cache giảm chi phí đáng kể (câu hỏi lặp lại rất phổ biến trong app thật); rate limit theo user ngăn một người dùng phá hỏng quota của tất cả.

Kiến trúc 3 — Model chạy trên thiết bị​

import 'package:tflite_flutter/tflite_flutter.dart';

class PhanLoaiAnh {
Interpreter? _interpreter;

Future<void> tai() async {
_interpreter = await Interpreter.fromAsset('assets/model.tflite');
}

/// Phân loại ảnh — chạy hoàn toàn offline, không gửi dữ liệu lên mạng
List<double> duDoan(List<double> dauVao) {
final ketQua = List.filled(10, 0.0).reshape([1, 10]);
_interpreter!.run(dauVao.reshape([1, 224, 224, 3]), ketQua);
return List<double>.from(ketQua[0]);
}
}
Tiêu chíTrên thiết bịQua API
Chi phí mỗi lần gọi0Theo token
Cần mạng❌ Không✅ Có
Riêng tư dữ liệu✅ Rất tốt⚠️ Dữ liệu lên server
Kích thước appTăng vài MB — vài trăm MBKhông đổi
Chất lượng modelGiới hạnTút nhất
Tốc độTuỳ thiết bịỔn định

Khi nào chọn trên thiết bị: dữ liệu nhạy cảm (y tế, tài chính cá nhân), cần hoạt động offline, hoặc model nhỏ đã đủ tốt (OCR, phát hiện khuôn mặt, phân loại đơn giản).

Streaming — trải nghiệm người dùng tốt hơn nhiều​

Thay vì chờ 5 giây rồi hiện cả đoạn văn, hãy hiện dần từng từ.

Stream<String> hoiAIStream(String cauHoi) async* {
final request = http.Request('POST', Uri.parse('$baseUrl/ai/hoi-stream'))
..headers['Content-Type'] = 'application/json'
..body = jsonEncode({'cau_hoi': cauHoi});

final phanHoi = await request.send();

await for (final dong in phanHoi.stream
.transform(utf8.decoder)
.transform(const LineSplitter())) {
if (dong.startsWith('data: ')) {
final duLieu = dong.substring(6).trim();
if (duLieu == '[DONE]') break;
try {
yield jsonDecode(duLieu)['delta'] as String? ?? '';
} catch (_) {
continue; // bỏ qua chunk bị cắt giữa
}
}
}
}

Dùng với StreamBuilder, chữ sẽ xuất hiện dần — trải nghiệm cảm giác nhanh hơn nhiều dù tổng thời gian bằng nhau.

Bốn sai lầm khi tích hợp AI​

  1. Để API key trong app. Key sẽ bị lấy. Luôn qua backend.
  2. Không giới hạn số lần gọi. Một user có thể đốt hết ngân sách của bạn trong một ngày.
  3. Không cache. Câu hỏi "app này là gì?" được hỏi hàng nghìn lần — mỗi lần tốn tiền.
  4. Không xử lý khi AI trả lời sai. Luôn có fallback và cho người dùng báo cáo câu trả lời kém.

📚 Tài liệu khóa học​

Hẹn gặp các bạn vào ngày khai giảng! 🚀