REST API trong lập trình Flutter – Hướng dẫn chi tiết
Trong bài viết này, Báo Flutter sẽ hướng dẫn các bạn chi tiết về các kỹ thuật làm việc với REST API trong lập trình Flutter.
REST API là gì
API (Application Programming Interface) : Hiểu đơn giản là một cách thức để một ứng dụng hay một thành phần sẽ tương tác với một ứng dụng hay thành phần khác …Kiểu dữ liệu mà API trả về có thể là file JSON hoặc XML.
REST (REpresentational State Transfer) là một dạng chuyển đổi cấu trúc dữ liệu, một kiểu kiến trúc để viết API. REST gửi một yêu cầu HTTP như GET, POST, DELETE, vv đến một URL để xử lý dữ liệu.
RESTful API là một tiêu chuẩn dùng trong việc thiết kế các API cho các ứng dụng web để quản lý các resource. RESTful là một trong những kiểu thiết kế API được sử dụng phổ biến ngày nay để cho các ứng dụng (web, mobile…) khác nhau giao tiếp với nhau.
Để demo cho bài viết này, Báo Flutter sẽ dùng một link REST API bên dưới:
1 |
https://api.randomuser.me/?results=10 |
Nội dung trả về từ link API này là một list thông tin các user.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
{ "results": [ { "gender": "female", "name": { "title": "Ms", "first": "Allison", "last": "Flores" }, "location": { "street": { "number": 6925, "name": "W Dallas St" }, "city": "Sydney", "state": "Queensland", "country": "Australia", "postcode": 1011, "coordinates": { "latitude": "-81.1480", "longitude": "52.1020" }, "timezone": { "offset": "+4:00", "description": "Abu Dhabi, Muscat, Baku, Tbilisi" } }, "email": "allison.flores@example.com", "login": { "uuid": "04cabec6-e225-4c3a-8262-7ae23fa85996", "username": "blackkoala693", "password": "goldberg", "salt": "iiwYmQck", "md5": "19612ec8a2ed9898f7fbae63af3e9b97", "sha1": "20ed05395f018a856c6b92cba408be4c92b521bd", "sha256": "21d5ae02846c4d576062eb0c6f56323623d70bf484a4581cb1937072ec9b3dd7" }, "dob": { "date": "1992-03-10T19:34:04.764Z", "age": 29 }, "registered": { "date": "2005-02-22T04:58:37.501Z", "age": 16 }, "phone": "09-6536-5490", "cell": "0406-332-577", "id": { "name": "TFN", "value": "564674545" }, "picture": { "large": "https://randomuser.me/api/portraits/women/20.jpg", "medium": "https://randomuser.me/api/portraits/med/women/20.jpg", "thumbnail": "https://randomuser.me/api/portraits/thumb/women/20.jpg" }, "nat": "AU" }, { "gender": "male", "name": { "title": "Mr", "first": "Jeremy", "last": "Fortin" }, "location": { "street": { "number": 7, "name": "Coastal Highway" }, "city": "Fountainbleu", "state": "British Columbia", "country": "Canada", "postcode": "Q2Q 8G7", "coordinates": { "latitude": "77.2966", "longitude": "-149.2488" }, "timezone": { "offset": "-11:00", "description": "Midway Island, Samoa" } }, "email": "jeremy.fortin@example.com", "login": { "uuid": "0a5648cc-9b69-4c78-b812-67410451fde2", "username": "beautifulelephant234", "password": "1a2b3c", "salt": "Y4kWqJeO", "md5": "d0834072b0675419fadf8ad2dc118ea1", "sha1": "a05dd8c054ca7483dec01aa9ab49c27e40252736", "sha256": "868946332d3d51bacd172e4dc9020c62562d6874bd8489a2e91d90835d1d4a69" }, "dob": { "date": "1988-07-09T18:46:37.147Z", "age": 33 }, "registered": { "date": "2019-02-16T10:44:22.343Z", "age": 2 }, "phone": "288-104-0811", "cell": "859-895-3782", "id": { "name": "", "value": null }, "picture": { "large": "https://randomuser.me/api/portraits/men/98.jpg", "medium": "https://randomuser.me/api/portraits/med/men/98.jpg", "thumbnail": "https://randomuser.me/api/portraits/thumb/men/98.jpg" }, "nat": "CA" }, |
LẬP TRÌNH FLUTTER VỚI REST API
Đầu tiên, bạn cần tạo ra một Flutter project mới.
Để làm việc với REST API, bạn cần một thư viện để làm việc với các đường link API. Có 2 thư viện chính mà bạn có thể sử dụng đó là :
- http
- dio
Dio thì có nhiều tính năng hơn http, tuy nhiên về hiệu năng khi làm việc với API thì 2 thư viện này như nhau. Do đó chúng ta sẽ sử dụng http cho dễ sử dụng.
Trong file pubspec.yaml, khai báo thư viện http mới nhất:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
dependencies: flutter: sdk: flutter # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 http: ^0.13.3 dev_dependencies: flutter_test: sdk: flutter |
TẠO MODEL
Trong file lib của project, tạo một folder : models .
Việc tạo một model, phụ thuộc vào dữ liệu trả về, như trên đường link API :
1 |
https://api.randomuser.me/?results=10 |
Dữ liệu trả về là một file Json, trả về một list các User, được mô tả dưới dạng file json:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
"gender": "female", "name": { "title": "Ms", "first": "Allison", "last": "Flores" }, "location": { "street": { "number": 6925, "name": "W Dallas St" }, "city": "Sydney", "state": "Queensland", "country": "Australia", "postcode": 1011, "coordinates": { "latitude": "-81.1480", "longitude": "52.1020" }, "timezone": { "offset": "+4:00", "description": "Abu Dhabi, Muscat, Baku, Tbilisi" } }, "email": "allison.flores@example.com", "login": { "uuid": "04cabec6-e225-4c3a-8262-7ae23fa85996", "username": "blackkoala693", "password": "goldberg", "salt": "iiwYmQck", "md5": "19612ec8a2ed9898f7fbae63af3e9b97", "sha1": "20ed05395f018a856c6b92cba408be4c92b521bd", "sha256": "21d5ae02846c4d576062eb0c6f56323623d70bf484a4581cb1937072ec9b3dd7" }, "dob": { "date": "1992-03-10T19:34:04.764Z", "age": 29 }, "registered": { "date": "2005-02-22T04:58:37.501Z", "age": 16 }, "phone": "09-6536-5490", "cell": "0406-332-577", "id": { "name": "TFN", "value": "564674545" }, "picture": { "large": "https://randomuser.me/api/portraits/women/20.jpg", "medium": "https://randomuser.me/api/portraits/med/women/20.jpg", "thumbnail": "https://randomuser.me/api/portraits/thumb/women/20.jpg" }, "nat": "AU" .................... |
Từ đoạn json này, chúng ta sẽ tạo ra một model class để mô tả cho một đối tượng từ file json trước.
Chúng ta có thể chọn các thông tin cần lấy để thêm vào model class ví dụ :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
class User { Name? name; Picture? picture; String? email; String? phone; User({this.name, this.picture, this.email, this.phone}); User.fromJson(Map<String, dynamic> json) : this.name = new Name.fromJson(json['name']), this.picture = new Picture.fromJson(json['picture']), this.email = json['email'], this.phone = json['phone']; } class Name { String? last; String? first; Name({this.last, this.first}); Name.fromJson(Map<String, dynamic> json) : this.last = json['last'], this.first = json['first']; } class Picture { String? medium; Picture({this.medium}); Picture.fromJson(Map<String, dynamic> json) : medium = json['large']; } |
Nếu bạn phải xử lý một model từ link API quá dài và lằng ngoằng, bạn có thể dùng tool.
Ví dụ : để tạo ra một model để biểu diễn hết thông tin từ đoạn json kia, chúng ta có thể dùng tool hoặc bạn có thể tự tạo tool để tạo Model tự động bằng việc copy toàn bộ đoạn json kia vào trong {…}và đặt vào link web : https://jsontodart.zariman.dev/ , nhập tên class và chọn Null Safety, Type Checking.
Sau đó nhấn: Generate và kết quả là một model class hiển thị ngay bên cạnh:
Tạo hàm xử lý API
Tạo folder: data_sources, trong đó tạo 2 file : api_urls.dart, api_services.dart
api_urls.dart
1 2 3 4 |
class ApiUrls{ final Uri API_USER_LIST = Uri.parse('https://api.randomuser.me/?results=10'); } |
api_services.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
import 'dart:convert'; import 'package:api_base/models/user.dart'; import 'package:http/http.dart' as http; import 'dart:convert' as json; import 'api_urls.dart'; class ApiServices{ Future<List<User>> fetchUser() { return http .get(ApiUrls().API_USER_LIST) .then((http.Response response) { final String jsonBody = response.body; final int statusCode = response.statusCode; if(statusCode != 200 || jsonBody == null){ print(response.reasonPhrase); throw new Exception("Lỗi load api"); } final JsonDecoder _decoder = new JsonDecoder(); final useListContainer = _decoder.convert(jsonBody); final List userList = useListContainer['results']; return userList.map((contactRaw) => new User.fromJson(contactRaw)).toList(); }); } } |
Tạo views
Tạo file : user_list_screen.dart.
Trong phần này tôi sẽ dùng FutureBuilder để hiển thị lên UI.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
import 'package:api_base/data_sources/api_services.dart'; import 'package:api_base/models/user.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; class UserListScreen extends StatefulWidget { @override _UserListScreenState createState() => _UserListScreenState(); } class _UserListScreenState extends State<UserListScreen> { @override void initState() { // TODO: implement initState super.initState(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Danh sách"), centerTitle: true, ), body: Container( padding: EdgeInsets.only(left: 10), height: MediaQuery.of(context).size.height, width: MediaQuery.of(context).size.height, child: FutureBuilder<List<User>>( future: ApiServices().fetchUser(), builder: (context, snapshot) { if((snapshot.hasError)||(!snapshot.hasData)) return Container( child: Center( child: CircularProgressIndicator(), ), ); List<User>? userList = snapshot.data; return ListView.builder( itemCount:userList!.length, itemBuilder: (BuildContext context, int index) { return UserItem(user: userList[index],); } ); }, ), ), ); } } class UserItem extends StatelessWidget { User? user; UserItem({this.user}); @override Widget build(BuildContext context) { return new Container( margin: new EdgeInsets.only(top: 20.0), child: new Row( children: <Widget>[ new Container( height: 80.0, width: 80.0, margin: new EdgeInsets.only(right: 20.0), child: new CircleAvatar( backgroundImage: new NetworkImage(user!.picture!.medium!), ), ), new Column( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ new Text( user!.name!.first! + " " + user!.name!.last!, style: new TextStyle( fontSize: 20.0, color: Colors.black, ), ), new Container( margin: new EdgeInsets.only(top: 10.0), child: new Text( user!.email!, style: new TextStyle( fontSize: 15.0, color: Colors.grey, ), ), ), ], ) ], ), ); } } |
Cài đặt permission cho android
Sửa file AndroidManifest.xml, trong đường dẫn : android > app > src > main .
1 2 3 4 5 6 7 8 9 |
<manifest ...> <uses-permission android:name="android.permission.INTERNET" /> <application ... android:usesCleartextTraffic="true" ...> ... </application> </manifest> |
SOURCE CODE : Link Github
Kết quả cuối cùng
Vừa rồi, Báo Flutter đã hướng dẫn các bạn về fetch data từ đường link API.
Còn thực tế, khi làm việc với API không chỉ có fetch API mà còn dùng các lệnh như: http.post , ví dụ :
1 2 3 4 5 6 7 8 |
/// For Login postData(data) async { var fullUrl = Uri.parse('https://api.randomuser.me/?results=10'); return await http.post( fullUrl, body: data, ); } |
Lập trình Flutter với REST API là một phần rất quan trọng trong quá trình lập trình ứng dụng Flutter. Hy vọng từ bài viết này bạn có được một kiến thức nhất định, từ đó có thể tư duy để làm việc với hầu hết các vấn đề liên quan đến API.
anh cho em hỏi nếu dùng lib dio thì rest api hoạt động như thế nào , tốt hơn so với http không ạ ? em muốn học cách dùng dio với rest api thì nên dùng nguồn nào phù hợp với người mới không ạ? mong anh tư vấn ạ , em đang bối rối vấn đề này.