Hướng dẫn căn bản: Cách tích hợp REST API trong Flutter
Trong quá trình học và phát triển ứng dụng di động với Flutter, bạn sẽ nhận ra rằng một ứng dụng không thể chỉ hoạt động với dữ liệu tĩnh trên máy. Để ứng dụng "sống" và liên tục cập nhật thông tin (như đọc báo, xem danh sách sản phẩm, hiển thị thời tiết), bạn cần phải kết nối nó với máy chủ (server). Và cầu nối phổ biến nhất để làm việc này chính là REST API.
Bài viết này sẽ giải thích cho bạn REST API là gì theo ngôn ngữ đơn giản nhất, cũng như hướng dẫn chi tiết từng bước cách gọi một API trong Flutter để hiển thị dữ liệu lên màn hình. Hãy cùng bắt đầu nhé!
Mục Lục Bài Viết
1. REST API là gì? Hiểu sao cho đơn giản?
Hãy tưởng tượng bạn đang vào một nhà hàng. Bạn (Ứng dụng Flutter - Client) muốn gọi món ăn. Nhà bếp (Máy chủ - Server) là nơi có nguyên liệu và chế biến món ăn. Tuy nhiên, bạn không thể tự chạy vào bếp lấy đồ ăn được. Bạn cần một người Bồi bàn (API) để ghi lại yêu cầu của bạn, mang vào bếp, và sau đó bưng món ăn từ bếp ra bàn cho bạn.
REST API (Representational State Transfer Application Programming Interface) chính là "người bồi bàn" đó trên môi trường internet. Nó tuân theo một bộ quy tắc nhất định để Client và Server có thể nói chuyện được với nhau.
Trong REST API, chúng ta có 4 hành động (phương thức) cơ bản tương ứng với chuẩn CRUD (Create, Read, Update, Delete):
- GET (Read): Lấy dữ liệu từ server về (Ví dụ: Lấy danh sách sản phẩm).
- POST (Create): Gửi dữ liệu mới lên server (Ví dụ: Tạo tài khoản mới, Gửi bình luận).
- PUT / PATCH (Update): Cập nhật dữ liệu đã có trên server.
- DELETE (Delete): Xóa dữ liệu trên server.
Dữ liệu trao đổi qua lại thường được định dạng dưới dạng JSON (JavaScript Object Notation) - một định dạng văn bản rất dễ đọc cho cả người và máy.
2. Tại sao ứng dụng Flutter của bạn lại cần API?
Sử dụng API mang lại những lợi ích khổng lồ cho ứng dụng di động:
- Dữ liệu luôn mới: Bạn không cần bắt người dùng cập nhật ứng dụng trên App Store chỉ để đổi một dòng chữ hay thêm một sản phẩm. Mọi thứ được tải từ server xuống theo thời gian thực.
- Tiết kiệm dung lượng: Ứng dụng sẽ rất nhẹ vì dữ liệu (hình ảnh, văn bản, video) nằm ở server, ứng dụng chỉ tải về những thứ cần thiết khi người dùng lướt tới.
- Đồng bộ đa nền tảng: Bạn có thể dùng chung một REST API cho cả ứng dụng Flutter (iOS/Android), ứng dụng Web và ứng dụng Desktop. Đảm bảo mọi nền tảng đều thấy cùng một dữ liệu.
3. Ví dụ minh họa: Gọi API hiển thị danh sách bài viết
Trong ví dụ này, chúng ta sẽ sử dụng một Fake API miễn phí dành cho lập trình viên là JSONPlaceholder để lấy về một danh sách các bài viết (Posts) bằng phương thức GET.
Đường dẫn API: https://jsonplaceholder.typicode.com/posts
Bước 1: Cài đặt gói thư viện HTTP
Flutter mặc định không có sẵn công cụ tối ưu để gọi API, nên chúng ta sẽ sử dụng một thư viện (package) rất phổ biến của Google là http.
Mở file pubspec.yaml trong project của bạn và thêm dòng sau vào dưới mục dependencies:
dependencies:
flutter:
sdk: flutter
http: ^1.1.0 # Thêm thư viện http tại đây
Sau đó chạy lệnh flutter pub get trong terminal để tải thư viện về.
Bước 2: Tạo Lớp Dữ Liệu (Model)
Khi gọi API, dữ liệu trả về là một chuỗi JSON (text). Để Flutter có thể hiểu và dễ dàng lấy ra các thông tin (như tiêu đề, nội dung), chúng ta cần chuyển đổi chuỗi JSON đó thành một đối tượng Dart. Ở đây ta tạo một class Post.
class Post {
final int id;
final String title;
final String body;
Post({required this.id, required this.title, required this.body});
// Hàm chuyển đổi từ JSON (Map) sang Object Dart
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
id: json['id'],
title: json['title'],
body: json['body'],
);
}
}
Bước 3: Viết hàm bất đồng bộ để gọi API (Fetch Data)
Việc kết nối mạng sẽ tốn thời gian (có thể nhanh hoặc chậm tùy mạng). Do đó, chúng ta phải sử dụng hàm bất đồng bộ (Future, async, await) để ứng dụng không bị đơ trong lúc chờ đợi server phản hồi.
import 'dart:convert';
import 'package:http/http.dart' as http;
Future<List<Post>> fetchPosts() async {
// Gửi request GET tới server
final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts'));
// Kiểm tra mã trạng thái (StatusCode 200 nghĩa là thành công)
if (response.statusCode == 200) {
// Ép kiểu dữ liệu chuỗi trả về sang dạng List
List jsonResponse = jsonDecode(response.body);
// Chuyển đổi List JSON sang List<Post>
return jsonResponse.map((data) => Post.fromJson(data)).toList();
} else {
// Nếu lỗi, ném ra một ngoại lệ
throw Exception('Không thể tải danh sách bài viết');
}
}
Bước 4: Hiển thị dữ liệu lên giao diện với FutureBuilder
Để vẽ giao diện dựa trên dữ liệu đang tải từ mạng, Flutter cung cấp một Widget tuyệt vời có tên là FutureBuilder. Nó sẽ tự động cập nhật UI tùy theo trạng thái: Đang tải, Tải lỗi, hoặc Tải thành công.
import 'package:flutter/material.dart';
class PostListScreen extends StatefulWidget {
@override
_PostListScreenState createState() => _PostListScreenState();
}
class _PostListScreenState extends State<PostListScreen> {
late Future<List<Post>> futurePosts;
@override
void initState() {
super.initState();
// Gọi hàm fetch data ngay khi màn hình khởi tạo
futurePosts = fetchPosts();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Danh Sách Bài Viết API')),
body: FutureBuilder<List<Post>>(
future: futurePosts,
builder: (context, snapshot) {
// Trạng thái 1: Đang tải dữ liệu
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
// Trạng thái 2: Có lỗi xảy ra
else if (snapshot.hasError) {
return Center(child: Text('Lỗi: ${snapshot.error}'));
}
// Trạng thái 3: Tải thành công và có dữ liệu
else if (snapshot.hasData) {
List<Post> posts = snapshot.data!;
return ListView.builder(
itemCount: posts.length,
itemBuilder: (context, index) {
return Card(
margin: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
child: ListTile(
leading: CircleAvatar(child: Text(posts[index].id.toString())),
title: Text(posts[index].title, style: TextStyle(fontWeight: FontWeight.bold)),
subtitle: Text(posts[index].body, maxLines: 2, overflow: TextOverflow.ellipsis),
),
);
},
);
}
// Trạng thái mặc định
return Center(child: Text('Không có dữ liệu'));
},
),
);
}
}
4. Những lưu ý quan trọng khi làm việc với API
Để ứng dụng của bạn trở nên chuyên nghiệp và ít gặp lỗi (bug) khi người dùng sử dụng, bạn cần chú ý những điểm sau khi làm việc với REST API:
- Xử lý lỗi (Error Handling): Không phải lúc nào server cũng trả về kết quả thành công (Mã 200). Đôi khi mất mạng, đôi khi link API bị sai (Mã 404), hoặc lỗi từ server (Mã 500). Bạn phải luôn có khối lệnh
try-catchhoặc xử lýstatusCodecẩn thận để ứng dụng không bị văng (crash) bất ngờ. - Parsing JSON cẩn thận: Kiểu dữ liệu từ API trả về có thể thay đổi (ví dụ API hứa trả về một chuỗi String nhưng lại trả về null). Bạn nên kiểm tra null an toàn (Null Safety) khi chuyển đổi JSON. Các lập trình viên thường dùng thư viện như
json_serializablehoặcfreezedđể tự động hóa và làm an toàn quá trình này thay vì tự viết tay. - Quản lý trạng thái (State Management): Trong các ứng dụng thực tế lớn, thay vì dùng
setStatehayFutureBuilderở mọi nơi, người ta sẽ kết hợp gọi API với các công cụ quản lý trạng thái như Provider, Riverpod, BLoC hoặc GetX để cấu trúc code gọn gàng, tách biệt phần Giao diện (UI) và phần Xử lý dữ liệu (Logic) ra khỏi nhau.
5. Lời kết
Kết nối REST API trong Flutter ban đầu có thể trông hơi phức tạp vì có liên quan đến các khái niệm bất đồng bộ (async/await) và chuyển đổi JSON. Tuy nhiên, khi bạn đã làm quen với mô hình cơ bản (Tạo Model -> Viết hàm gọi HTTP -> Dùng FutureBuilder hiển thị), bạn sẽ thấy nó cực kỳ logic và mạnh mẽ.
Hi vọng bài viết này đã giúp bạn hiểu rõ bản chất của API và tự tin triển khai thành công tính năng này vào ứng dụng Flutter của mình. Hãy thử thay đổi đường dẫn API trong ví dụ trên bằng một API thời tiết hay giá coin để thấy sự thú vị nhé. Chúc các bạn code vui vẻ!