Flutter Akses Data di Internet
Pemrograman Mobile 2
Muhamad Saad Nurul Ishlah, M.Comp.
Dept. Sistem Informasi & Dept. Ilmu Komputer, Universitas Pakuan
Agenda Kuliah
- Mengambil Data dari Internet
- Parsing JSON di belakang layar
- Menyimpan Data ke Internet
Mengambil Data dari Internet
Mengambil Data dari Internet
- Pengambilan data dari internet merupaka sesuatu yang perlu untuk sebagian besar aplikasi
- Dart dan Flutter menyediakan tools
- package http
- Beberapa protokol REST yang disupport http:
- GET: mengambil data
- POST: menyimpan data baru
- PUT: memperbaharui data
- DELETE: menghapus data
Catatan untuk Android
- Harus melakukan deklarasi untuk penggunaan internet pada berkas Android manifest (AndroidManifest.xml)
<manifest xmlns:android...>
...
<uses-permission android:name="android.permission.INTERNET" />
<application ...
</manifest>
Catatan untuk macOS
- Harus mengizinkan akses jaringan dalam berkas
.entitlements
<key>com.apple.security.network.client</key>
<true/>
Langkah Penggunaan http
- Tambahkan dependency package http di pubspec.yaml
- Lakukan permintaan jaringan menggunakan http di file dart
- Ubah respon menjadi objek Dart
- Ambil dan tampilkan data
1. Tambahkan package http
# pada file pubspec.yaml tambahkan dependency dari http
dependencies:
http: <latest_version>
- Tambahkan dependency package http versi terakhir
- Simpan untuk mengunduh package (Visual Code Studio)
-
$ flutter pub get
1. Tambahkan package http
// import package pada file dart
import 'package:http/http.dart' as http;
- Import package pada file Dart
- Biasanya pada file yang khusus untuk melakukan pemanggilan jaringan
1. Tambahkan package http
<!-- Diperlukan untuk mengakses data dari internet -->
<uses-permission android:name="android.permission.INTERNET" />
- Jika menggunakan Android, tambahkan izin untuk mengakses internet pada berkas AndroidManifest.xml
2. Lakukan Permintaan Jaringan
Future<http.Response> fetchAlbum() {
return http.get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'));
}
- metode http.get() akan mengembalikan objek Future yang berisi Response
- Future merupakan kelas inti dari Dart yang digunakan untuk operasi yang asinkronus
- http.Response berisi data yang diterima ketika pemanggilan http sukses dilakukan
3. Ubah Respon
// Kelas Model sebagai skema objek data dart
class Album {
final int userId;
final int id;
final String title;
Album({
required this.userId,
required this.id,
required this.title,
});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
userId: json['userId'],
id: json['id'],
title: json['title'],
);
}
}
- Lakukan pengubahan http.Response menjadi objek Dart
- JSON and serialization
- Buat class yang mewakili model data, contoh Album
- Perhatikan struktur JSON yang diakses
3. Ubah Respon
// Kelas Model sebagai skema objek data dart
class Album {
final int userId;
final int id;
final String title;
Album({
required this.userId,
required this.id,
required this.title,
});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
userId: json['userId'],
id: json['id'],
title: json['title'],
);
}
}
// Ubah objek http.Response menjadi objek Album
Future<Album> fetchAlbum() async {
final response = await http
.get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'));
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return Album.fromJson(jsonDecode(response.body));
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load album');
}
}
- Ubah respon menjadi JSON Map dengan dart:convert
- Jika server mengembalikan respon OK (200), ubah JSON menjadi Album menggunakan metode fromJson()
- JIka tidak, lempar exception
4. Panggil fungsi pengambilan Data
class _MyAppState extends State<MyApp> {
late Future<Album> futureAlbum;
@override
void initState() {
super.initState();
futureAlbum = fetchAlbum();
}
// ···
}
- Panggil fungsi pemanggilan data pada metode initState() atau pada didChangeDependencies(). Contoh fetchAlbum()
- initState dipanggil sekali
- gunakan pada didChangeDependencies jika ingin melakukan pemanggilan API setiap kali ada perubahan pada InheritedWidget
- Tidak direkomendasikan dipanggil pada build()
5. Tampilkan Data pada UI
FutureBuilder<Album>(
future: futureAlbum,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data!.title);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
// By default, show a loading spinner.
return const CircularProgressIndicator();
},
)
- Gunakan widget FutureBuilder untuk menampilkan data pada UI – proses asinkronus
- properti future – memanggil fungsi pengambilan data
- properti builder – render UI. bisa menyesuaikan dengan status pemanggilan data (snapshot)
Contoh Lengkap
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Future<Album> fetchAlbum() async {
final response = await http
.get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'));
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return Album.fromJson(jsonDecode(response.body));
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load album');
}
}
class Album {
final int userId;
final int id;
final String title;
Album({
required this.userId,
required this.id,
required this.title,
});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
userId: json['userId'],
id: json['id'],
title: json['title'],
);
}
}
void main() => runApp(const MyApp());
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late Future<Album> futureAlbum;
@override
void initState() {
super.initState();
futureAlbum = fetchAlbum();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fetch Data Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: const Text('Fetch Data Example'),
),
body: Center(
child: FutureBuilder<Album>(
future: futureAlbum,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data!.title);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
// By default, show a loading spinner.
return const CircularProgressIndicator();
},
),
),
),
);
}
}
Melakukan Otentikasi
final response = await http.get(
Uri.parse('https://jsonplaceholder.typicode.com/albums/1'),
// Send authorization headers to the backend.
headers: {
HttpHeaders.authorizationHeader: 'Basic your_api_token_here',
},
);
Cukup menambahkan properti headers pada request dengan isi kelas HttpHeaders
Parsing JSON di belakang layar
Hindari "Jank"
- Default – Dart melakukan proses dalam 1 thread (utas)
- Namun kadang butuh komputasi yang lebih berat, misal
- memparsing JSON yang besar
- proses yang membutuhkan waktu > 16 milidetik
- Menyebabkan "jank" - "halt"
- kinerja aplikasi yang buruk
- animasi tersendat/freeze
- Lakukan proses komputasi yang berat di belakang layar
- Proses sama dengan pengambilan data, tapi isolasi dengan metode compute()
Isolasi Proses Berat dg compute()
// A function that converts a response body into a List<Photo>.
List<Photo> parsePhotos(String responseBody) {
final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Photo>((json) => Photo.fromJson(json)).toList();
}
Future<List<Photo>> fetchPhotos(http.Client client) async {
final response = await client
.get(Uri.parse('https://jsonplaceholder.typicode.com/photos'));
// Use the compute function to run parsePhotos in a separate isolate.
return parsePhotos(response.body);
}
Isolasi Proses Berat dg compute()
// A function that converts a response body into a List<Photo>.
List<Photo> parsePhotos(String responseBody) {
final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Photo>((json) => Photo.fromJson(json)).toList();
}
Future<List<Photo>> fetchPhotos(http.Client client) async {
final response = await client
.get(Uri.parse('https://jsonplaceholder.typicode.com/photos'));
// Use the compute function to run parsePhotos in a separate isolate.
return compute(parsePhotos, response.body);
}
Catatan Penggunaan compute
- compute melakukan komunikasi dengan cara melempar pesan secara bolak-balik
- pesan dapat berupa nilai primitif
- null, num, bool, double, String
- objek sederhana – List<Album>
- akan mengalami error jika pesan berupa objek kompleks
- Future
- http.Response
Mengirim Data ke Internet
Menyimpan data ke Internet
Future<Album> createAlbum(String title) async {
final response = await http.post(
Uri.parse('https://jsonplaceholder.typicode.com/albums'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, String>{
'title': title,
}),
);
if (response.statusCode == 201) {
// If the server did return a 201 CREATED response,
// then parse the JSON.
return Album.fromJson(jsonDecode(response.body));
} else {
// If the server did not return a 201 CREATED response,
// then throw an exception.
throw Exception('Failed to create album.');
}
}
Memperbaharui data di Internet
Future<Album> updateAlbum(String title) async {
final response = await http.put(
Uri.parse('https://jsonplaceholder.typicode.com/albums/1'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, String>{
'title': title,
}),
);
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return Album.fromJson(jsonDecode(response.body));
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to update album.');
}
}
Menghapus data di Internet
Future<Album> deleteAlbum(String id) async {
final http.Response response = await http.delete(
Uri.parse('https://jsonplaceholder.typicode.com/albums/$id'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
);
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON. After deleting,
// you'll get an empty JSON `{}` response.
// Don't return `null`, otherwise `snapshot.hasData`
// will always return false on `FutureBuilder`.
return Album.fromJson(jsonDecode(response.body));
} else {
// If the server did not return a "200 OK response",
// then throw an exception.
throw Exception('Failed to delete album.');
}
}
Diskusi
Ada pertanyaan?
Bacaan Tambahan
- Serialization with JSON: https://docs.flutter.dev/data-and-backend/json
- Bekerja dengan WebSockets: https://docs.flutter.dev/cookbook/networking/web-sockets
Referensi
- Kevin D Moore. Networking in Flutter. https://www.raywenderlich.com/books/flutter-apprentice/v2.0/chapters/11-networking-in-flutter, diakses 10 Desember 2021
- Fetch data from the internet https://docs.flutter.dev/cookbook/networking/fetch-data, diakses 10 Desember 2021
Terima Kasih
Flutter Akses Data di Internet
By Muhamad Ishlah
Flutter Akses Data di Internet
- 1,274