Anda di halaman 1dari 51

1.

Kontrak Perkuliahan
2. Pengenalan HTTP & API
3. Tampil Data API Firebase
3.1 Menampilkan data API
Syarat : sudah terinstall flutter dan get_cli

Buat Projek baru dengan nama flutter_application_3_tampil lalu jalankan perintah get init untuk
menggunakan get pattern di flutter.

Jalankan projeknya menggunakan chrome web

Pada latihan ini, kita akan mengambil data dari api dengan url berikut:

https://materi-flutter-default-rtdb.asia-southeast1.firebasedatabase.app/students.json

Untuk mengambil data dari API, kita perlu membuat file provider, file ini bertugas untuk melakukan
request ke url.

Untuk membuat provider, buka terminal lalu ketik perintah berikut:


 get create provider:Student

Setelah menjalankan perintah diatas, get_cli akan membuatkan kita file dengan nama student_provider
di \lib\app\data\student_provider.dart

Get_cli merupakan packages bagian dari getx, dengan get_cli kita dimudahkan untuk membuat file
dengan perintah-perintah di terminal.

Buka file student_provider.dart lalu kita buat kode untuk melakukan request seperti berikut:
import 'package:get/get.dart';

class StudentProvider extends GetConnect {


  Future<Response<dynamic>> getAll() async {
    return await get(
        'https://materi-flutter-default-rtdb.asia-southeast1.firebasedatabase.app/
students.json');
  }
}

Perintah diatas, kita membuat fungsi/method dengan nama getAll, method getAll merupakan method
dengan tipe async, method ini mengembalikan future dynamic
Synchronous merupakan eksekusi yang dilaksanakan secara berurutan, jika terdapat ada 2 perintah,
perintah satu membutuhkan waktu 2 menit maka perintah kedua tidak akan di eksekusi sebelum
perintah satu selesai di eksekusi.

Kebalikan dari Synchronous, Asynchronous memungkinkan ekseksui perintah kedua tanpa harus
menunggu perintah pertama selesai.

Flutter menggunakan bahasa pemrograman dart, pada bahasa dart, untuk menjalankan asynchronous
pada sebuat method kita bisa menggunakan keyword async pada body method dan keyword
await pada perintah yang ingin di asynchronous lalu tipe kembalian method tersebut adalah
Future<tipe-data> seperti contoh berikut:
Future<Response<dynamic>> getAll() async {
    return await get(
        'https://materi-flutter-default-rtdb.asia-southeast1.firebasedatabase.app/students.json');
  }
Buka file home_controller.dart lalu buat kode seperti berikut:
import 'package:flutter_application_3_tampil/app/data/student_provider.dart';
import 'package:get/get.dart';

class HomeController extends GetxController {


  List listData = [].obs;

  @override
  void onInit() {
    StudentProvider().getAll().then((Response response) {
      if (response.statusCode == 200) {
        final Map<String, dynamic> jsonResponse = response.body;
        jsonResponse.forEach((key, value) {
          Map student = {
            "nama": value['nama'],
            "kelas": value['kelas'],
          };
          listData.add(student);
        });
      } else {
        Get.snackbar('Error', response.statusText!);
      }
    });
    super.onInit();
  }
}

Di home_controller.dart, kita menggunakan method onInit, semua kode di method ini akan dieksekusi
pada saat controller dibuat di memory (sesaat sebelum view ditampilkan).

Untuk mendapatkan respon dari method asynchronous kita bisa menggunakan then seperti kode
diatas. Jadi pada saat kita memanggil method asynchronous, kita menambahkan method then dengan
menambahkan parameter tipe data dari respon method async.
StudentProvider().getAll().then((Response response) {
      print(response);
    });

Setelah mendapatkan respon dari http request get di student_provider.dart, berikutnya kita cek apakah
respon yang dilakukan berhasil melalui statusCode seperti berikut:
if (respon.statusCode == 200) {
        final Map<String, dynamic> jsonResponse = response.body;
        jsonResponse.forEach((key, value) {
          Map student = {
            "nama": value['nama'],
            "kelas": value['kelas'],
          };
          listData.add(student);
        });
      } else {
        Get.snackbar('Error', response.statusText!);
      }

Jika statusCode tidak sama dengan 200, maka sistem akan menampilkan error di snackbar, jika berhasil
maka kita akan mengambil data dari http response body kemudian langsung mengubah datanya menjadi
tipe data Map seperti berikut:
final Map<String, dynamic> mapAllData = response.body;
Data yang di konvert dari respon.body ke map akan menjadi seperti array, maka kita bisa menggunakan
perulangan forEach untuk mengambil data satu persatu dan mengolah data tersebut seperti berikut:
final Map<String, dynamic> jsonResponse = response.body;
jsonResponse.forEach((key, value) {
  Map student = {
    "nama": value['nama'],
    "kelas": value['kelas'],
  };
  listData.add(student);
});

Pada kode diatas, kita melakukan perulangan, lalu di setiap perulangan kita membuat objek map
dengan nama student, lalu objek tersebut kita tambahkan ke variable listData menggunakan
method add.

Berikutnya kita akan memodifikasi home_view.dart menjadi seperti berikut:


import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/home_controller.dart';

class HomeView extends GetView<HomeController> {


  const HomeView({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('HomeView'),
        centerTitle: true,
      ),
      backgroundColor: Colors.white70,
      body: Obx(
        () => ListView.builder(
          itemCount: controller.listData.length,
          itemBuilder: (BuildContext context, int index) {
            var list = controller.listData[index];
            return Container(
              color: Colors.white,
              margin: EdgeInsets.only(bottom: 1),
              child: ListTile(
                title: Text(list['nama']),
                subtitle: Text(list['kelas']),
              ),
            );
          },
        ),
      ),
    );
  }
}

Pada kode diatas, kita mengatur background dari scaffold dengan argument
backgroundColor: Colors.white70,

Lalu kita menghitung jumlah data yang ada di listData dengan perintah:
itemCount: controller.listData.length,

Pada bagian perulangan itemBuilder kita membuat variabel baru dengan nama list, variabel ini akan
menampung setiap item dari listData sesuai index, misalnya di perulangan pertama, variabel list akan
berisi data objek {nama: budi, kelas: 01PS5} sesuai dengan data dari API lalu data yang ada di
variabel list ini ditampilkan di widget ListTile sesuai dengan key nya

3.2 Menambahkan CircularProgressIndicator


Proses mengambil data terkadang membutuhkan waktu, hal tergantung banyak faktor,misalnya
bandwidth, resource server dan lain-lain. Programmer biasanya akan menampilkan animasi loading agar
user mengetahui bahwa data sedang dalam proses pengambilan.

Flutter menyediakan widget animasi loading yang bisa kita gunakan yaitu:
CircularProgressIndicator()

Buka file home_controller, kita akan menambahkan satu variabel boolean untuk menyimpan perubahan
status request seperti berikut:
import 'package:flutter_application_3_tampil/app/data/student_provider.dart';
import 'package:get/get.dart';

class HomeController extends GetxController {


  List listData = [].obs;
  var isLoading = false.obs;

  @override
  void onInit() {
    isLoading.value = true;
    StudentProvider().getAll().then((Response response) {
      if (response.statusCode == 200) {
        final Map<String, dynamic> jsonResponse = response.body;
        jsonResponse.forEach((key, value) {
          Map student = {
            "nama": value['nama'],
            "kelas": value['kelas'],
          };
          listData.add(student);
        });
      } else {
        Get.snackbar('Error', response.statusText!);
      }
      isLoading.value = false;
    });
    super.onInit();
  }
}

Pada kode diatas, kita mengatur nilai isLoading menjadi true sebelum data student diambil,lalu
setelah perulangan forEach selesai, kita mengatur nilai isLoading menjadi false
Klik kanan pada ListView.builder lalu refactor dengan memilih wrap with widget modifikasi widget
menjadi obx agar semua widget di body dibungkus dengan Obx seperti berikut:
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/home_controller.dart';

class HomeView extends GetView<HomeController> {


  const HomeView({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text('HomeView'),
          centerTitle: true,
        ),
        backgroundColor: Colors.white70,
        body: Obx(
          () => ListView.builder(
            itemCount: controller.listData.length,
            itemBuilder: (BuildContext context, int index) {
              var list = controller.listData[index];

              return Container(
                color: Colors.white,
                margin: EdgeInsets.all(5),
                child: ListTile(
                  title: Text(list['nama']),
                  subtitle: Text(list['kelas']),
                ),
              );
            },
          ),
        ));
  }
}

Setelah melakukan wrap widget, berikutnya kita akan menampilkan circular progress indicator jika
isLoading bernilai true menggunakan tenary operator seperti berikut:
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/home_controller.dart';

class HomeView extends GetView<HomeController> {


  const HomeView({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('HomeView'),
        centerTitle: true,
      ),
      backgroundColor: Colors.white70,
      body: Obx(
        () => controller.isLoading.value
            ? Center(child: CircularProgressIndicator())
            : ListView.builder(
                itemCount: controller.listData.length,
                itemBuilder: (BuildContext context, int index) {
                  var list = controller.listData[index];

                  return Container(
                    color: Colors.white,
                    margin: EdgeInsets.all(5),
                    child: ListTile(
                      title: Text(list['nama']),
                      subtitle: Text(list['kelas']),
                    ),
                  );
                },
              ),
      ),
    );
  }
}
Jika variabel isLoading bernilai true maka widget yang ditampilkan adalah widget Center yang memiliki
child CircularProgressIndicator, jika isLoading bernilai false maka menampilkan ListView.builder
3.3 Menggunakan Model Data
Untuk menghandle data API (URL), programmer biasanya membuat sebuah class model untuk
memudahkan dalam mengakses data tersebut, sebagai contoh, kita akan membuat class dengan nama
StudentModel untuk membuat class, klik kanan di folder lib\app\data lalu pilih new file seperti
gambar berikut:

Data[‘nama’]

Data.attribute

Buat file dengan nama student_model.dart

Kita bisa mengetik kode model secara manual atau menggunakan situs-situs untuk melakukan generate
model seperti https://app.quicktype.io/?l=dart . Dengan menggunakan situs ini, kita cukup memasukkan
respon json lalu model data akan di generate sendiri seperti berikut:
Klik make all properties required lalu copy kode diatas dan pastekan di bagian bawah file
home_controller.dart seperti berikut:
StudentModel studentModelFromJson(String str) =>
    StudentModel.fromJson(json.decode(str));

String studentModelToJson(StudentModel data) => json.encode(data.toJson());

class StudentModel {
  StudentModel({
    required this.createdAt,
    required this.kelas,
    required this.nama,
    required this.nim,
  });

  String createdAt;
  String kelas;
  String nama;
  String nim;

  factory StudentModel.fromJson(Map<String, dynamic> json) => StudentModel(


        createdAt: json["created_at"],
        kelas: json["kelas"],
        nama: json["nama"],
        nim: json["nim"].toString(),
      );

  Map<String, dynamic> toJson() => {


        "created_at": createdAt,
        "kelas": kelas,
        "nama": nama,
        "nim": nim,
      };
}

Pada class ini kita membuat beberapa properti dengan tipe data String. Tanda tanya ? pada string
mengindikasikan bahwa variabel ini bisa berisi string atau null.

Kita juga membuat Method dengan tipe factory untuk mengubah format data dari json menjadi model.
Jika data dari API ada yang bernilai null maka kita ubah menjad kosong, kita juga mengubah tipe data
nim menjadi string.
Selanjutnya kita akan mengubah kode di home_controller.dart menjadi seperti berikut :
import 'package:flutter_application_3_tampil/app/data/student_provider.dart';
import 'package:get/get.dart';

class HomeController extends GetxController {


  var listData = List<StudentModel>.empty().obs;
  var isLoading = false.obs;

  @override
  void onInit() {
    isLoading.value = true;

    StudentProvider().getAll().then((Response response) {
      if (respon.statusCode == 200) {
        final Map<String, dynamic> jsonResponse = response.body;
        jsonResponse.forEach((key, value) {
          StudentModel studentModel = StudentModel.fromJson(value);
          studentModel.key = key;
          listData.add(studentModel);
        });
      } else {
        Get.snackbar('Error', response.statusText!);
      }
      isLoading.value = false;
    });
    super.onInit();
  }
}
Perubahan pertama dilakukan pada bagian definisi variabel listData, tipe data List diganti menjadi var,
variabel ini disiapkan untuk menyimpan data List dengan tipe StudentModel.

Variable listData ini juga di observer agar perubahan nilai di variabel ini bisa selalu diikuti oleh screen.
var listData = List<StudentModel>.empty().obs;

Perubahan selanjutnya pada perulangan forEach menjadi seperti berikut:


jsonResponse.forEach((key, value) {
  StudentModel studentModel = StudentModel.fromJson(value);
  studentModel.key = key;
  listData.add(studentModel);
});

Pada perubahan kedua, kita membuat objek studentModel dari class StudentModel yang
menjalankan method fromJson(val). Method fromJson ini akan mengubah tipe data json yaitu val
menjadi objek StudentModel sehingga proses perubahan data tidak lagi di lakukan di class
HomeController tetapi dilakukan di class StudentModel
Buka home_view.dart, lalu modifikasi kode pada bagian berikut:
body: Obx(
  () => controller.isLoading.value
      ? Center(child: CircularProgressIndicator())
      : ListView.builder(
          itemCount: controller.listData.length,
          itemBuilder: (BuildContext context, int index) {
            var list = controller.listData[index];

            return Container(
              color: Colors.white,
              margin: EdgeInsets.all(5),
              child: ListTile(
                title: Text(list.nama),
                subtitle: Text(list.kelas),
              ),
            );
          },
        ),
)

Pada file home_view, kita mengubah cara mengakses data list yang tadinya menggunakan array
Text(list['nama'])

Menjadi objek seperti berikut


Text(list.nama!)

Jika terdapat Tanda ! menyatakan bahwa kita memberitahu kepada flutter bhwa variabel ini tidak
akan null.
3.4 Membuat Method getStudents
Agar proses mengambil data dari API dapat dilakukan dengan mudah, kita bisa memindahkan kode di
onInit ke fungsi/method khusus sehingga dapat digunakan berkali-kali sesuai kebutuhan.

Buat method dengan nama getStudents() lalu pindahkan kode dari onInit sehingga kode di
home_controller menjadi seperti berikut:
import 'package:flutter_application_3_tampil/app/data/student_provider.dart';
import 'package:get/get.dart';

class HomeController extends GetxController {


  var listData = List<StudentModel>.empty().obs;
  var isLoading = false.obs;

  @override
  void onInit() {
    getStudents ();
    super.onInit();
  }

  void getStudents() {
    isLoading.value = true;
    StudentProvider().getAll().then((Response respon) {
      if (respon.statusCode == 200) {
        final Map<String, dynamic> jsonResponse = respon.body;
        jsonResponse.forEach((key, val) {
          StudentModel studentModel = StudentModel.fromJson(val);
          studentModel.key = key;
          listData.add(studentModel);
        });
      } else {
        Get.snackbar('Error', respon.statusText!);
      }
      isLoading.value = false;
    });
  }
}

Pada kode diatas, kita memindahkan semua kode untuk mengolah data dari provider dari onInit
ke method getStudents, lalu di method onInit kita memanggil method getData. Dengan cara seperti ini,
kita bisa memanggil method getStudents dari kode mana saja sesuai dengan kebutuhan.
4. Tampil Data reqres.in
Selain API firebase, kita juga bisa menggunakan publik API lainnya seperti yang ada di situs reqres.in
berikut URL:

https://reqres.in/api/users?page=2

Format json di situs ini adalah sebagai berikut:


{
    "page": 2,
    "per_page": 6,
    "total": 12,
    "total_pages": 2,
    "data": [
{
            "id": 7,
            "email": "michael.lawson@reqres.in",
            "first_name": "Michael",
            "last_name": "Lawson",
            "avatar": "https://reqres.in/img/faces/7-image.jpg"
        },
        {
            "id": 8,
            "email": "lindsay.ferguson@reqres.in",
            "first_name": "Lindsay",
            "last_name": "Ferguson",
            "avatar": "https://reqres.in/img/faces/8-image.jpg"
        }
    ],
    "support": {
        "url": "https://reqres.in/#support-heading",
        "text": "To keep ReqRes free, contributions towards server costs are appreciated!"
    }
}
Perbedaan API di situs reqres dengan API firebase adalah pada struktur json nya. Data API di situs reqres
terdiri dari beberapa level, level pertama terdapat key:

1. page
2. per_page
3. total
4. total_pages
5. data
6. support

Pada key data terdapat data user dalam bentuk json array.

Kita akan melakukan request di API reqres dengan membuat file provider baru dengan nama
user_provider

Buat file provider dengan nama User:


> get create provider:User
Buka file user_provider.dart lalu tambahkan kode berikut:
import 'package:get/get.dart';

class UserProvider extends GetConnect {


  Future<Response<dynamic>> getAll() async {
    return await get('https://reqres.in/api/users');
  }
}

Buka file home_controller.dart, lalu buat method dengan nama getUsers:


void getUsers() {
  isLoading.value = true;
  UserProvider().getAll().then((Response response) {
    final Map jsonResponse = response.body;
    var data = jsonResponse['data'];
    var page = jsonResponse['page'];
    var perPage = jsonResponse['per_page'];
    var total = jsonResponse['total'];
    var totalPage = jsonResponse['total_page'];
    for (var element in data) {
      Map studentModel = {
        'nama': element['email'],
        'kelas': element['first_name'],
      };
      listData.add(studentModel);
    }
    isLoading.value = false;
  });
}

Pada kode diatas, kita mengambil data sesuai dengan key datanya, misalnya key data yang berisi array
data user. Lalu ada key page, per_page dan data lainnya yang sesuai dengan kebutuhan kita.

Kita juga memodifikasi studentModel dengan key yang berbeda agar tidak perlu mengubah kode di
home_view, karena key yang ada di firebase dan di reqres berbeda seperti berikut:
      Map studentModel = {
        'nama': element['email'],
        'kelas': element['first_name'],
      };

Method ini lalu kita panggil di onInit :


@override
void onInit() {
  getStudents();
  getUsers();
  super.onInit();
}
Lalu reload aplikasi.

5. Tambah Data
Buka project flutter_application_3_tampildata lalu jalankan di chrome.

Buka terminal lalu buat page baru dengan perintah berikut:


> get create page:StudentForm

Buka home_controller.dart lalu buat method dengan nama onTapButtonTambah seperti berikut:
  void onTapButtonTambah() {
    Get.toNamed(Routes.STUDENT_FORM);
  }
Method onTapButtonTambah jika dijalankan akan membuka halaman STUDENT_FORM.

Buat tombol di widget appbar seperti berikut:


actions: [
  IconButton(
    onPressed: () => controller.onTapButtonTambah(),
    icon: Icon(Icons.add),
  ),
  IconButton(
    onPressed: () => controller.getStudents(),
    icon: Icon(Icons.refresh),
  )
],
Perintah diatas akan membuat 2 tombol, yaitu untuk berpindah ke student_form dan untuk memuat
ulang data student atau refresh.

5.1 Buat Method postData


Buka file student_provider.dart lalu kita tambahkan method baru dengan nama postData:
  Future<Response<dynamic>> postData(var studentJson) async {
    return await post(
        'https://materi-flutter-default-rtdb.asia-southeast1.firebasedatabase.app/students.json',
        studentJson);
  }
Pada kode diatas, kita membuat method postData dengan parameter studentJson yang tipe datanya.
Kode ini akan mengirim data (post) ke url.
5.2 Buat Method addData
Buka file student_form_controller.dart lalu modifikasi menjadi seperti berikut:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_application_3_tampil/app/data/student_provider.dart';
import 'package:get/get.dart';

class StudentFormController extends GetxController {


  TextEditingController txtNama = TextEditingController();
  TextEditingController txtKelas = TextEditingController();
  TextEditingController txtNim = TextEditingController();
  var isLoading = false.obs;

  void addData() {
    isLoading.value = true;
    String nama = txtNama.text;
    String kelas = txtKelas.text;
    String nim = txtNim.text;
    if (nama.isEmpty || kelas.isEmpty || nim.isEmpty) {
      Get.snackbar(
        "Error",
        "Data tidak boleh kosong",
        backgroundColor: Colors.red,
        colorText: Colors.white,
      );
      isLoading.value = false;
      return;
    }
    //buat data json
    var studentJson = jsonEncode({
      "created_at": DateTime.now().toString(),
      "kelas": kelas,
      "nama": nama,
      "nim": int.parse(nim), //ini harus diubah menjadi integer
    });
    //kirim data ke provider
    StudentProvider().postData(studentJson).then((value) {
      if (value.statusCode == 200) {
        Get.snackbar(
          "Success",
          "Data berhasil ditambahkan",
          backgroundColor: Colors.green,
          colorText: Colors.white,
        );
      } else {
        Get.snackbar(
          "Error",
          value.statusText!,
          backgroundColor: Colors.red,
          colorText: Colors.white,
        );
      }
      isLoading.value = false;
    });
  }
}
Pada kode diatas, kita menyiapkan variabel dengan tipe TextEditingController yang nantinya akan
digunakan oleh widget TextField. Kita juga membuat variabel isLoading untuk menandai state apakah
sebelum request atau sesudah request.

Kita juga membuat method dengan nama addData() method ini akan memeriksa apakah nilai
TextEditingController kosong, jika kosong maka akan tampil pesan. Jika tidak maka eksekusi dilanjutkan
ke baris berikutnya.
Pada kode diatas, kita membuat data json yang berasal dari TextEditingController seperti berikut:
    var studentJson = jsonEncode({
      "created_at": DateTime.now().toString(),
      "kelas": kelas,
      "nama": nama,
      "nim": int.parse(nim),
    });
Variabel studentJson ini kita kirim ke postData yang ada di student_provider untuk kemudian di kirim ke
API firebase.
5.3 Buat Tampilan View
Selanjutkan kita mengubah student_form_view.dart menjadi seperti berikut:
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/student_form_controller.dart';

class StudentFormView extends GetView<StudentFormController> {


  const StudentFormView({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text('StudentFormView'),
          centerTitle: true,
        ),
        body: Obx(() => ListView(
              children: [
                controller.isLoading.value
                    ? Center(
                        child: CircularProgressIndicator(),
                      )
                    : Container(),
                Container(
                  margin: EdgeInsets.all(8),
                  child: Text(
                    "Form Data Mahasiswa",
                    style: TextStyle(
                      fontSize: 20,
                    ),
                  ),
                ),
                Container(
                  margin: EdgeInsets.all(8),
                  child: TextField(
                    controller: controller.txtNama,
                    decoration: InputDecoration(
                      labelText: 'Nama',
                      border: OutlineInputBorder(),
                    ),
                  ),
                ),
                Container(
                  margin: EdgeInsets.all(8),
                  child: TextField(
                    controller: controller.txtNim,
                    decoration: InputDecoration(
                      labelText: 'Nim',
                      border: OutlineInputBorder(),
                    ),
                  ),
                ),
                Container(
                  margin: EdgeInsets.all(8),
                  child: TextField(
                    controller: controller.txtKelas,
                    decoration: InputDecoration(
                      labelText: 'Kelas',
                      border: OutlineInputBorder(),
                    ),
                  ),
                ),
                Container(
                  margin: EdgeInsets.all(8),
                  child: ElevatedButton(
                    onPressed: () => controller.addData(),
                    child: Text('SIMPAN'),
                  ),
                )
              ],
            )));
  }
}
Pada kode diatas kita membuat input TextField dengan controller yang sudah dibuat di
student_form_controller.dart :
TextField(
                    controller: controller.txtNama,
                    decoration: InputDecoration(
                      labelText: 'Nama',
                      border: OutlineInputBorder(),
                    ),
                  )
Untuk mendapatkan nilai dari widget TextField kita membutuhkan controller yang bisa dibuat dari class
TextEditingController

6. Ubah Data
Untuk mengubah data API, kita membutuhkan attribute yang unik, pada api firebase kita bisa
menggunakan unik id seperti berikut:

Buka projek sebelumnya lalu jalankan dengan chrome.

Buka file student_provider.dart lalu tambahkan method update seperti berikut:


Future<Response<dynamic>> updateData(String id, var studentModel) async {
  return await put(
      "https://materi-flutter-default-rtdb.asia-southeast1.firebasedatabase.app/students/$id.json",
      studentModel);
}
buat page baru dengan nama StudentEditForm
> get create page:StudentEditForm
Reload aplikasi.

Buka home_controller.dart lalu modif kode pada method getStudents seperti berikut:
 jsonResponse.forEach((key, value) {
        Map studentModel = {
          "key":key,
"nim": value['nim'],
          "nama": value['nama'],
          "kelas": value['kelas'],
        };
        listData.add(studentModel);
      });
Pad kode diatas, kita menambahkan data key dan nim ke studentModel, key ini yang nantinya akan
digunakan untuk mengubah data.

Berikutnya kita akan membuat method agar user berpindah ke page studentEditForm. Buka
home_controller.dart lalu tambahkan kode berikut:
void onTapButtonEdit(var studentModel) async {
  var data =
      await Get.toNamed(Routes.STUDENT_EDIT_FORM, arguments: studentModel);
  if (data != null) {
    getStudents();
  }
}
Pada kode diatas, kita membuat kode untuk berpindah ke student edit dengan membawa data
argument yaitu studentmodel yang didapat dari parameter. Kita menggunakan await yang digunakan
ketika user kembali ke halaman ini maka method getStudents akan dijalankan.
return Container(
  color: Colors.white,
  margin: EdgeInsets.only(bottom: 1),
  child: ListTile(
    title: Text(list['nama']),
    subtitle: Text(list['kelas']),
    trailing: Row(
      mainAxisSize: MainAxisSize.min,
      children: [
        IconButton(
          onPressed: () => controller.onTapButtonEdit(list),
          icon: Icon(Icons.edit),
        )
      ],
    ),
  ),
);
Pada kode diatas, kita membuat icon button di argument trailing dan pada saat icon di tekan kita
memanggil method onTapButtonEdit(list), di onTapButtonEdit, kita mengirim parameter list yang berisi
nama, kelas dan key.
Buka student_edit_form_controller.dart lalu modifikasi kodenya menjadi seperti berikut:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_application_3_tampil/app/data/student_provider.dart';
import 'package:get/get.dart';

class StudentEditFormController extends GetxController {


  var studentModel = Get.arguments;
  TextEditingController txtNama = TextEditingController();
  TextEditingController txtNim = TextEditingController();
  TextEditingController txtKelas = TextEditingController();
  var isLoading = false.obs;

  @override
  void onInit() {
    txtNama.text = studentModel['nama'];
    txtNim.text = studentModel['nim'].toString();
    txtKelas.text = studentModel['kelas'];
    super.onInit();
  }

  void editData() {
    isLoading.value = true;
    String nama = txtNama.text;
    String nim = txtNim.text;
    String kelas = txtKelas.text;
    if (nama.isEmpty || kelas.isEmpty || nim.isEmpty) {
      Get.snackbar(
        "Error",
        "Data tidak boleh kosong",
        backgroundColor: Colors.red,
        colorText: Colors.white,
      );
      isLoading.value = false;
      return;
    }

    //buat data json


    var studentModelBaru = jsonEncode({
      "nama": nama,
      "kelas": kelas,
      "nim": int.parse(nim),
      "created_at": DateTime.now().toString()
    });

    StudentProvider()
        .updateData(studentModel['key'], studentModelBaru)
        .then((Response response) {
      if (response.statusCode == 200) {
        Get.defaultDialog(
            title: "OK",
            middleText: 'Data berhasil disimpan',
            barrierDismissible: false,
            onConfirm: () {
              Get.back();
              Get.back(result: true);
            });
      } else {
        Get.snackbar(
          "Error",
          response.statusText!,
          backgroundColor: Colors.red,
          colorText: Colors.white,
        );
      }
      isLoading.value = false;
    });
  }
}
Pada kode diatas, kita mengambil nilai yang dikirim dari onTapButtonEdit dengan perintah
  var studentModel = Get.arguments;
lalu pada method onInit, kita mengisi nilai di variabel TextEditingController txtNama, txtNim, txtKelas:
@override
void onInit() {
  txtNama.text = studentModel['nama'];
  txtNim.text = studentModel['nim'].toString();
  txtKelas.text = studentModel['kelas'];
  super.onInit();
}
Kode ini untuk menampilkan nilai di widget TextField

Pada kode ini, kita menggunakan Get.defaultDialog untuk menampilkan konfirmasi data berhasil
disimpan. Jika user mengklik tombol ok, maka kita menjalankan Get.back() untuk menutup dialog lalu
kita menjalankan lagi Get.back(‘result:true’) untuk kembali ke halaman sebelumnya dengan membawa
data result true.
Buka file home_student_ edit_form.dart lalu modif seperti berikut: (kode ini sama dgn kode
student_form.
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/student_edit_form_controller.dart';

class StudentEditFormView extends GetView<StudentEditFormController> {


  const StudentEditFormView({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('EDIT FORM'),
        centerTitle: true,
      ),
      body: Obx(
        () => ListView(
          children: [
            controller.isLoading.value
                ? Center(
                    child: CircularProgressIndicator(),
                  )
                : Container(),
            Container(
              margin: EdgeInsets.all(8),
              child: Text(
                "Form Data Mahasiswa",
                style: TextStyle(
                  fontSize: 20,
                ),
              ),
            ),
            Container(
              margin: EdgeInsets.all(8),
              child: TextField(
                controller: controller.txtNama,
                decoration: InputDecoration(
                  labelText: 'Nama',
                  border: OutlineInputBorder(),
                ),
              ),
            ),
            Container(
              margin: EdgeInsets.all(8),
              child: TextField(
                controller: controller.txtNim,
                decoration: InputDecoration(
                  labelText: 'Nim',
                  border: OutlineInputBorder(),
                ),
              ),
            ),
            Container(
              margin: EdgeInsets.all(8),
              child: TextField(
                controller: controller.txtKelas,
                decoration: InputDecoration(
                  labelText: 'Kelas',
                  border: OutlineInputBorder(),
                ),
              ),
            ),
            Container(
              margin: EdgeInsets.all(8),
              child: ElevatedButton(
                onPressed: () => controller.editData(),
                child: Text('SIMPAN'),
              ),
            )
          ],
        ),
      ),
    );
  }
}

7. Hapus Data
Buka file student_provider.dart lau tambahkan method berikut:
  Future<Response<dynamic>> deleteData(String id) async {
    return await delete(
        "https://materi-flutter-default-rtdb.asia-southeast1.firebasedatabase.app/students/$id.json");
 }
Buka home_controller.dart lalu tambahkan method berikut:

Buka home_view.dart lalu tambahkan icon button delete seperti berikut:


void onTapIconDelete(String id) {
  Get.defaultDialog(
      title: "Konfirmasi Hapus",
      middleText: "Anda yakin ingin menghapus data ?",
      textConfirm: "Confirm",
      textCancel: "Cancel",
      barrierDismissible: false,
      onConfirm: () {
        isLoading.value = true;
        StudentProvider().deleteData(id).then((Response response) {
          if (response.statusCode == 200) {
            Get.defaultDialog(
                title: "OK",
                middleText: 'Data berhasil dihapus',
                barrierDismissible: false,
                onConfirm: () {
                  Get.back();
                  Get.back(result: true);
                });
          } else {
            Get.snackbar(
              "Error",
              response.statusText!,
              backgroundColor: Colors.red,
              colorText: Colors.white,
            );
     }
          isLoading.value = false;
          getStudents();
        });
      });
}

Selanjutnya kita akan membuat icon delete di listtile, buka home_view.dart lalu modifikasi kodenya jadi
seperti berikut:
return Container(
  color: Colors.white,
  margin: EdgeInsets.only(bottom: 1),
  child: ListTile(
    title: Text(list['nama']),
    subtitle: Text(list['kelas']),
    trailing: Row(
      mainAxisSize: MainAxisSize.min,
      children: [
        IconButton(
          onPressed: () => controller.onTapButtonEdit(list),
          icon: Icon(Icons.edit),
        ),
        IconButton(
          onPressed: () =>
              controller.onTapIconDelete(list['key']),
          icon: Icon(Icons.delete),
        ),
      ],
    ),
  ),
Pada onPressed icon delete, kita memanggil fungsi onTapIconDelete dengan membawa nilai dari key.
Key inilah nanti yang akan dikirim ke provider lalu di kirim ke API firebase untuk dihapus.

8. MID
9. Proyek Aplikasi SPP
Pada bagian ini kita akan membuat proyek baru, kita akan membuat aplikasi yang terdiri dari modul:

1. Register
2. Login
3. Home (auth)
4. Data siswa (auth)
5. Data Biaya (auth)
6. Data Tagihan (auth)

Base url / API aplikasi kita adalah http://belajar-api.unama.ac.id

Setiap proses request harus menambahkan pada bagian http header yaitu Accept: application/json

Untuk mengakses modul selain register dan login, kita harus menambahkan pada bagian http header
berupa bearer token yang didapat dari proses login.
10. Register
Membuat Register sama dengan membuat tambah data, karena register adalah proses user mengirim
data ke server.

Berikut adalah tampilan form register yang akan dibuat:

Berikut adalah keterangan dari API register:


url http://belajar-api.unama.ac.id/api/register
method POST
header Accept: application/json
body name:required|string
email: required|email
password: required|string
Response OK http status code : 201
"data": {
    "name": "abdul rahim",
    "email": "user@unama.ac.id",
    "updated_at": "2022-10-28 03:49:34",
    "created_at": "2022-10-28 03:49:34",
    "id": 13,
    "avatar_url": null
},
"message": "Pendaftaran berhasil, silahkan buka email klik link aktivtasi"
Response error 422 data tidak benar.

Jika pendaftaran berhasil, maka sistem belajar-api unama akan mengirim email untuk mengaktifkan
akun agar dapat login.

Buat Proyek baru dengan nama flutter_proyek_nim lalu jalankan perintah get init:
 get init
Install package flutter_easy_loading agar kita bisa dengan mudah membuat loading
(https://pub.dev/packages/flutter_easyloading ) :
 flutter pub add flutter_easyloading

Buka file main.dart lalu modif kodenya menjadi seperti berikut:


runApp(
  GetMaterialApp(
    title: "Application",
    initialRoute: AppPages.INITIAL,
    getPages: AppPages.routes,
    builder: EasyLoading.init(),
  ),
);

Buat page register dengan perintah berikut:


 get create page:register

Buat provider dengan nama register:


 get create provider:register

Buka file app_pages.dart di file ini kita akan mengubah inisial page, sehingga page yang akan dijalankan
pertama kali adalah page register
  static const INITIAL = Routes.REGISTER;
simpan lalu lakukan reload.

Buka file register_provider.dart lalu tambahkan method berikut:


import 'package:get/get.dart';

class RegisterProvider extends GetConnect {


  Future<Response<dynamic>> postData(var data) async {
    Map<String, String> headers = {
      'Accept': 'application/json',
    };
    return await post('http://belajar-api.unama.ac.id/api/register', data,
        headers: headers);
 }
}
Pada method postData, kita tidak hanya mengirim data pada http.body, namun kita juga menambahkan
informasi pada http.header yaitu agar server API menerima request ini sebagai application/json, ini
merupakan konfigurasi pada server API yang akan menjadi pembeda antara request yang berasa dari
web/form dan yang berasal dari external seperti mobile.
Berikutnya buka file register_controller.dart lalu modifikasi kodenya menjadi seperti berikut:
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_proyek_nim/app/data/register_provider.dart';
import 'package:flutter_proyek_nim/app/routes/app_pages.dart';
import 'package:get/get.dart';

class RegisterController extends GetxController {


  TextEditingController txtName = TextEditingController();
  TextEditingController txtEmail = TextEditingController();
  TextEditingController txtPassword = TextEditingController();

  void onTapButtonSimpan() {
    String name = txtName.text;
    String email = txtEmail.text;
    String password = txtPassword.text;
    if (name.isEmpty || email.isEmpty || password.isEmpty) {
      Get.snackbar(
        "Proses Gagal",
        'Data tidak boleh kosong',
        backgroundColor: Colors.red,
        colorText: Colors.white,
      );
      return;
  }
    EasyLoading.show();
    var dataRegistrasi = {
      "name": name,
      "email": email,
      "password": password,
    };
    RegisterProvider().postData(dataRegistrasi).then((Response response) {
      var responseBody = response.body;
      EasyLoading.dismiss();
      if (response.statusCode == 201) {
        Get.snackbar(
          "Success",
          "Data berhasil ditambahkan",
          backgroundColor: Colors.green,
          colorText: Colors.white,
        );
        txtName.text = "";
        txtEmail.text = "";
        txtPassword.text = "";
      } else if (response.statusCode == 422) {
        Get.snackbar(
          "Proses Gagal",
          responseBody['message']!,
          backgroundColor: Colors.red,
          colorText: Colors.white,
        );
      } else {
        Get.snackbar(
          "Proses Gagal",
          "Terjadi Kesaahan ",
          backgroundColor: Colors.red,
          colorText: Colors.white,
        );
   }
    });
 }

  void onTapTextLogin() {
    Get.toNamed(Routes.LOGIN);
 }
}

Buka file register_view.dart lalu modifikasi kodenya menjadi seperti berikut:


import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../routes/app_pages.dart';
import '../controllers/register_controller.dart';

class RegisterView extends GetView<RegisterController> {


  const RegisterView({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    double marginTextField = 10;
    OutlineInputBorder borderStyle = OutlineInputBorder(
      borderRadius: BorderRadius.circular(10),
    );
    return Scaffold(
      appBar: AppBar(
        elevation: 0,
        backgroundColor: Colors.white,
      ),
      backgroundColor: Colors.white,
      body: SingleChildScrollView(
        child: Column(
          children: [
            Container(
              child: FlutterLogo(
                size: 80,
              ),
            ),
            Container(
              margin: EdgeInsets.all(marginTextField),
              child: Text(
                "Daftar Akun Operator",
                style: TextStyle(
                  fontSize: 24,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ),
            Container(
              margin: EdgeInsets.all(marginTextField),
              child: TextField(
                controller: controller.txtName,
                decoration: InputDecoration(
                  labelText: 'Nama Lengkap',
                  border: borderStyle,
                ),
              ),
            ),
            Container(
              margin: EdgeInsets.all(marginTextField),
              child: TextField(
                controller: controller.txtEmail,
                decoration: InputDecoration(
                  labelText: 'Email',
                  border: borderStyle,
                ),
              ),
            ),
            Container(
              margin: EdgeInsets.all(marginTextField),
              child: TextField(
                controller: controller.txtPassword,
                obscureText: true,
                decoration: InputDecoration(
                  labelText: 'Password',
                  border: borderStyle,
                ),
              ),
            ),
            SizedBox(
              height: 10,
            ),
            ElevatedButton(
              style: ElevatedButton.styleFrom(
                fixedSize: Size(250, 50),
                padding:
                    EdgeInsets.only(left: 30, right: 30, top: 15, bottom: 15),
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(40),
                ),
              ),
              onPressed: () => controller.onTapButtonSimpan(),
              child: Text(
                'Daftar Sekarang',
                style: TextStyle(fontSize: 16),
              ),
            ),
            SizedBox(
              height: 20,
            ),
            Column(
              children: [
                Text('Sudah punya akun ?'),
                TextButton(
                  child: const Text(
                    'Login aja',
                    style: TextStyle(fontSize: 20),
                  ),
                  onPressed: () => Get.toNamed(Routes.LOGIN),
        )
              ],
              mainAxisAlignment: MainAxisAlignment.center,
            ),
          ],
        ),
      ),
    );
 }
}
11. Login
Tampilan yang akan dibuat
Berikut adalah deskripsi dari API login:
url http://belajar-api.unama.ac.id/api/login
method POST
header Accept: application/json
body email: email@email.com
password:123
Response http status code : 200
{
    "data": {
        "id": 9,
        "name": "abdul rahim",
        "akses": null,
        "nohp": null,
        "nohp_verified_at": null,
        "email": "user@unama.ac.id",
        "email_verified_at": null,
        "created_at": "2022-10-27 04:25:39",
        "updated_at": "2022-10-27 04:25:39",
        "token": "string_token",
        "avatar_url": null
    }
}
Response 422, Auth failed
error
Pada API diatas, jika user berhasil login maka server akan memberikan informasi data user dan token
dengan status kode 200. Jika gagal, maka server akan memberikan kode 422

Buat page login dengan perintah berikut:


 get create page:login

Buat provider dengan nama login user:


 get create provider:user

Install package baru dengan nama sp_util, dengan package ini kita dapat menyimpan token dan data
user secar persistent.
 flutter pub add sp_util

Reload aplikasi.

Buka file main.dart lalu modif fungsi main untuk menjalankan package sp_util :
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await SpUtil.getInstance();
  runApp(
    GetMaterialApp(
      title: "Application",
      initialRoute: AppPages.INITIAL,
      getPages: AppPages.routes,
      debugShowCheckedModeBanner: false,
      builder: EasyLoading.init(),
    ),
  );
}
Buka file register_view.dart lalu tambahkan kode untuk mengarahkan user ke halaman Login

Buka file user_provider.dart lalu buat method login seperti berikut:


import 'package:get/get.dart';

class UserProvider extends GetConnect {


  Future<Response<dynamic>> login(var data) async {
    Map<String, String> headers = {
      'Accept': 'application/json',
    };
    return await post('http://belajar-api.unama.ac.id/api/login', data,
        headers: headers);
 }
}
Buka file login_controller.dart lalu modif seperti berikut:
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_proyek_nim/app/data/user_provider.dart';
import 'package:flutter_proyek_nim/app/routes/app_pages.dart';
import 'package:get/get.dart';
import 'package:sp_util/sp_util.dart';

class LoginController extends GetxController {


  TextEditingController txtEmail = TextEditingController();
  TextEditingController txtPassword = TextEditingController();

  void onTapButtonLogin() {
    String email = txtEmail.text;
    String password = txtPassword.text;
    if (email.isEmpty || password.isEmpty) {
      Get.snackbar(
        "Proses Gagal",
        'Data tidak boleh kosong',
        backgroundColor: Colors.red,
        colorText: Colors.white,
      );
      return;
  }
    EasyLoading.show();
    var dataLogin = {
      "email": email,
      "password": password,
    };
    UserProvider().login(dataLogin).then((Response response) {
      var responseBody = response.body;
      EasyLoading.dismiss();
      if (response.statusCode == 200) {
        var responseData = responseBody['data'];
        SpUtil.putString('token', responseData['token']);
        SpUtil.putString('id', responseData['id'].toString());
        SpUtil.putString('name', responseData['name']);
        SpUtil.putString('email', responseData['email']);
SpUtil.putBool('isLogin', true);
        Get.offAllNamed(Routes.HOME);
      } else if (response.statusCode == 422) {
        Get.snackbar(
          "Proses Gagal",
          responseBody['message']!,
          backgroundColor: Colors.red,
          colorText: Colors.white,
        );
      } else {
        Get.snackbar(
          "Proses Gagal",
          "Terjadi Kesaahan ",
          backgroundColor: Colors.red,
          colorText: Colors.white,
        );
   }
    });
 }
}

Pada kode login, jika login berhasil maka kita menggunakan perintah Get.offAllNamed perintah ini akan
mengarahkan user ke halaman home dan user tidak bisa kembalil ke halaman sebelumnya.

Buka file login_view.dart lalu modif kodenya jadi seperti berikut:


import 'package:flutter/material.dart';
import 'package:flutter_proyek_nim/app/routes/app_pages.dart';
import 'package:get/get.dart';
import '../controllers/login_controller.dart';

class LoginView extends GetView<LoginController> {


  const LoginView({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    double marginTextField = 10;
    OutlineInputBorder borderStyle = OutlineInputBorder(
      borderRadius: BorderRadius.circular(10),
    );
    return Scaffold(
      appBar: AppBar(
        elevation: 0,
        backgroundColor: Colors.white,
      ),
      backgroundColor: Colors.white,
      body: SingleChildScrollView(
        child: Column(
          children: [
            Container(
              child: FlutterLogo(
                size: 80,
              ),
            ),
            Container(
              margin: EdgeInsets.all(marginTextField),
              child: Text(
                "Login Sebagai Operator",
                style: TextStyle(
                  fontSize: 24,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ),
            Container(
              margin: EdgeInsets.all(marginTextField),
              child: TextField(
                controller: controller.txtEmail,
                decoration: InputDecoration(
                  labelText: 'Email',
                  border: borderStyle,
                ),
              ),
            ),
            Container(
              margin: EdgeInsets.all(marginTextField),
              child: TextField(
                controller: controller.txtPassword,
                obscureText: true,
                decoration: InputDecoration(
                  labelText: 'Password',
                  border: borderStyle,
                ),
              ),
            ),
            SizedBox(
              height: 10,
            ),
            ElevatedButton(
              style: ElevatedButton.styleFrom(
                fixedSize: Size(250, 50),
                padding:
                    EdgeInsets.only(left: 30, right: 30, top: 15, bottom: 15),
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(40),
                ),
              ),
              onPressed: () => controller.onTapButtonLogin(),
              child: Text(
                'L O G I N',
                style: TextStyle(fontSize: 16),
              ),
            ),
            SizedBox(
              height: 15,
            ),
            Column(
              children: [
                Text('Belum punya akun ?'),
                TextButton(
                  child: const Text(
                    'Daftar disini',
                    style: TextStyle(fontSize: 20),
                  ),
                  onPressed: () {
                    Get.toNamed(Routes.REGISTER);
                  },
        )
              ],
              mainAxisAlignment: MainAxisAlignment.center,
            ),
          ],
        ),
      ),
    );
 }
}
12. Halaman Utama
Berikut adalah tampilan halaman utama aplikasi yang akan kita buat:

Pada halaman utama diatas terdapat tombol untuk mengubah profil, mengolah data siswa dan tombol
logout. Kita akan membuat page untuk ubah profil dan mengolah data siswa.
 get create page:ProfilForm

 get create page:SiswaList

 get create page:SiswaForm


Berikut kode untuk home_view.dart:
import 'package:flutter/material.dart';
import 'package:flutter_proyek_nim/app/routes/app_pages.dart';
import 'package:get/get.dart';
import 'package:sp_util/sp_util.dart';
import '../controllers/home_controller.dart';

class HomeView extends GetView<HomeController> {


  const HomeView({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    double widthButton = Get.width - 130;
    return Scaffold(
        appBar: AppBar(
          title: const Text('Halaman Utama'),
          backgroundColor: Colors.white,
          elevation: 0,
        ),
        backgroundColor: Colors.white,
        body: SingleChildScrollView(
          child: Container(
            alignment: Alignment.center,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.start,
              children: [
                Container(
                  margin: EdgeInsets.only(top: Get.height / 5),
                  child: Text(
                    'Aplikasi Tagihan SPP',
                    style: TextStyle(fontSize: 20),
                  ),
                ),
                Container(
                  margin: EdgeInsets.only(top: 0),
                  child: Text(
                    'SD Usaha Mandiri',
                    style: TextStyle(fontSize: 30),
                  ),
                ),
                Container(
                  width: widthButton,
                  alignment: Alignment.centerLeft,
                  margin: EdgeInsets.only(top: 30),
                  child:
                      Text('Selamat Datang User, ${SpUtil.getString("name")}'),
                ),
                Container(
                  width: widthButton,
                  height: 60,
                  child: ElevatedButton(
                    onPressed: () {
                      //route profil
                      Get.toNamed(Routes.PROFIL_FORM);
                    },
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        Icon(Icons.edit),
                        SizedBox(
                          width: 5,
                        ),
                        Text('Ubah Profil'),
                      ],
                    ),
                  ),
                ),
                Container(
                  width: widthButton,
                  height: 60,
                  margin: EdgeInsets.only(top: 16),
                  child: ElevatedButton(
                    onPressed: () {
                      //route menampilkan list data siswa
                      Get.toNamed(Routes.SISWA_LIST);
                    },
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        Icon(Icons.switch_account),
                        SizedBox(
                          width: 5,
                        ),
                        Text('Data Siswa'),
                      ],
                    ),
                  ),
                ),
                Container(
                  width: widthButton,
                  height: 60,
                  margin: EdgeInsets.only(top: 16),
                  child: ElevatedButton(
                    onPressed: () {
                      SpUtil.clear();
                      Get.offAllNamed('/login');
                    },
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        Icon(Icons.logout),
                        SizedBox(
                          width: 5,
                        ),
                        Text('Logout'),
                      ],
                    ),
                  ),
                ),
              ],
            ),
          ),
        ));
 }
}
Pada kode diatas, membuat kode logout, dimana ketika user menekan tombol logout maka kita akan
menghapus semua isi data Sputil dengan perintah clear() lalu kita menghapus semua halaman dan
mengarahkan user ke halaman login.
13. Redirect Auth User
Pada bagian ini kita akan membuat kode dimana jika user telah login, maka setiap aplikasi dijalankan
yang terbuka adalah halaman utama, bukan lagi halaman login, jika user belum login maka halaman
login yang akan ditampilkan.

Buka file main.dart lalu buat kode seperti berikut:


  runApp(
    GetMaterialApp(
      title: "Application",
      initialRoute: SpUtil.getBool('isLogin')! ? Routes.HOME : Routes.LOGIN,
      getPages: AppPages.routes,
      debugShowCheckedModeBanner: false,
      builder: EasyLoading.init(),
    ),
  );
Pada kode diatas, kita membuat kondisi, jika SpUtils terdapat variabel isLogin yang bernilai true maka
halaman yang akan tampil pertama kali adalah HOME jika tidak maka tampil halaman LOGIN.

14. CRUD Data Siswa


14.1 Tambah Data Siswa
Berikut adalah spesifikasi API untuk tambah data siswa:
url http://belajar-api.unama.ac.id/api/siswa
method POST
header Accept: application/json
body nisn:required|unique
nama: required
kelas: required
tanggal_lahir: required|date:YYYY-MM-DD
foto: required|mimes:jpg, jpeg, png, maks:5MB
Response OK http status code : 201
{
    "nisn": "120120010231",
    "nama": "budi",
    "kelas": "0101",
    "foto": "public/Az6YWm9Joggl9kVoa221y0V2tswRDtBln7hz2l0K.jpg",
    "tanggal_lahir": "2022-12-12",
    "updated_at": "2022-11-04 09:34:25",
    "created_at": "2022-11-04 09:34:25",
    "id": 3,
    "foto_url": "http://belajar-api.unama.ac.id/storage/nama-file-server.jpg"
}
Response error 422 data tidak benar.
Berdasarkan spesifikasi respon API, kita dapat membuat class model untuk memudahkan kita mengubah
data dari json ke objek ataupun sebaliknya dari objek menjadi json.
Gambar 1 Konsep Model
Pada gambar 1, controller menerima data inputan dari widget textfield lalu data ini akan dikirim ke class
model untuk diubah menjadi format json, kemudian data ini akan dikirim ke provider lalu di proses ke
API.

Jadi kode untuk mengubah data dari class TextEditingController menjadi JSON tidak lagi di controller,
namun di letakkan di class tersendiri.

Membuat class model pada dart dapat dibuat dengan mudah melalui situs
https://javiercbk.github.io/json_to_dart/
Ketik nama class SiswaModel lalu klik tombol Generate dart, klik Copy Dart code to clipboard lalu
pastekan di file siswa_model.dart

Berikut isi dari siswa_model.dart :


class SiswaModel {
  String? nisn;
  String? nama;
  String? kelas;
  String? foto;
  String? tanggalLahir;
  String? updatedAt;
  String? createdAt;
  int? id;
  String? fotoUrl;

  SiswaModel(
      {this.nisn,
      this.nama,
      this.kelas,
      this.foto,
      this.tanggalLahir,
      this.updatedAt,
      this.createdAt,
      this.id,
      this.fotoUrl});

  SiswaModel.fromJson(Map<String, dynamic> json) {


    nisn = json['nisn'];
    nama = json['nama'];
    kelas = json['kelas'];
    foto = json['foto'];
    tanggalLahir = json['tanggal_lahir'];
    updatedAt = json['updated_at'];
    createdAt = json['created_at'];
    id = json['id'];
    fotoUrl = json['foto_url'];
 }

  Map<String, dynamic> toJson() {


    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['nisn'] = this.nisn;
    data['nama'] = this.nama;
    data['kelas'] = this.kelas;
    data['foto'] = this.foto;
    data['tanggal_lahir'] = this.tanggalLahir;
    data['updated_at'] = this.updatedAt;
    data['created_at'] = this.createdAt;
    data['id'] = this.id;
    data['foto_url'] = this.fotoUrl;
    return data;
 }
}

Install package intl agar kita bisa mengolah tipe data waktu dengan mudah:
 flutter pub add intl

Install image picker untuk menggunakan widget kamera dan image galeri
 flutter pub add image_picker

Stop dan Jalankan ulang aplikasi flutter agar semua file package baru dapat terbaca.
Buat tombol untuk berpindah dari siswa_list_view ke siswa_form_view

Buka file siswa_list_view.dart, kita akan membuat tombol tambah menggunakan widget
FloatingActionButton seperti berikut:

Buat file provider dan buat method postData:


 get create provider:Siswa

Jika kita pelajari kode di file provider yang telah dibuat sebelumnya, semua methodnya membutuhkan
header dan url yang sama, agar kode kita lebih baik, kita bisa membuat satu file misalnya dengan nama
BaseApiProvider lalu provider lain menjadi turunan dari provider ini:
 get create provider:BaseApi

Buka file tersebut, lalu ubah kodenya menjadi seperti berikut:


import 'package:get/get.dart';
import 'package:sp_util/sp_util.dart';

class BaseApiProvider extends GetConnect {


  final String apiUrl = 'http://belajar-api.unama.ac.id/api';
  final Map<String, String> headers = {
    'Accept': 'application/json',
    'Authorization': 'Bearer ${SpUtil.getString("token")}'
  };
}
Di file ini kita mendefinisikan variabel url dan headers, tujuannya adalah agar semua provider yang
menjadi turunan dari provider ini bisa menggunakan variabel apiUrl dan headers.
Buka file user_provider.dart lalu modif kodenya menjadi seperti berikut:
import 'package:flutter_proyek_nim/app/data/base_api_provider.dart';
import 'package:get/get.dart';

class UserProvider extends BaseApiProvider {


  Future<Response<dynamic>> login(var data) async {
    return await post('$apiUrl/login', data, headers: headers);
 }
}
Di class UserProvider, kita mengubah extend yang tadinya GetConnect menjadi BaseApiProvider
tujuannya agar class UserProvider bisa mengakses semua variabel di class BaseApiProvider dan di class
GetConnect

Buka file register_provider lalu ubah kodenya agar extends ke BaseApiProvider


import 'package:flutter_proyek_nim/app/data/base_api_provider.dart';
import 'package:get/get.dart';

class RegisterProvider extends BaseApiProvider {


  Future<Response<dynamic>> postData(var data) async {
    return await post('$apiUrl/register?delay=2', data, headers: headers);
 }
}

Buka siswa_provider.dart lalu modif seperti berikut:

Buka file siswa_provider.dart lalu tambahkan method postData seperti berikut:


import 'dart:io';
import 'package:flutter_proyek_nim/app/data/base_api_provider.dart';
import 'package:flutter_proyek_nim/app/data/siswa_model.dart';
import 'package:get/get.dart';

class SiswaProvider extends BaseApiProvider {


  Future<Response> postData(SiswaModel siswaModel) async {
    final FormData formData = FormData({
      'foto': MultipartFile(File(siswaModel.foto.toString()).readAsBytesSync(),
          filename: siswaModel.foto.toString().split('/').last),
      'nisn': siswaModel.nisn,
      'nama': siswaModel.nama,
      'kelas': siswaModel.kelas,
      'tanggal_lahir': siswaModel.tanggalLahir,
    });

    var response = await post('$apiUrl/siswa', formData, headers: headers);


    return response;
 }
}
Pada method postData, kita menggunakan FormData untuk menghandle upload foto. Lalu
menambahkan path foto. Foto akan dikirim menggunakan MultipartFile ke server api.

Buka file siswa_form_controller.dart lalu modif jadi seperti berikut:


import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_proyek_nim/app/data/siswa_model.dart';
import 'package:flutter_proyek_nim/app/data/siswa_provider.dart';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';

class SiswaFormController extends GetxController {


  TextEditingController txtNisn = TextEditingController();
  TextEditingController txtNama = TextEditingController();
  TextEditingController txtKelas = TextEditingController();
  TextEditingController txtTanggalLahir = TextEditingController();
  var pathFile = "".obs;

  void onTapPicker(var type) async {


    final ImagePicker _picker = ImagePicker();
    XFile? image;
    if (type == 'galeri') {
      image = await _picker.pickImage(
        source: ImageSource.gallery,
        preferredCameraDevice: CameraDevice.rear,
      );
  }
    if (type == 'camera') {
      image = await _picker.pickImage(
        source: ImageSource.camera,
        imageQuality: 75,
      );
  }

    if (image != null) {
      pathFile.value = image.path;
  }
 }

  void addData() {
    String nisn = txtNisn.text;
    String nama = txtNama.text;
    String kelas = txtKelas.text;
    String tanggalLahir = txtTanggalLahir.text;

    if (nisn.isEmpty || nama.isEmpty || kelas.isEmpty || tanggalLahir.isEmpty) {


      Get.snackbar(
        "Error",
        "Data tidak boleh kosong",
        backgroundColor: Colors.red,
        colorText: Colors.white,
      );
      return;
  }
    SiswaModel model = SiswaModel(
      nisn: nisn,
      nama: nama,
      kelas: kelas,
      tanggalLahir: tanggalLahir,
      foto: pathFile.value,
    );
    EasyLoading.show();
    SiswaProvider().postData(model).then((value) {
      EasyLoading.dismiss();
      if (value.statusCode == 201) {
        Get.snackbar(
          "Yeay...",
          "Data berhasil ditambahkan",
          backgroundColor: Colors.green,
          colorText: Colors.white,
        );
        Get.back();
      } else {
        Get.snackbar(
          "Ops.. Terjadi Kesalahan",
          "Gagal menambahkan data, ${value.statusText.toString()}",
          backgroundColor: Colors.red,
          colorText: Colors.white,
        );
   }
    });
 }
}
Pada class SiswaFormController kita membuat fungsi onTapPicker, fungsi ini digunakan untuk
menghandle ketika user memilih file dari galeri atau dari camera.
var pathFile = "".obs;
void onTapPicker(var type) async {
  final ImagePicker _picker = ImagePicker();
  XFile? image;
  if (type == 'galeri') {
    image = await _picker.pickImage(
      source: ImageSource.gallery,
      preferredCameraDevice: CameraDevice.rear,
    );
 }
  if (type == 'camera') {
    image = await _picker.pickImage(
      source: ImageSource.camera,
      imageQuality: 75,
    );
 }
Lokasi gambar pilihan atau pun hasil camera akan disimpan di variabel pathFile untuk kemudian
ditampilkan di siswal_form_view menggunakan widget Image

Pada method addData kita mengubah data dari TextField menjadi objek class SiswaModel lalu mengirim
datanya ke postData yang ada di class SiswaProvider
SiswaModel model = SiswaModel(
  nisn: nisn,
  nama: nama,
  kelas: kelas,
  tanggalLahir: tanggalLahir,
  foto: pathFile.value,
);

Berikutnya buka file siswa_form_view.dart lalu ubah jadi seperti ini:


import 'dart:io';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import '../controllers/siswa_form_controller.dart';

class SiswaFormView extends GetView<SiswaFormController> {


  const SiswaFormView({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('SiswaFormView'),
        centerTitle: true,
      ),
      body: Obx(
        () => Container(
          margin: EdgeInsets.all(20),
          child: ListView(
            children: [
              Container(
                margin: EdgeInsets.only(bottom: 20),
                child: Text(
                  'FORM TAMBAH SISWA',
                  style: TextStyle(fontSize: 20),
                ),
                alignment: Alignment.center,
              ),
              Container(
                width: Get.width - 60,
                child: controller.pathFile.value == ""
                    ? Icon(
                        Icons.account_box,
                        size: 80,
           )
                    : Image.file(
                        File(controller.pathFile.value),
                        fit: BoxFit.cover,
                      ),
              ),
              SizedBox(height: 15),
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  IconButton(
                    onPressed: () => controller.onTapPicker('galeri'),
                    icon: Icon(
                      Icons.image,
                      size: 40,
                    ),
                  ),
                  SizedBox(width: 8),
                  IconButton(
                    onPressed: () => controller.onTapPicker('camera'),
                    icon: Icon(
                      Icons.add_a_photo,
                      size: 40,
                    ),
                  ),
                ],
              ),
              TextField(
                controller: controller.txtNisn,
                decoration: const InputDecoration(
                  border: OutlineInputBorder(),
                  hintText: "NISN",
                ),
              ),
              SizedBox(height: 10),
              TextField(
                controller: controller.txtNama,
                decoration: const InputDecoration(
                  border: OutlineInputBorder(),
                  hintText: "Nama",
                ),
              ),
              SizedBox(height: 10),
              TextField(
                controller: controller.txtKelas,
                decoration: const InputDecoration(
                  border: OutlineInputBorder(),
                  hintText: "Kelas",
                ),
              ),
              SizedBox(height: 10),
              TextField(
                controller: controller.txtTanggalLahir,
                decoration: const InputDecoration(
                  border: OutlineInputBorder(),
                  hintText: "Tanggal Lahir",
                ),
                readOnly: true,
                onTap: () async {
                  DateTime? pickedDate = await showDatePicker(
                    context: Get.context!,
                    initialDate: DateTime.now(),
                    firstDate: DateTime(2000),
                    lastDate: DateTime(2101),
                  );

                  if (pickedDate != null) {
                    String tanggalSelected =
                        DateFormat('yyyy-MM-dd').format(pickedDate);
                    controller.txtTanggalLahir.text = tanggalSelected;
         }
                },
              ),
              SizedBox(height: 10),
              ElevatedButton(
                onPressed: () {
                  controller.addData();
                },
                child: const Text("Simpan"),
              ),
            ],
          ),
        ),
      ),
    );
 }
}
Pada file siswa_form_view.dart kita membuat textfield dan menampilkan 2 icon button agar user bisa
memilih apakah gambar dipilih dari galeri atau dari camera,
Container(
  width: Get.width - 60,
  child: controller.pathFile.value == ""
      ? Icon(
          Icons.account_box,
          size: 80,
    )
      : Image.file(
          File(controller.pathFile.value),
          fit: BoxFit.cover,
        ),
),
SizedBox(height: 15),
Row(
  mainAxisAlignment: MainAxisAlignment.center,
  children: [
    IconButton(
      onPressed: () => controller.onTapPicker('galeri'),
      icon: Icon(
        Icons.image,
        size: 40,
      ),
    ),
    SizedBox(width: 8),
    IconButton(
      onPressed: () => controller.onTapPicker('camera'),
      icon: Icon(
        Icons.add_a_photo,
        size: 40,
      ),
    ),
  ],
),

Selain itu, kita juga membuat input yang menampilkan date picker seperti berikut:
TextField(
  controller: controller.txtTanggalLahir,
  decoration: const InputDecoration(
    border: OutlineInputBorder(),
    hintText: "Tanggal Lahir",
  ),
  readOnly: true,
  onTap: () async {
    DateTime? pickedDate = await showDatePicker(
      context: Get.context!,
      initialDate: DateTime.now(),
      firstDate: DateTime(2000),
      lastDate: DateTime(2101),
    );

    if (pickedDate != null) {
controller.txtTanggalLahir.text =
                        DateFormat('yyyy-MM-dd').format(pickedDate);
  }
  },
),

14.2 Tampil Data Siswa


Buka file siswa_provider.dart lalu tambahkan method getData:
Future<Response> getData() async {
  return await get('$apiUrl/siswa', headers: headers);
}
Buka siswa_list_controller.dart lalu modif kodenya seperti berikut:
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_proyek_nim/app/data/siswa_model.dart';
import 'package:flutter_proyek_nim/app/data/siswa_provider.dart';
import 'package:get/get.dart';

class SiswaListController extends GetxController {


  var listData = List<SiswaModel>.empty().obs;

  void getSiswa() {
    EasyLoading.show();
    listData.clear();
    SiswaProvider().getAll().then((Response response) {
      if (response.statusCode == 200) {
        var data = response.body['data'];
        for (var element in data) {
          listData.add(SiswaModel.fromJson(element));
    }
      } else {
        Get.snackbar(
          'Ops..Error ${response.statusText}',
          'Terjadi kesalahan',
          backgroundColor: Colors.red,
          colorText: Colors.white,
        );
   }
      EasyLoading.dismiss();
    });
 }

  @override
  void onInit() {
    getSiswa();
    super.onInit();
 }
}
Kode Get.snackbar sudah kita buat berulang-ulang, untuk menghemat kode, kita bisa membuat fungsi
untuk snackbar. Buka file main.dart lalu tambahkan kode berikut di bagian paling bawah:
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:get/get.dart';
import 'package:sp_util/sp_util.dart';
import 'app/routes/app_pages.dart';

void main() async {


  WidgetsFlutterBinding.ensureInitialized();
  await SpUtil.getInstance();
  runApp(
    GetMaterialApp(
      title: "Application",
      initialRoute: SpUtil.getBool('isLogin')! ? Routes.HOME : Routes.LOGIN,
      getPages: AppPages.routes,
      debugShowCheckedModeBanner: false,
      builder: EasyLoading.init(),
    ),
  );
}

void snackBarError(String title, String message) {


  Get.snackbar(
    title,
    message,
    backgroundColor: Colors.red,
    colorText: Colors.white,
  );
}

void snackBarSuccess(String title, String message) {


  Get.snackbar(
    title,
    message,
    backgroundColor: Colors.green,
    colorText: Colors.white,
  );
}

Sehingga, jika kita ingin menggunakan, tinggal menjalankan perintah:


snackBarError(
          'Ops.. Error',
          'Terjadi kesalahan ${response.statusText.toString()}',
);

15. CRUD Data Tagihan

Anda mungkin juga menyukai