Pendahuluan
Perpindahan media baca dari media cetak ke media digital telah menjadi 'budaya' yang diminati sejak akses teknologi informasi semakin mudah & cepat didapatkan. Al-Qur'an juga telah mengalami peralihan dengan banyaknya umat muslim menggunakan perangkat telepon genggam mereka sebagai media untuk membaca Al-Qur'an.
Berangkat dari kebiasaan tersebut maka artikel kali ini akan mengangkat case membuat aplikasi Qur'an Digital menggunakan Flutter. Adapun sumber data yang akan digunakan adalah data dari Kemenag yang pastinya sudah kredibel dijadikan sumber rujukan untuk mengambil data digital salinan Al-Qur'an.
Tidak hanya untuk membaca Al-Qur'an, tapi aplikasi yang akan kita buat juga dilengkapi dengan fitur untuk memainkan MP3 (Audio) sehingga pengguna dapat mendengarkan bacaan Al-Qur'an langsung dari aplikasi ini.
Baca Juga: Aplikasi Tracking Resi Ekspedisi Flutter
Install Flutter & Package
Sebelum memulai proses development, install Flutter terlebih dahulu dengan command:
flutter create dw-quran
Buka Flutter yang baru saja di-install, kemudian buka file pubspec.yaml
dan modifikasi bagian berikut:
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2
http: ^0.12.0+2
provider: ^3.1.0
audioplayers: ^0.13.2
Penjelasan: 3 package baru tersebut diantaranya: http digunakan untuk request data dari internet, provider untuk state management dan audioplayers untuk memainkan audio dari mp3. Pastikan posisinya sejajar dengan flutter karena posisi sangat berpengaruh.
Ketika fungsi save dijalankan, biasanya secara otomatis akan mengunduh package tersebut. Akan tetapi kita juga bisa melakukannya secara manual menggunakan command:
flutter pub get
Menampilkan List Surah Al-Qur'an
Ada dua screen yang akan digunakan, pertama adalah screen untuk menampilkan list dari seluruh surah yang ada di Al-Qur'an dan screen kedua untuk menampilkan detail atau isi dari surah yang dipilih. Bagian pertama yang akan diselesaikan terlebih dahulu adalah screen untuk menampilkan list surah karena detail surah membutuhkan id surah yang dipilih.
Buka file main.dart
dan modifikasi menjadi:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import './screen/quran_list.dart';
import './models/quran_model.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider.value(
value: QuranData(), //LOAD PROVIDER QURANDATA UNTUK STATE MANAGEMENT LIST SURAH
),
//PROVIDER LAINNYA AKAN DITEMPAT DISINI, DIPISAHKAN DENGAN KOMA
],
child: MaterialApp(
title: 'DaengwebID',
theme: ThemeData(
primarySwatch: Colors.pink,
),
home: QuranList(), //DEFAULTNYA AKAN ME-LOAD SCREEN QURANLIST
routes: {
//ROUTING UNTUK DETAIL SURAH
},
),
);
}
}
Perhatikan bagian import statement, ada 2 file yang di-import. File tersebut adalah quran_list.dart
dan quran_model.dart
. Mari kita selesaikan satu persatu, buat file quran_list.dart
di dalam folder lib/screen
dan tambahkan code berikut:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../models/quran_model.dart';
import '../widget/quran_surah.dart';
class QuranList extends StatefulWidget {
@override
_QuranListState createState() => _QuranListState();
}
class _QuranListState extends State<QuranList> {
ScrollController controller; //VARIABLE controller INI BER-TIPE SCROLLCONTROLLER YANG AKAN DIGUNAKAN UNTUK MENDETEKSI EVENT SCROLL PADA LAYAR
//DEFINISIKAN VARIABLE LAINNYA YANG AKAN DIGUNAKAN UNTUK MENAMPILKAN DATA / PROGRESS LOADING
bool loadMore = false;
bool firstLoad = true;
// HOOKS KETIKA CLASS INI DI-RENDER MAKA AKAN MENJALAN FUNGSI YANG DI-APITNYA
@override
void initState() {
//UNTUK MENJALANKAN FUNGSI PROVIDER DIDALAM INITSTATE, MAKA KITA MEMBUTUHKAN PENUNDAAN MENGGUNAKAN FUTURE.DELAYED, DAN DURASINYA KITA SET KE 0
Future.delayed(Duration.zero).then((_) {
//LOAD PROVIDER YANG DITAUTKAN DENGAN QURANDATA (BERASAL DARI QURAN_MODEL.DART)
// KEMUDIAN DIAKHIR KITA JALANKAN FUNGSI getData() YANG NANTINYA AKAN KITA BUAT
Provider.of<QuranData>(context, listen: false).getData().then((_) {
//KEMUDIAN SET firstLoad MENJADI FALSE
setState(() {
firstLoad = false;
});
});
});
super.initState();
controller = ScrollController()..addListener(_scrollListener); //TAMBAHKAN LISTENER SCROLLING
}
//KETIKA CLASS INI DITINGGALKAN
@override
void dispose() {
//MAKA HAPUS LISTENER SCROLLING
controller.removeListener(_scrollListener);
super.dispose();
}
//ADD LISTENER DAN REMOVE LISTENER MASING-MASING MEMANGGIL METHOD _scrollListener
//KITA DEFINISIKAN METHOD TERSEBUT
void _scrollListener() {
//CEK JIKA POSISI = MAX SCROLL (MENTOK PALING BAWAH)
if (controller.position.pixels == controller.position.maxScrollExtent) {
//MAKA SET loadMore JADI TRUE
setState(() {
loadMore = true;
});
//DAN JALANKAN FUNGSI getData() DARI QURAN_MODEL.DART
Provider.of<QuranData>(context, listen: false).getData().then((_) {
//JIKA BERHASIL, MAKA SET loadMore JADI FALSE
setState(() {
loadMore = false;
});
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
//BUAT APPBAR UNTUK HEAD APPS
appBar: AppBar(
leading: const Icon(Icons.book),
title: const Text("DaengWeb - Al-Qur'an"),
),
//FLOATING BUTTON KITA GUNAKAN UNTUK MENAMPILKAN PROGRESS LOADING
floatingActionButton: loadMore ? CircularProgressIndicator() : null,
body: Container(
padding: const EdgeInsets.all(5),
margin: const EdgeInsets.all(5),
//KETIKA PERTAMA KALI DILOAD MAKA KITA GUNAKAN LOADING DITENGAH LAYAR
//NAMUN LOAD DATA SELANJUTNYA KITA GUNAKAN LOAD DI POJOK KANAN LAYAR
//AGAR TIDAK MENGGANGGU PENGGUNA
child: firstLoad
? Center(
child: CircularProgressIndicator(),
)
//DETEKSI PERUBAHAN DATA MENGGUNAKAN CONSUMER
//SEHINGGA WIDGET YANG DI-RENDER HANYA WIDGET YANG DIAPIT-NYA
: Consumer<QuranData>(
//SETIAP DATANYA KITA BUILD MENGGUNAKAN LISTVIEW AGAR BISA DI SCROLL
builder: (ctx, data, _) => ListView.builder(
controller: controller, //SET CONTROLLER YANG DIBUAT DIAWAL
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: data.items.length, //HITUNG JUMLAH DATA, MAKA LISTVIEW AKAN ME-RNDER SEBANYAK JUMLAH DATA
//RENDER DATANYA KE DALAM CUSTOM WIDGET BERNAMA QuranSurah() YANG SELANJUTNYA AKAN KITA BUAT
itemBuilder: (ctx, i) => QuranSurah(
//KIRIM DATA YANG DIBUTUHKAN
data.items[i].id,
data.items[i].name,
data.items[i].arab,
data.items[i].translate,
data.items[i].countAyat,
),
),
),
),
);
}
}
Penjelasan: setState()
digunakan untuk memberitahu bahwa terjadi perubahan data sehingga perlu dilakukan rebuild. Apabila kamu tidak ingin melakukan rebuild widget, maka cukup assign value variable secara langsung tanpa harus diapit oleh setState.
Terdapat kesamaan file yang di-import antara main.dart
dan quran_list.dart
yakni sama sama meng-import file quran_model.dart
, jadi kita akan buat quran_model.dart
di dalam folder lib/models
dan tambahkan code:
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
//CLASS INI AKAN MENG-HANDLE FORMAT DATA YANG DIINGINKAN
class Quran {
//DEFINISIKAN VARIABLENYA BESERTA TIPE DATANYA
final int id;
final String name;
final String arab;
final String translate;
final int countAyat;
//BUAT CONSTRUCTOR DIMANA KETIKA CLASS INI DI-LOAD MAKA WAJIB MENGIRIMKAN DATA YANG DIMINTA
Quran({
@required this.id,
@required this.name,
@required this.arab,
@required this.translate,
@required this.countAyat
});
}
//CLASS INI DIGUNAKAN UNTUK STATE MANAGEMENT PROVIDER
class QuranData with ChangeNotifier {
//DEFINISIKAN VARIABLE _data DENGAN TIPE LIST DAN FORMAT QURAN DIMANA NILAI DEFAULT ADA EMPTY ARRAY
List<Quran> _data = [];
int offset = 0; //DEFINISIKAN VARIABLE offset DENGAN TIPE int DAN VALUE AWAL ADALAH 0
//KARENA _data ADALAH PRIVATE PROPERTY DITANDAI DENGAN AWAL _
//MAKA KITA BUAT GETTER AGAR _data BISA DIAKSES DARI LUAR CLASS
//DAN GETTERNYA DIBERI NAMA items
List<Quran> get items {
return [..._data];
}
//FUNGSI getData() DIGUNAKAN UNTUK REQUEST LIST DATA SURAH YANG ADA
Future<void> getData() async {
try {
//REQUEST KE SERVER HANYA DIJALANKAN JIKA OFFSET SAMA DENGAN TOTAL DATA YANG ADA
if (offset == _data.length) {
//URL TEMPAT KITA MENGAMBIL DATA SURAH, DIMANA OFFSET NILAINYA TERGANTUNG BERAPA BANYAK DATA YANG SUDAH DI-LOAD
final url = 'https://quran.kemenag.go.id/index.php/api/v1/surat/${offset}/10';
//MINTA DATA KE API
final response = await http.get(url);
//CONVER DATA NYA MENJADI LIST
final extractData = json.decode(response.body)['data'] as List;
//JIKA DATANYA KOSONG
if (extractData == null || extractData.length == 0) {
return; //MAKA HENTIKAN FUNGSI
}
final List<Quran> quranData = []; //BUAT VARIABLE SEMENTARA UNTUK MENAMPUNG DATA
//LOOPING DATA YANG SUDAH DI CONVERT
extractData.forEach((value) {
//TAMBAHKAN DATA TERSEBUT KE DALAM VARIABLE SEMENTARANYA
//DENGAN FORMAT SESUAI DENGAN CLASS Quran()
quranData.add(Quran(
id: value['id'],
name: value['surat_name'],
arab: value['surat_text'],
translate: value['surat_terjemahan'],
countAyat: value['count_ayat']
));
});
offset += quranData.length; //UBAH VALUE OFFSET DENGAN MENAMBAHKAN DATA BARU
_data.addAll(quranData); //INSERT DATA BARU KE DALAM VARIABLE _data
notifyListeners(); //INFORMASIKAN JIKA TERJADI PERUBAHAN
}
} catch(error) {
throw error;
}
}
}
Model yang bertugas sebagai state management sudah tersedia, 1 file lagi yang luput adalah file quran_surah.dart
dimana file ini import oleh class QuranList()
. Buat file baru bernama quran_surah.dart
di dalam folder lib/widget
dan tambahkan code:
import 'package:flutter/material.dart';
class QuranSurah extends StatelessWidget {
//DEFINISIKAN VARIABLE APA SAJA YANG DIBUTUHKAN UNTUK INFORMASI SURAH
final int id;
final String name;
final String arab;
final String translate;
final int countAyat;
//BUAT CONSTRUCTOR UNTUK MEMINTA DATA KETIKA CLASS INI DI-LOAD
QuranSurah(this.id, this.name, this.arab, this.translate, this.countAyat);
//BUAT METHOD UNTUK REDIRECT KE SCREEN BARU
//FUNGSI INI NANTINYA DIGUNAKAN UNTUK BERPINDAH KE DETAIL MASING-MASING SURAH
//DIMANA KETIKA SURAH TERSEBUT DITAP MAKA AKAN PINDAH KE SCREEN DETAIL
void _viewDetail(BuildContext context) {
Navigator.of(context).pushNamed('/detail', arguments: id); //MENGGUNAKAN NAVIGATOR DAN MENUJU ROUTES /detail DIMANA ROUTE INI BELUM DIDEFINISIKAN
//ROUTE DIATAS AKAN DIDEFINISIKAN KEMUDIAN. SELAIN ITU KITA MENGIRIMKAN ID SURAH SEBAGAI ARGUMENT YANG NANTINYA DIGUNAKAN OLEH SCREEN YANG BARU UNTUK MENGAMBIL DATA SURAH TERKAIT
}
@override
Widget build(BuildContext context) {
return Card(
elevation: 8,
margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
child: ListTile(
//KETIKA LISTTILE DITAP, MAKA AKAN MENJALANKAN METHOD DIATAS
onTap: () => _viewDetail(context),
contentPadding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
//MULAI DARI SINI HINGGA CODE DIBAWAH HANYA MENAMPILKAN INFORMASI SURAH
//LEADING POSISINYA SEBELAH KIRI, DIMANA KITA GUNAKAN UNTUK MENAMPILKAN NOMOR URUT SURAH
leading: Container(
padding: EdgeInsets.only(right: 12),
decoration: BoxDecoration(
border: Border(
right: BorderSide(width: 1, color: Colors.black),
),
),
child: CircleAvatar(
child: Text('$id'),
),
),
//TITLE POSISINYA DITENGAH, KITA GUNAKAN UNTUK MENAMPILKAN NAMA SURAH (LENGKAP DENGAN BAHASA ARABNYA)
title: Text(
'$name ($arab)',
style: TextStyle(fontWeight: FontWeight.bold),
),
//DAN SUBTITLE POSISINYA DIBAWAH TITLE UNTUK MENAMPILKAN INFORMASI TAMBAHAN
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
//PERTAMA ADALAH TERJEMAHAN DARI NAMA SURAH
Row(
children: <Widget>[
Icon(Icons.local_florist, color: Colors.pinkAccent,),
Expanded(child: Text('Terjemahan: $translate')),
],
),
//JUMLAH AYAT DARI SURAH TERSEBUT
Row(
children: <Widget>[
Icon(Icons.local_florist, color: Colors.pinkAccent,),
Text('Jumlah Ayat: $countAyat'),
],
),
],
),
//POSISINYA PALING KANAN, KITA GUNAKAN UNTUK MENAMPILKAN ICON SAJA
trailing: Icon(Icons.keyboard_arrow_right, size: 30,),
),
);
}
}
Sampai pada tahap ini kita sudah berhasil menampilkan list surah dimana fitur yang dimiliki adalah progress loading ketika data sedang di-load dari internet, list view untuk menampilkan data surah, membuat custom widget dimana widget ini re-usable untuk menampilkan informasi surah, menerapkan pagination dengan menampilkan 10 surah per sekali load dan untuk mendeteksi pagination tersebut kita gunakan lazy load atau dengan kata lain mendeteksi scroll screen.
Quran Detail & Play Audio
Masih ingat dengan penjelasan sebelumnya? Jadi ketika ListTile di-tap, maka akan diarahkan ke screen yang akan kita buat pada pembahasan kali ini. Kembali ke main.dart
, buka file tersebut dan tambahkan code berikut pada bagian import statement:
import './models/quran_ayat_model.dart';
import './screen/quran_detail.dart';
Masih dengan file yang sama, pada bagian array providers
dari MultiProvider()
, tambahkan code berikut untuk meng-handle model QuranAyat()
ChangeNotifierProvider.value(
value: QuranAyat(),
)
Perubahan terakhir dari main.dart
adalah pada bagian routes
, modifikasi menjadi:
routes: {
'/detail': (ctx) => QuranDetail(),
},
Kita tinggalkan main.dart
, maka tugas selanjutnya adalah membuat class baru bernama quran_detail.dart
, tempatkan file tersebut di dalam folder lib/screen
dan tambahkan code:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:audioplayers/audioplayers.dart';
import '../models/quran_model.dart'; //IMPORT QURAN MODEL UNTUK MENGAMBIL DATA SURAH
import '../models/quran_ayat_model.dart'; //IMPORT MODEL BARU YANG NANTINYA AKAN KITA BUAT
import '../widget/quran_read.dart'; //CUSTOM WIDGET UNTUK MENAMPILKAN ISI SURAH
class QuranDetail extends StatefulWidget {
@override
_QuranDetailState createState() => _QuranDetailState();
}
class _QuranDetailState extends State<QuranDetail> {
//VARIABLE INI UNTUK MENG-HANDLE BOTTOM NAVIGATION (PREVIOUS = 0, PLAY = 1 DAN NEXT = 2)
//VALUE DEFAULTNYA KITA SET 2 (NEXT)
int bottomIndex = 2;
int offset = 0; //OFFSET UNTUK MENANDAI BERAPA DATA YANG SUDAH DI-LOAD
int totalData = 0; //UNTUK MENYIMPAN INFORMASI ID DATA TERAKHIR YANG DI-LOAD
int id; //ID SURAH
bool isPlay = false; //VARIABLE UNTUK PLAY/STOP AUDIO
AudioPlayer audioPlayer = AudioPlayer(); //VARIABLE YANG AKAN MENG-HANDLE AUDIO
//METHOD UNTUK MENJALANKAN AUDIO DARI SURAH YANG DIPILIH
void play() async {
//JIKA isPlay = false
if (!isPlay) {
//MAKA KITA AMBIL URL DARI SURAH TERKAIT MENGGUNAKAN METHOD findMp3Url DENGAN MENGIRIMKAN ID SURAH
final mp3URL = Provider.of<QuranAyat>(context, listen: false).findMp3Url(id);
int result = await audioPlayer.play(mp3URL); //PLAY AUDIONYA
if (result == 1) {
//JIKA BERHASIL DIPLAY
setState(() {
isPlay = true; //MAKA SET VARIABLE NYA JADI TRUE
});
}
} else {
//JIKA ISPLAY = TRUE MAKA KITA JALANKAN FUNGSI STOP AUDIO
int result = await audioPlayer.stop();
if (result == 1) {
setState(() {
isPlay = false; //DAN SET isPlay JADI FALSE
});
}
}
}
//KETIKA BOTTOM NAVIGATION DI-TAP MAKA AKAN MENJALANKAN FUNGSI INI
void _changeBottomIndex(index) {
//DI-CEK INDEX YANG DI-TAP, JIKA VALUE-NYA 1 (TOMBOL PLAY)
if (index == 1) {
play(); //MAKA KITA JALANKAN METHOD play()
}
//AMBIL DATA TERBARU YANG ADA DI CLASS QuranAyat DENGAN PROPERTY items
final loadData = Provider.of<QuranAyat>(context, listen: false).items;
//KEMUDIAN KITA AMBIL NOMOR SURAH DARI DATA YANG TERAKHIR
totalData = loadData.length > 0 ? loadData[loadData.length - 1].ayatNumber:0;
//JIKA INDEX 0 (PREVIOUS)
if (index == 0) {
//OFFSET KITA KURANGI KARENA AKAN KEMBALI KE DATA SEBELUMNYA
offset -= totalData == offset ? (loadData.length * 2):loadData.length;
} else if (index == 2) {
//JIKA INDEX 2 (NEXT), MAKA OFFSET KITA TAMBAH KARENA AKAN KE DATA BERIKUTNYA
offset += totalData == offset ? (loadData.length * 2):loadData.length;
}
setState(() {
bottomIndex = index; //SET STATE UNTUK MEMBERITAHU BAHWA ADA PERUBAHAN MAKA WIDGET AKAN DIRENDER KEMBALI
});
}
@override
Widget build(BuildContext context) {
id = ModalRoute.of(context).settings.arguments as int; //DAPATKAN ARGUMEN YANG DIKIRIMKAN DARI PAGE SEBELUMNYA
//KEMUDIAN CARI DATA BERDASARKAN ID SURAH YANG DITERIMA
final data = Provider.of<QuranData>(context, listen: false).findById(id);
return Scaffold(
appBar: AppBar(
title: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text("DaengWeb Al-Qur'an"),
Text(
'${data.name} - ${data.arab}',
style: TextStyle(fontSize: 14, color: Colors.white70),
),
],
),
),
body: Container(
padding: const EdgeInsets.all(5),
margin: const EdgeInsets.all(5),
//PERHATIKAN BAGIAN SEBELUMNYA, KITA TIDAK MENGGUNAKAN FUNGSI getDetail()
//KARENA FUTURE BUILDER SECARA OTOMATIS AKAN BERJALAN KETIKA SCREEN DI-LOAD
//DAN KETIKA USER TAP NEXT / PREVIOUS, KITA MENGGUNAKAN SET STATE YANG BERARTI WIDGET DI RE-RENDER, MAKA FUTURE BUILDER AKAN DIJALANKAN KEMBALI
child: FutureBuilder(
future:
Provider.of<QuranAyat>(context, listen: false).getDetail(id, bottomIndex, offset, totalData), //SECARA OTOMATIS FUNGSI INI AKAN DIJALANKAN SETIAP KALI WIDGET BERUBAH UNTUK MENGAMBIL DATA BERDASARKAN ID SURAH, INDEX BOTTOM NAVIGATION DAN OFFSET YANG DI-REQUEST
builder: (context, snapshot) {
//KETIKA PROSES REQUEST BERLANGSUNG
if (snapshot.connectionState == ConnectionState.waiting) {
//MAKA PROGRESS LOADING DIJALANKAN
return Center(
child: CircularProgressIndicator(),
);
} else {
//JIKA TERJADI ERROR
if (snapshot.hasError) {
//MAKA INFORMASI ERROR DI RENDER
return Center(
child: Text("Error! Periksa Koneksi Anda"),
);
} else {
//SELAIN ITU MAKA KITA RENDER ISI SURAH TERKAIT
return Consumer<QuranAyat>(
//MENGGUNAKAN CONSUMER UNTUK MENGAMBIL DATA STATE DAN LIST VIEW UNTUK RENDER CONTENT-NYA
builder: (ctx, data, _) => ListView.builder(
shrinkWrap: true,
itemCount: data.items.length, //LIST VIEW AKAN DIJALANKAN BERDASARKAN JUMLAH DATA
//LAGI LAGI KITA GUNAKAN CUSTOM WIDGET AGAR CODINGAN DIFILE INI TIDAK PANJANG, CLASS QuranRead() AKAN DIBUAT SELANJUTNYA.
itemBuilder: (ctx, i) => QuranRead(
data.items[i].ayatNumber,
data.items[i].ayatArab,
data.items[i].ayatText,
),
),
);
}
}
},
),
),
//BOTTOM NAVIGATION NYA KITA BUAT DISINI
bottomNavigationBar: BottomNavigationBar(
//DIMANA INDEXNYA SESUAI DENGAN BOTTOMINDEX
currentIndex: bottomIndex,
//DAN ITEMSNYA ADALAH 3, PREVIOUS, PLAY DAN NEXT
items: [
BottomNavigationBarItem(
icon: Icon(Icons.arrow_left), title: Text('Previous')),
BottomNavigationBarItem(
icon: Icon(isPlay ? Icons.stop:Icons.play_arrow), title: Text('${!isPlay ? "Play":"Stop"}')),
BottomNavigationBarItem(
icon: Icon(Icons.arrow_right), title: Text('Next')),
],
//KETIAK DI-TAP MAKA AKAN MENJALANKAN FUNGSI _changeBottomIndex
onTap: _changeBottomIndex,
));
}
}
Sebelum membuat file selanjutnya, lengkapi terlebih dahulu fungsi dari file yang ada. Jadi dari codingan diatas ada fungsi untuk menjalankan method findById()
dimana method ini bertujuan untuk mengambil informasi surah berdasarkan ID-nya. Buka file quran_model.dart
dan tambahkan method berikut di dalam class QuranData()
Quran findById(int id) {
return _data.firstWhere((item) => item.id == id); //GET DATA SURAH BERDASARKAN ID
}
Dua langkah terakhir adalah membuat file quran_read.dart
dan quran_ayat_model.dart
. Pertama kita buat file quran_ayat_model.dart
dan letakkan di dalam folder lib/widget
, kemudian tambahkan code:
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
//CLASS INI SAMA DENGAN CLASS SEBELUMNYA, UNTUK MENG-HANDLE FORMAT DATA YANG DIINGINKAN
class QuranAyatModel {
final int ayatId;
final int ayatNumber;
final int surahId;
final int juzId;
final String ayatArab;
final String ayatText;
QuranAyatModel({
@required this.ayatId,
@required this.ayatNumber,
@required this.surahId,
@required this.juzId,
@required this.ayatArab,
@required this.ayatText,
});
}
//CLASS INI UNTUK MENG-HANDLE STATE MANAGEMENT
class QuranAyat with ChangeNotifier {
List<QuranAyatModel> _data = []; //DATA CONTENT SURAH KITA BUAT BERTIPE LIST DENGAN FORMAT SESUAI DENGAN CLASS QuranAyatModel
//KARENA URL MP3 TIDAK ADA DALAM API, MAKA KITA BUAT MANUAL. DIMANA URUTANNYA KITA SESUAIKAN DENGAN URUTAN SURAH
//DATA INI MASIH BELUM LENGKAP, SILAHKAN DILENGKAPI
List _mp3 = [
'http://ia802609.us.archive.org/13/items/quraninindonesia/001AlFaatihah.mp3',
'http://ia802609.us.archive.org/13/items/quraninindonesia/002AlBaqarah.mp3',
'http://ia802609.us.archive.org/13/items/quraninindonesia/003AliImran.mp3',
'http://ia802609.us.archive.org/13/items/quraninindonesia/004AnNisaa.mp3',
'http://ia802609.us.archive.org/13/items/quraninindonesia/005AlMaaidah.mp3',
'http://ia802609.us.archive.org/13/items/quraninindonesia/006AlAnaam.mp3',
'http://ia802609.us.archive.org/13/items/quraninindonesia/007AlAaraaf.mp3',
'http://ia802609.us.archive.org/13/items/quraninindonesia/008AlAnfaal.mp3',
'http://ia802609.us.archive.org/13/items/quraninindonesia/009AtTaubah.mp3',
'http://ia802609.us.archive.org/13/items/quraninindonesia/010Yunus.mp3',
'http://ia802609.us.archive.org/13/items/quraninindonesia/011Huud.mp3',
'http://ia802609.us.archive.org/13/items/quraninindonesia/012Yusuf.mp3',
'http://ia802609.us.archive.org/13/items/quraninindonesia/013ArRaad.mp3',
'http://ia802609.us.archive.org/13/items/quraninindonesia/014Ibrahim.mp3',
'http://ia802609.us.archive.org/13/items/quraninindonesia/015AlHijr.mp3',
'http://ia802609.us.archive.org/13/items/quraninindonesia/016AnNahl.mp3',
'http://ia802609.us.archive.org/13/items/quraninindonesia/017AlIsraa.mp3',
'http://ia802609.us.archive.org/13/items/quraninindonesia/018AlKahfi.mp3',
'http://ia802609.us.archive.org/13/items/quraninindonesia/019Maryam.mp3',
'http://ia802609.us.archive.org/13/items/quraninindonesia/020Thaahaa2.mp3'
];
//GETTER AGAR VALUE _data BISA DIAKSES DARI LUAR CLASS
List<QuranAyatModel> get items {
return [..._data];
}
//MENGAMBIL URL MP3 BERDASARKAN ID SURAH
String findMp3Url(id) {
return _mp3[id - 1]; //KARENA ID SURAH DIMULAI DARI 1 SEDANGKAN ARRAY DIMULAI DARI 0, MAKA KITA KURANGI 1 SETIAP ID SURAHNYA
}
//METHOD UNTUK MENGAMBIL ISI SURAH BERDASARKAN ID SURAH
Future<void> getDetail(int id, int navigationBarIndex, int offset, int total) async {
//PROSES REQUEST HANYA DIJALANKAN JIKA MEMENUHI KONDISI YANG ADA DI DALAM IF
//HAL INI DILAKUKAN KARENA SETSTATE JUGA BERARTI AKAN MENJALANKAN FUNGSI INI KETIKA TOMBOL PLAY DITAP
//AKAN TETAP KITA TIDAK INGIN MENJALANKAN FUNGSI INI JIKA BUKAN TOMBOL NEXT/PREVIOUS
if ((navigationBarIndex == 2 && total == offset) || (navigationBarIndex == 0 && total > offset)) {
//GET DATA BERDASARKAN ID DAN OFFSET, DIMANA PER SEKALI LOAD KITA AMBIL 10 DATA
final url = 'https://quran.kemenag.go.id/index.php/api/v1/ayatweb/$id/0/$offset/10';
final response = await http.get(url);
final extractData = json.decode(response.body)['data'] as List; //FORMAT DATA BERBENTUK LIST
if (extractData == null) {
return;
}
final List<QuranAyatModel> ayatData = [];
//LOOPING DATA
extractData.forEach((value) {
//DAN MASUKKAN DATANYA SESUAI FORMAT QuranAyatModel
ayatData.add(QuranAyatModel(
ayatId: value['aya_id'],
ayatNumber: value['aya_number'],
surahId: value['sura_id'],
juzId: value['juz_id'],
ayatArab: value['aya_text'],
ayatText: value['translation_aya_text']
));
});
_data = ayatData; //TAMBAHKAN DATANYA KE DALAM _data
notifyListeners();
}
}
}
Bagian terakhir adalah membuat file quran_read.dart
dan tempatkan di dalam folder lib/widget
, kemudian tambahkan code:
import 'package:flutter/material.dart';
class QuranRead extends StatelessWidget {
//DEFINISIKAN VARIABLE DARI INFORMAIS YANG DIBUTUHKAN
final int ayatNumber;
final String ayatArab;
final String ayatText;
//BUAT CONSTRUCTOR UNTUK MEMINTA DATA
QuranRead(this.ayatNumber, this.ayatArab, this.ayatText);
@override
Widget build(BuildContext context) {
return Card(
elevation: 8,
child: ListTile(
contentPadding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
leading: CircleAvatar(child: Text('$ayatNumber'),), //POSISI KIRI KITA TAMPILKAN NOMOR AYAT
//DITENGAH KITA TAMPILKAN TEXT ARABNYA
title: Text(
'$ayatArab',
textAlign: TextAlign.right,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
//DIBAWAH TITLE TAMPILKAN TERJEMAHANNYA
subtitle: Text('$ayatText'),
),
);
}
}
Kesimpulan
Studi kasus sederhana ini telah membantu kita dalam belajar Flutter, dimana sepanjang artikel ini kita telah belajar bagimana mengambil data dari internet, menampilkan list ayat dan surah menggunakan ListView, membuat pagination, memainkan audio, membuat progress loading, membuat bottom navigation dan berpindah screen menggunakan navigator.
Adapun dokumentasi code dari artikel ini bisa kamu lihat di Github.
Comments