Menyimpan Pengaturan Aplikasi dengan Flutter

Bagikan jika Anda sukai postingan ini!

Pembahasan kali ini saya akan mencoba menjelaskan bagaimana caranya menyimpan pengaturan-pengaturan pada aplikasi untuk kemudian digunakan kembali saat aplikasi dibuka di lain waktu. Pada Android native (Java atau Kotlin) tersedia pustaka SharedPrefrence, demikian pula pada Flutter. Namun jika pada Android native kita bisa langsung memanfaatkan pustaka, tidak begitu pada Flutter. Anda harus mengimpor pustaka tersebut lebih dulu.

Kita langsung ke pembahasan, silakan buka kembali proyek pada artikel sebelumnya. Untuk proses impor pustaka, Anda dapat mendaftarkan pustaka yang akan diimpor pada berkas pubspec.yaml. Temukan cabang pubspec.yaml pada panel Project, kemudian klik ganda untuk mengubahnya.

Cabang pubspec.yaml pada Panel Project

Pendaftaran pustaka dapat dituliskan setelah baris dependencies pada pubspec.yaml. Untuk menambahkan SharedPrefrence, tuliskan perintah berikut ini.

shared_preferences: ^0.5.3+1
Menambahkan shared_preferences pada pubspec.yaml

Simpan perubahan pada pubspec.yaml kemudian buka jendela kode berkas main.dart, atau berkas dart lainnya. Di atas jendela kode akan ditampilkan sebuah batang berjudul Pubspec has been edited. Klik tautan Get dependencies, tunggu sampai proses selesai.

Sayangnya kadang masih ada bug saat pemuatan pustaka ini. Di mana pustaka tidak dapat langsung Anda gunakan pada kode sumber. Untuk mengatasi bug ini, Anda harus menutup proyek dan membukanya kembali.

Catatan

Baik, setelah pustaka shared_preferences siap, buka kode sumber people.dart. Ubah menjadi seperti di bawah ini.

import 'dart:convert';

class PeopleItem {
  String name;
  int age;

  PeopleItem({this.name, this.age});

  factory PeopleItem.fromJson(Map<String, dynamic> json) {
    return new PeopleItem (
      name: json['name'] as String,
      age: json['age'] as int,
    );
  }

  Map<String, dynamic> toJson() => {
    'name': name,
    'age': age,
  };

  @override
  String toString() {
    var jsonData = json.encode(toJson());
    return jsonData;
  }
}

Saya menambahkan sebuah fungsi fromJson untuk mengonversi JSON menjadi kelas PeopleItem, toJSon untuk mengonversi kelas menjadi berformat JSON, serta mengubah metode bentukan toString untuk mengonversi JSON ke String. Proses konversi ini membutuhkan impor pustaka dart:convert yang sudah saya tulis pada bagian paling awal kode sumber.

Selanjutnya untuk proses pemanfaatan shared_preferences kita membutuhkan sebuah berkas dart baru. Silakan tambahkan berkas baru dengan nama shared.dart pada proyek Anda. Tulis kode sumber di bawah ini.

import 'package:shared_preferences/shared_preferences.dart';
import 'dart:convert';
import 'people.dart';

Future<String> _getSavedString(String key) async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
  return (prefs.getString(key) ?? "");
}

void _setSavedString(String key, String value) async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
  prefs.setString(key, value);
}

Future<PeopleItem> getPeopleSetting() async{
  String strPeople = await _getSavedString("people");
  if (strPeople.isNotEmpty){
    try {
      final jsonResponse = json.decode(strPeople);
      return PeopleItem.fromJson(jsonResponse);
    } catch (e) {
      /* Bila terjadi error pada penyimpanan
        maka tentukan nilai default
      */
      return new PeopleItem(name: '', age: 0);
    }
  } else {
    /* Bila belum terdapat nilai penyimpanan
    maka tentukan nilai default
    */
    return new PeopleItem(name: '', age: 0);
  }
}

void setPeopleSetting(PeopleItem people) {
  _setSavedString("people", people.toString());
}

Saya membuat 2 (dua) buah metode private, yaitu _getSavedString dan _setSavedString, serta 2 (dua) buah metode public, yaitu getPeopleSetting dan setPeopleSetting. Metode _getSavedString digunakan untuk mengambil nilai penyimpanan dari SharedPreferences berupa nilai kembali String. Kemudian nilai kembali ini dikonversi menjadi PeopleItem pada metode getPeopleSetting. Metode _setSavedString digunakan untuk menyimpan nilai String pada SharedPreferences. Sementara setPeopleSetting digunakan untuk menyimpan nilai PeopleItem dengan lebih dulu dikonversi menjadi String.

Anda dapat melihat tipe data baru yaitu Future. Future merupakan tipe data yang dihasilkan dari proses asinkronisasi (asynchronous). Perlu penanganan khusus untuk memanfaatkan tipe data ini. Anda akan melihatnya nanti.

Sekarang buka kode sumber dari main.dart. Pada bagian impor tambahkan baris kode di bawah ini.

import 'shared.dart';

Selanjutnya sebelum bagian deklarasi build, tambahkan kode di bawah ini.

var _load = Column(
  mainAxisAlignment: MainAxisAlignment.center,
  children: <Widget>[
    CircularProgressIndicator(),
    SizedBox(height: 10,),
    Text("Menyiapkan Data")
  ],
);

Kemudian ubah perintah-perintah pada metode build menjadi seperti di bawah ini.

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("My First App"),
      ),
      body: Center(
        child: FutureBuilder(
          /* Memanggil metode getPeopleSetting */
          future: getPeopleSetting(),
          /* Prosesi builder saat metode getPeopleSetting dieksekusi */
          builder: (BuildContext context, AsyncSnapshot snapshot){
            /* Saat snapshot mendapatkan data */
            if (snapshot.hasData) {
              /* Saat data pada snapshot tidak null */
              if (snapshot.data != null) {

                /* Mengubah _people dengan data dari snapshot */
                _people = snapshot.data;

                /* Mengembalikan nilai widget untuk anak dari Center */
                return Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    Padding(
                      padding: EdgeInsets.symmetric(
                          horizontal: 16.0, vertical: 4.0),
                      child: Row(
                        children: <Widget>[
                          Text("Name"),
                          SizedBox(width: 50.0,),
                          Text(_people.name,
                            style: TextStyle(
                                fontWeight: FontWeight.bold
                            ),
                          ),
                        ],
                      ),
                    ),
                    Padding(
                      padding: EdgeInsets.symmetric(
                          horizontal: 16.0, vertical: 4.0),
                      child: Row(
                        children: <Widget>[
                          Text("Age"),
                          SizedBox(width: 50.0,),
                          Text(_people.age.toString(),
                            style: TextStyle(
                                fontWeight: FontWeight.bold
                            ),
                          ),
                        ],
                      ),
                    ),
                    RaisedButton(
                      color: Colors.blue,
                      onPressed: setPeople,
                      child: Text("Edit",
                        style: TextStyle(
                          color: Colors.white,
                          fontSize: 14.0,
                        ),
                      ),
                    ),
                  ],
                );
              } else {
                return _load;
              }
            } else {
              return _load;
            }
          }
        ),
      ),
    );
  }

Apa yang berbeda? Pada argumen body, pada argumen child dari widget Column saya mengubahnya menjadi FutureBuilder untuk memanfaatkan tipe data Future pada metode getPeopleSetting di shared.dart. Terdapat 2 (dua) argumen yang harus disertakan pada widget FutureBuilder yaitu future, dan builder. Argumen future digunakan untuk menentukan metode (dengan nilai kembali berupa tipe data Future) yang akan dipanggil. Sementara argumen builder digunakan untuk menangkap proses yang terjadi saat eksekusi metode pada argumen future.

Setelah proses dari metode pada argumen future selesai dan nilai data pada snapshot tidak null, maka akan mengembalikan nilai berupa widget.

Selanjutnya ubah bagian kode pada deklarasi metode setPeople menjadi seperti di bawah ini.

  void setPeople(){
    /* Memanggil SecondActivity */
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => SecondActivity(people: _people),
      ),
    ).then((value){
      /* Ketika menerima data dari Navigator */
      if (value != null){
        /* Mengubah dan menyimpan variabel _people */
        setState(() {
          _people = value;

          /* Menyimpan _people pada SharedPreferences */
          setPeopleSetting(_people);
        });
      }
    });
  }

Saya menambahkan perintah setPeopleSetting untuk menyimpan hasil dari Navigator pada SharedPreferences.

Anda bisa mencoba menjalankan aplikasi. Silakan melengkapi masukkan yang diminta. Setelah selesai, hentikan atau keluar dari aplikasi Anda bisa dengan mengklik tombol Stop pada IDE Android Studio Anda, kemudian jalankan ulang aplikasi. Maka nilai terakhir yang tersimpan akan ditampilkan pada aplikasi saat mulai dijalankan.

Transaksi Activity
Aplikasi Menampilkan Data Tersimpan

Di bawah ini kode sumber akhir dari main.dart.

import 'package:flutter/material.dart';
import 'people.dart';
import 'second.dart';
import 'shared.dart';

void main(){
  runApp(MainApp());
}

class MainApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MainActivity(),
    );
  }
}

class MainActivity extends StatefulWidget {
  @override
  MainPage createState() {
    return MainPage();
  }
}

class MainPage extends State<MainActivity>{
  PeopleItem _people;

  /* initState: Inisialisasi awal */
  @override
  void initState() {
    _people = new PeopleItem(name: '', age: 0);
    super.initState();
  }

  /* setPeople: Memanggil SecondActivity sembari
     mengirim informasi pada variabel _people
  */
  void setPeople(){
    /* Memanggil SecondActivity */
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => SecondActivity(people: _people),
      ),
    ).then((value){
      /* Ketika menerima data dari Navigator */
      if (value != null){
        /* Mengubah dan menyimpan variabel _people */
        setState(() {
          _people = value;

          /* Menyimpan _people pada SharedPreferences */
          setPeopleSetting(_people);
        });
      }
    });
  }

  var _load = Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: <Widget>[
      CircularProgressIndicator(),
      SizedBox(height: 10,),
      Text("Menyiapkan Data")
    ],
  );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("My First App"),
      ),
      body: Center(
        child: FutureBuilder(
          /* Memanggil metode getPeopleSetting */
          future: getPeopleSetting(),
          /* Prosesi builder saat metode getPeopleSetting dieksekusi */
          builder: (BuildContext context, AsyncSnapshot snapshot){
            /* Saat snapshot mendapatkan data */
            if (snapshot.hasData) {
              /* Saat data pada snapshot tidak null */
              if (snapshot.data != null) {

                /* Mengubah _people dengan data dari snapshot */
                _people = snapshot.data;

                /* Mengembalikan nilai widget untuk anak dari Center */
                return Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    Padding(
                      padding: EdgeInsets.symmetric(
                          horizontal: 16.0, vertical: 4.0),
                      child: Row(
                        children: <Widget>[
                          Text("Name"),
                          SizedBox(width: 50.0,),
                          Text(_people.name,
                            style: TextStyle(
                                fontWeight: FontWeight.bold
                            ),
                          ),
                        ],
                      ),
                    ),
                    Padding(
                      padding: EdgeInsets.symmetric(
                          horizontal: 16.0, vertical: 4.0),
                      child: Row(
                        children: <Widget>[
                          Text("Age"),
                          SizedBox(width: 50.0,),
                          Text(_people.age.toString(),
                            style: TextStyle(
                                fontWeight: FontWeight.bold
                            ),
                          ),
                        ],
                      ),
                    ),
                    RaisedButton(
                      color: Colors.blue,
                      onPressed: setPeople,
                      child: Text("Edit",
                        style: TextStyle(
                          color: Colors.white,
                          fontSize: 14.0,
                        ),
                      ),
                    ),
                  ],
                );
              } else {
                return _load;
              }
            } else {
              return _load;
            }
          }
        ),
      ),
    );
  }
}

Itu saja untuk pembahasan kali ini, pembahasan berikutnya kita akan mencoba menggunakan database lokal pada aplikasi dengan Flutter.