Kali ini saya akan belajar Flutter MVC dengan API, oh iya sebelumnya pada pybspec.yaml pastikan anda sudah menambahkan library : "http : ^1.2.1" untuk lengkapnya seperti berikut :
Untuk API Server akan kita bahas di lain kesempatan ya, kali ini kita akan berfokus pada code di Flutternya saja. oh iya, kali ini kita masih menggunakan Arsitektur MVC ya
1. Part 1 (Method GET)
Untuk tahap 1 ini, kita akan menggunakan method GET, yakni hanya mengambil data saja
Dan untuk hasilnya adalah seperti berikut :
2. Part 2 (Method POST)
Untuk Part 2 ini kita akan menggunakan method POST, untuk file kita masih menggunakan file-file yang sama dengan Part 1 ya
main.dart :
import 'package:flutter/material.dart';
import 'View/ItemView.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => ItemView(),
},
);
}
}
ItemView.dart (View) :
import 'package:flutter/material.dart';
import '/Controller/ItemController.dart';
import '/Model/Item.dart';
class ItemView extends StatefulWidget {
@override
_ItemViewState createState() => _ItemViewState();
}
class _ItemViewState extends State<ItemView> {
final ItemController _controller = ItemController();
late Future<List<Item>> _futureItems;
@override
void initState() {
super.initState();
_futureItems = _controller.fetchItems();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Item List'),
),
body: FutureBuilder<List<Item>>(
future: _futureItems,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
} else if (snapshot.hasError) {
return Center(
child: Text('Error: ${snapshot.error}'),
);
} else {
return Column(
children: [
Text('baris1'),
Text('baris2'),
Text('baris3'),
//ElevatedButton(onPressed: _addItem, child: Text('Tambah data'))
ElevatedButton(
onPressed: () {
// Tindakan yang diambil ketika tombol ditekan
_addItem("nama5","desc5");
},
child: Text('Tekan Saya'),
),
],
);
/*
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
Item item = snapshot.data![index];
return ListTile(
title: Text(item.name),
subtitle: Text(item.description),
);
},
);
*/
}
},
),
);
}
void _addItem(nama5, desc5) async {
try {
// Melakukan proses tambah item dan mendapatkan hasil
//Item newItem = await _controller.createItem("nama4", "desc4");
Item newItem = await _controller.createItem(nama5, desc5);
// Jika berhasil, refresh halaman untuk menampilkan item baru
setState(() {
_futureItems = _controller.fetchItems();
});
} catch (e) {
// Menangani jika terjadi error
print('Failed to add item: $e');
// Tampilkan pesan error kepada pengguna jika diperlukan
}
}
}
ItemController.dart (Controller) :
import '/Model/Item.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
class ItemController {
Future<List<Item>> fetchItems() async {
final response = await http.get(Uri.parse('http://192.168.43.227/PHP-REST-API/getdata2.php'));
if (response.statusCode == 200) {
List<dynamic> jsonData = json.decode(response.body);
List<Item> items = jsonData.map((json) => Item.fromJson(json)).toList();
return items;
} else {
throw Exception('Failed to load items');
}
}
Future<Item> createItem(String name, String description) async {
var uri = Uri.parse("http://192.168.43.227/PHP-REST-API/post2.php");
var request = http.MultipartRequest('POST', uri);
request.fields['name'] = name;
request.fields['description'] = description;
var streamedResponse = await request.send();
var response = await http.Response.fromStream(streamedResponse);
if (response.statusCode == 200) {
return Item.fromJson(json.decode(response.body));
} else {
throw Exception('Failed to create item');
}
}
}
Item.dart (Model) :
class Item {
final String name;
final String description;
Item({required this.name, required this.description});
factory Item.fromJson(Map<String, dynamic> json) {
return Item(
name: json['name'],
description: json['description'],
);
}
Map<String, dynamic> toJson() {
return {
'name': name,
'description': description,
};
}
}
Untuk hasilnya adalah seperti berikut, ketika kita menekan tombol "Tekan Saaya" maka data akan masuk ke dalam database
Oke, data sudah berhasil terupload di database. Sebenarnya Method POST ini masih bisa kita kembangkan lagi, misal inputannya berupa TextEdit.
Kita perlu sedikit merubah itemView.dart (View) :
import 'package:flutter/material.dart';
import '/Controller/ItemController.dart';
import '/Model/Item.dart';
class ItemView extends StatefulWidget {
@override
_ItemViewState createState() => _ItemViewState();
}
class _ItemViewState extends State<ItemView> {
final ItemController _controller = ItemController();
late Future<List<Item>> _futureItems;
TextEditingController controllerTextEdit_name = TextEditingController(text: 'kosong');
TextEditingController controllerTextEdit_description = TextEditingController(text: '');
@override
void initState() {
super.initState();
_futureItems = _controller.fetchItems();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Item List'),
),
body: FutureBuilder<List<Item>>(
future: _futureItems,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
} else if (snapshot.hasError) {
return Center(
child: Text('Error: ${snapshot.error}'),
);
} else {
return Column(
children: [
Text('baris1'),
Text('baris2'),
Text(controllerTextEdit_name.text),
TextField(
controller: controllerTextEdit_name,
decoration: InputDecoration(
labelText: 'Masukkan Name', // Label input teks
),
),
TextField(
controller: controllerTextEdit_description,
decoration: InputDecoration(
labelText: 'Masukkan Description', // Label input teks
),
),
//ElevatedButton(onPressed: _addItem, child: Text('Tambah data'))
ElevatedButton(
onPressed: () {
// Tindakan yang diambil ketika tombol ditekan
_addItem(controllerTextEdit_name.text,controllerTextEdit_description.text);
},
child: Text('Tekan Saya'),
),
],
);
/*
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
Item item = snapshot.data![index];
return ListTile(
title: Text(item.name),
subtitle: Text(item.description),
);
},
);
*/
}
},
),
);
}
void _addItem(nama5, desc5) async {
try {
// Melakukan proses tambah item dan mendapatkan hasil
//Item newItem = await _controller.createItem("nama4", "desc4");
Item newItem = await _controller.createItem(nama5, desc5);
// Jika berhasil, refresh halaman untuk menampilkan item baru
setState(() {
_futureItems = _controller.fetchItems();
});
} catch (e) {
// Menangani jika terjadi error
print('Failed to add item: $e');
// Tampilkan pesan error kepada pengguna jika diperlukan
}
}
}
Untuk hasilnya kita bisa input "name" dan "deskription" di textFiled menjadi seperti berikut :
3. Part 3 (Pengantar edit) :
Sebelum kita melakukan edit di sisi server (mengupload data via API), kita akan melakukan kegiatan sederhana terlebih dahulu,
yaitu ketika kita melakukan klik bada salah satu listview, maka nilai dari baris yang kita klik akan dimunculkan pada TextField, saya kira ini perlu karena pasti akan diperlukan untuk operasi edit sebelum diupload ke server.
3.A (Get 1 data dari lokal)
Maksudnya adalah kita mendapatkan 1 data tanpa API
ItemView.dart (View) :
import 'package:flutter/material.dart';
import '/Controller/ItemController.dart';
import '/Model/Item.dart';
class ItemView extends StatefulWidget {
@override
_ItemViewState createState() => _ItemViewState();
}
class _ItemViewState extends State<ItemView> {
final ItemController _controller = ItemController();
late Future<List<Item>> _futureItems;
TextEditingController controllerTextEdit_name = TextEditingController(text: 'kosong');
TextEditingController controllerTextEdit_description = TextEditingController(text: '');
@override
void initState() {
super.initState();
_futureItems = _controller.fetchItems();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Item List'),
),
body: FutureBuilder<List<Item>>(
future: _futureItems,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
} else if (snapshot.hasError) {
return Center(
child: Text('Error: ${snapshot.error}'),
);
} else {
return Column(
children: [
Text('baris1'),
Text('baris2'),
Text(controllerTextEdit_name.text),
TextField(
controller: controllerTextEdit_name,
decoration: InputDecoration(
labelText: 'Masukkan Name', // Label input teks
),
),
TextField(
controller: controllerTextEdit_description,
decoration: InputDecoration(
labelText: 'Masukkan Description', // Label input teks
),
),
//ElevatedButton(onPressed: _addItem, child: Text('Tambah data'))
ElevatedButton(
onPressed: () {
// Tindakan yang diambil ketika tombol ditekan
_addItem(controllerTextEdit_name.text,controllerTextEdit_description.text);
},
child: Text('Tekan Saya'),
),
Expanded(
// Tambahkan Expanded untuk memberikan ruang tambahan ke ListView.builder
child: ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
Item item = snapshot.data![index];
return ListTile(
title: Text(item.name),
subtitle: Text(item.description),
onTap: () {
// Saat ListTile diklik, Anda akan mendapatkan print(item.name)
print(item.name);
controllerTextEdit_name.text = item.name;
controllerTextEdit_description.text = item.description;
},
);
},
),
),
],
);
/*
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
Item item = snapshot.data![index];
return ListTile(
title: Text(item.name),
subtitle: Text(item.description),
);
},
);
*/
}
},
),
);
}
void _addItem(nama5, desc5) async {
try {
// Melakukan proses tambah item dan mendapatkan hasil
//Item newItem = await _controller.createItem("nama4", "desc4");
Item newItem = await _controller.createItem(nama5, desc5);
// Jika berhasil, refresh halaman untuk menampilkan item baru
setState(() {
_futureItems = _controller.fetchItems();
});
} catch (e) {
// Menangani jika terjadi error
print('Failed to add item: $e');
// Tampilkan pesan error kepada pengguna jika diperlukan
}
}
}
Untuk hasilnya adalah seperti berikut :
Ketika kita melakukan klik pada kotak merah di bawah, maka nilainya akan dimunculkan di textField (Kotak merah yang atas)
3.B Get1Data dengan API
pada file ItemView.dart (View) tambahkan 2 blok berikut
- Tombol "get 1 data from API"
ElevatedButton(
onPressed: () {
_get1ItemAPI(controllerTextEdit_name.text,controllerTextEdit_description.text);
},
child: Text('Get 1 Data from API'),
),
lalu pada bagian bawah :
void _get1ItemAPI(nama5, desc5) async {
try {
// Melakukan proses tambah item dan mendapatkan hasil
//Item newItem = await _controller.createItem("nama4", "desc4");
Item newItem = await _controller.get1ItemAPI(nama5, desc5);
// Jika berhasil, refresh halaman untuk menampilkan item baru
setState(() {
_futureItems = _controller.fetchItems();
controllerTextEdit_description.text = newItem.description;
print(newItem.name);
print(newItem.description);
});
} catch (e) {
// Menangani jika terjadi error
print('Failed to add item: $e');
// Tampilkan pesan error kepada pengguna jika diperlukan
}
}
ItemController.dart (Contorller) :
Tambahkan blok berikut :
Future<Item> get1ItemAPI(String name, String description) async {
var uri = Uri.parse("http://192.168.43.227/contohAPI/get1data.php?name=$name");
print(uri);
try {
var response = await http.get(uri);
if (response.statusCode == 200) {
return Item.fromJson(json.decode(response.body));
} else {
throw Exception('Failed to fetch item');
}
} catch (e) {
throw Exception('Failed to fetch item: $e');
}
}
Untuk hasilnya adalah seperti berikut :
4. Edit Dan Delete
Setelah kita melakukan tahap 3,maka kita sudah siap melakukan pada tahap yakni Edidt Data dan Delete Data
ItemView.dart (View):
import 'package:flutter/material.dart';
import '/Controller/ItemController.dart';
import '/Model/Item.dart';
class ItemView extends StatefulWidget {
@override
_ItemViewState createState() => _ItemViewState();
}
class _ItemViewState extends State<ItemView> {
final ItemController _controller = ItemController();
late Future<List<Item>> _futureItems;
TextEditingController controllerTextEdit_name = TextEditingController(text: 'kosong');
TextEditingController controllerTextEdit_description = TextEditingController(text: '');
@override
void initState() {
super.initState();
_futureItems = _controller.fetchItems();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Item List'),
),
body: FutureBuilder<List<Item>>(
future: _futureItems,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
} else if (snapshot.hasError) {
return Center(
child: Text('Error: ${snapshot.error}'),
);
} else {
return Column(
children: [
Text('baris1'),
Text('baris2'),
Text(controllerTextEdit_name.text),
TextField(
controller: controllerTextEdit_name,
decoration: InputDecoration(
labelText: 'Masukkan Name', // Label input teks
),
),
TextField(
controller: controllerTextEdit_description,
decoration: InputDecoration(
labelText: 'Masukkan Description', // Label input teks
),
),
ElevatedButton(
onPressed: () {
_addItem(controllerTextEdit_name.text,controllerTextEdit_description.text);
},
child: Text('Input Data'),
),
ElevatedButton(
onPressed: () {
_editItem(controllerTextEdit_name.text,controllerTextEdit_description.text);
},
child: Text('Edit Data'),
),
ElevatedButton(
onPressed: () {
_deleteItem(controllerTextEdit_name.text,controllerTextEdit_description.text);
},
child: Text('Delete Data'),
),
Expanded(
// Tambahkan Expanded untuk memberikan ruang tambahan ke ListView.builder
child: ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
Item item = snapshot.data![index];
return ListTile(
title: Text(item.name),
subtitle: Text(item.description),
onTap: () {
// Saat ListTile diklik, Anda akan mendapatkan print(item.name)
print(item.name);
controllerTextEdit_name.text = item.name;
controllerTextEdit_description.text = item.description;
},
);
},
),
),
],
);
/*
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
Item item = snapshot.data![index];
return ListTile(
title: Text(item.name),
subtitle: Text(item.description),
);
},
);
*/
}
},
),
);
}
void _addItem(nama5, desc5) async {
try {
// Melakukan proses tambah item dan mendapatkan hasil
//Item newItem = await _controller.createItem("nama4", "desc4");
Item newItem = await _controller.createItem(nama5, desc5);
// Jika berhasil, refresh halaman untuk menampilkan item baru
setState(() {
_futureItems = _controller.fetchItems();
});
} catch (e) {
// Menangani jika terjadi error
print('Failed to add item: $e');
// Tampilkan pesan error kepada pengguna jika diperlukan
}
}
void _editItem(nama5, desc5) async {
try {
// Melakukan proses tambah item dan mendapatkan hasil
//Item newItem = await _controller.createItem("nama4", "desc4");
Item newItem = await _controller.editItem(nama5, desc5);
// Jika berhasil, refresh halaman untuk menampilkan item baru
setState(() {
_futureItems = _controller.fetchItems();
});
} catch (e) {
// Menangani jika terjadi error
print('Failed to add item: $e');
// Tampilkan pesan error kepada pengguna jika diperlukan
}
}
void _deleteItem(nama5, desc5) async {
try {
// Melakukan proses tambah item dan mendapatkan hasil
//Item newItem = await _controller.createItem("nama4", "desc4");
Item newItem = await _controller.deleteItem(nama5, desc5);
// Jika berhasil, refresh halaman untuk menampilkan item baru
setState(() {
_futureItems = _controller.fetchItems();
});
} catch (e) {
// Menangani jika terjadi error
print('Failed to add item: $e');
// Tampilkan pesan error kepada pengguna jika diperlukan
}
}
}
ItemController.dart (Controller) :
import '/Model/Item.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
class ItemController {
Future<List<Item>> fetchItems() async {
final response = await http.get(Uri.parse('http://192.168.43.227/PHP-REST-API/getdata2.php'));
if (response.statusCode == 200) {
List<dynamic> jsonData = json.decode(response.body);
List<Item> items = jsonData.map((json) => Item.fromJson(json)).toList();
return items;
} else {
throw Exception('Failed to load items');
}
}
Future<Item> createItem(String name, String description) async {
var uri = Uri.parse("http://192.168.43.227/PHP-REST-API/post2.php");
var request = http.MultipartRequest('POST', uri);
request.fields['name'] = name;
request.fields['description'] = description;
var streamedResponse = await request.send();
var response = await http.Response.fromStream(streamedResponse);
if (response.statusCode == 200) {
return Item.fromJson(json.decode(response.body));
} else {
throw Exception('Failed to create item');
}
}
Future<Item> editItem(String name, String description) async {
var uri = Uri.parse("http://192.168.43.227/PHP-REST-API/editdata2.php");
var request = http.MultipartRequest('POST', uri);
request.fields['name'] = name;
request.fields['description'] = description;
var streamedResponse = await request.send();
var response = await http.Response.fromStream(streamedResponse);
if (response.statusCode == 200) {
return Item.fromJson(json.decode(response.body));
} else {
throw Exception('Failed to create item');
}
}
Future<Item> deleteItem(String name, String description) async {
var uri = Uri.parse("http://192.168.43.227/PHP-REST-API/deletedata2.php");
var request = http.MultipartRequest('POST', uri);
request.fields['name'] = name;
request.fields['description'] = description;
var streamedResponse = await request.send();
var response = await http.Response.fromStream(streamedResponse);
if (response.statusCode == 200) {
return Item.fromJson(json.decode(response.body));
} else {
throw Exception('Failed to create item');
}
}
}
Untuk hasilnya adalah, tombol Edit dan Delete sudah siap digunakan
Pada contoh di atas terdapat kekurangan, harusnya saya menggunakan "id" karena berupa primary Key