Anda di halaman 1dari 257

Belajar Membuat Aplikasi Flutter untuk Pemula

Daftar Referensi
[1] wearesocial. “Digital 2020 - We Are Social” wearesocial.com
https://wearesocial.com/digital-2020 (diakses pada 23 April 2021)

[2] Flutter. “Flutter: the first UI platform designed for ambient computing”
developers.googleblog.com
https://developers.googleblog.com/2019/12/flutter-ui-ambient-computing.ht
ml (diakses pada 23 April 2021)

[3] Flutter. “Announcing Flutter 2” developers.googleblog.com


https://developers.googleblog.com/2021/03/announcing-flutter-2.html
(diakses pada 23 April 2021)

[4] Dart. “Language Tour” dart.dev


https://dart.dev/guides/language/language-tour#important-concepts
(diakses pada 23 April 2021)
Pengenalan Flutter

Framework Flutter
Teknologi sudah menjadi bagian tak terpisahkan dalam hidup kita terutama
smartphone. Menurut data dari We Are Social, pada bulan Januari 2020
pengguna internet di Indonesia mencapai sekitar 175,4 juta orang.
Terdapat peningkatan sebesar 17% atau sekitar 25 juta pengguna dari
tahun 2019. Jika dilihat dari total populasi penduduk Indonesia yang
berkisar 272,1 juta jiwa, maka 64% di antaranya sudah mengakses
internet. Dengan masifnya angka pengguna smartphone ini, kian spesifik
pula requirements alias permintaan bagi seorang pengembang aplikasi.
Termasuk requirement pada perangkat mobile dengan target platform
Android dan iOS. Masing-masing platform memiliki aplikasi native-nya
tersendiri.

Mengembangkan dua platform memiliki banyak implikasi. Ditinjau dari sisi


bisnis, biaya yang perlu dikeluarkan tentunya tidak murah. Dua aplikasi
berarti ada dua tipe developer yang perlu direkrut dan dua jenis
pengembangan untuk dikelola. Jika ada teknologi yang memungkinkan
pengembangan aplikasi di beberapa platform sekaligus hanya dengan satu
codebase tentunya lebih hemat bukan? Pasti lebih efisien dari sisi biaya,
waktu, dan tenaga.

Tertarik untuk mempelajari stack teknologi yang bisa mewujudkannya?


Mari kita berkenalan dengan Flutter. Inilah framework populer dan powerful
buatan Google, Flutter hadir untuk mengembangkan aplikasi multi-platform
yang dikompilasi secara native.

Apa itu Flutter


Flutter adalah SDK (Software Development Kit) yang dikembangkan oleh
Google untuk membuat aplikasi yang bagus dan bisa berjalan pada
berbagai platform. Flutter 2 yang merupakan versi terbaru memberikan
dukungan pada Anda untuk membangun aplikasi pada sistem operasi
Android, iOS, Web, Windows, Linux, dan MacOS. Dengan ini, Anda cukup
sekali coding atau dikenal dengan single codebase. Flutter juga sudah
digunakan oleh banyak developer maupun organisasi di seluruh dunia,
selain itu Flutter bersifat open source.
Mengapa Flutter?
Flutter berbeda dari kebanyakan SDK Cross-platform lainnya untuk
membuat aplikasi mobile. Untuk menarik widget, Flutter bukan
menggunakan WebView maupun widget OEM, melainkan mesin rendering
berkinerja tinggi. Flutter dapat digunakan bersamaan dengan aplikasi
native yang sudah ada atau digunakan secara keseluruhan untuk aplikasi
baru.

Ada beberapa kelebihan dari Flutter, antara lain:

1. Flutter memungkinkan kita untuk membuat aplikasi yang indah


(beautiful)
Desainer dapat dengan bebas berkreasi tanpa adanya banyak
batasan dari framework. Flutter juga dapat mengontrol setiap piksel
yang ada di layar, sehingga memudahkan dalam membuat animasi.
Flutter menyediakan banyak komponen material design yang dapat
berjalan baik pada Android, iOS, dan web.

2. Flutter berjalan dengan sangat cepat (fast)


Flutter menggunakan graphic engine bernama Skia-2D yang juga
digunakan pada Chrome dan Android. Kode Flutter menggunakan
bahasa Dart, yang memungkinkan untuk dikompilasi ke kode native
32-bit dan 64-bit ARM untuk iOS dan Android.

3. Flutter sangat produktif (productive)


Flutter memiliki fitur hot-reload yang memungkinkan Anda untuk
melihat hasil kompilasi secara real-time. Dengan hot-reload, Anda
dapat dengan mudah melihat perubahan kode pada perangkat, tanpa
perlu menunggu restart dan kehilangan state.
4. Flutter bersifat terbuka (open source)
Flutter adalah proyek open source dengan lisensi BSD. Kode yang
ada di Flutter berasal dari kontribusi ratusan developer dari seluruh
dunia. Banyak plugin yang sudah dibuat oleh developer. Anda dapat
ikut berkontribusi mengembangkan Flutter dengan melaporkan
bug/issue atau ikut memperbaiki kode yang sudah ada. Source code
dari Flutter dapat Anda temukan pada tautan
https://github.com/flutter/flutter.

Sejarah Perkembangan Flutter


Versi pertama dari Flutter bernama Sky yang diumumkan pada Dart
Developer Summit 2015. Pada saat itu, Sky hanya berjalan pada sistem
operasi Android.

Lalu, pada Google Developer Days di Shanghai, Google mengumumkan


Flutter versi Release Preview 2 yang merupakan rilis sebelum versi stable
1.0.

Pada 3 Maret 2021 kemarin, Flutter mengumumkan versi Flutter 2 dengan


pembaruan yang cukup besar, yaitu dukungan Flutter untuk platform web
dan desktop (Windows, Linux, dan macOS) yang memasuki versi stable.

Sejak rilis beta pertama, Flutter sudah banyak digunakan untuk


mengembangkan aplikasi mobile. Contohnya adalah perusahaan Abbey
Road Studios, Alibaba, Capital One, Groupon, Hamilton, JD.com, Philips
Hue, Reflectly, Tencent, dan GITS Indonesia.
Dart
Seperti yang telah kita ketahui, aplikasi Flutter ditulis dengan bahasa Dart.
Seharusnya Anda telah mempelajari tentang fundamental Dart di kelas
Memulai Pemrograman dengan Dart. Untuk menyegarkan kembali ingatan
Anda kami akan mengulas sedikit tentang Dart.

Bahasa pemrograman Dart merupakan bahasa pemrograman


general-purpose yang dirancang oleh Lars Bak dan Kasper Lund. Bahasa
pemrograman ini dikembangkan sebagai bahasa pemrograman aplikasi
yang dapat dengan mudah dipelajari dan disebarkan.

Bahasa pemrograman besutan Google ini dapat digunakan untuk


mengembangkan berbagai macam platform termasuk di dalamnya adalah
web, aplikasi mobile, server, dan perangkat yang mengusung teknologi
Internet of Things (IoT).

Bahasa pemrograman tersebut dapat digunakan untuk mengembangkan


aplikasi yang dijalankan pada berbagai macam peramban (browser)
modern. Dart juga dapat digunakan untuk mengembangkan aplikasi dari
codebase tunggal menjadi aplikasi Android maupun iOS.

Bahasa pemrograman Dart dapat digunakan secara bebas oleh para


developer karena bahasa ini dirilis secara open-source oleh Google di
bawah lisensi BSD. Bahasa pemrograman Dart merupakan bahasa
pemrograman berbasis kelas dan berorientasi terhadap objek dengan
menggunakan Syntax bahasa pemrograman C.

Bahasa ini dikenalkan oleh Google sebagai pengganti bahasa


pemrograman JavaScript, akan tetapi secara opsional bahasa ini dapat
dikompilasi ke dalam JavaScript dengan menggunakan Dart-to-JavaScript
compiler. Sedikit berbeda dengan bahasa pemrograman JavaScript yang
bertipe dinamis, bahasa pemrograman Dart merupakan bahasa
pemrograman bertipe statis.

Oke, sampai sini kita telah mempelajari tentang apa itu Flutter, termasuk
sejarah dan kenapa kita perlu belajar mengembangkan aplikasi dengan
Flutter. Di kelas ini Anda akan belajar dasar pengembangan aplikasi
Flutter, seperti package, widget yang umum digunakan, cara berpindah
halaman menggunakan Navigation, hingga proses build menjadi APK yang
bisa diinstal pada smartphone Anda

Catatan: Materi-materi di kelas ini akan disampaikan dalam banyak opsi,


mulai dari sistem operasi (OS), platform Android atau iOS, dan Integrated
Development Environment (IDE). Silakan sesuaikan dengan spesifikasi
hardware/software Anda dan lewatilah materi yang dirasa tidak diperlukan.

Untuk lulus dari kelas ini cukup dengan mengirimkan submission dari satu
platform saja, misal Anda memilih platform Android maka tidak perlu
mengirimkan versi iOS atau webnya.

Mari kita lanjut ke materi selanjutnya, yaitu instalasi Flutter.

Instalasi Flutter
Sebelum mulai mengembangkan aplikasi menggunakan Flutter, tentunya kita perlu
menyiapkan dan menginstal tools apa saja yang dibutuhkan untuk membuat aplikasi Flutter.
Untuk melakukan instalasi Flutter, ada perbedaan cara di setiap sistem operasi. Simak
caranya berikut ini sesuai dengan sistem operasi yang Anda gunakan:

Persyaratan Minimum
Sebelum melakukan instalasi dan menjalankan Flutter, perangkat milik Anda harus
memenuhi persyaratan minimum seperti di bawah ini:

● Windows

● Sistem Operasi : Windows 7 SP1 atau lebih baru (64-bit).


● Ruang Penyimpanan : 1.64 GB dan tidak termasuk IDE dan tools lainnya.
● Flutter bergantung pada tools yang ada di dalam environment:
○ Windows Powershell 5.0 atau versi terbaru (sudah terdapat pada paket
instalasi Windows 10), dapat Anda unduh pada link berikut ini
https://docs.microsoft.com/en-us/powershell/scripting/install/installing-window
s-powershell.
○ Git for Windows 2.x, dengan opsi "Use Git from the Windows Command
Prompt", dapat Anda unduh pada link berikut ini
https://git-scm.com/download/win.
● MacOS

● Sistem Operasi : Mac OS 64-bit.


● Ruang penyimpanan : 2.8 GB dan tidak termasuk IDE dan tools
lainnya.
● Flutter tergantung pada command-line tools ini yang tersedia di
environment:
○ bash
○ curl
○ git 2.x
○ Mkdir
○ rm
○ unzip
○ which

● Linux

● Sistem Operasi : Linux 64-bit.


● Ruang penyimpanan : 1.8 GB dan tidak termasuk IDE dan tools
lainnya.
● Flutter tergantung pada command-line tools ini yang tersedia di
environment:
○ bash
○ curl
○ git 2.x
○ mkdir
○ rm
○ unzip
○ which
○ xz-utils
● Shared Libraries : Perintah test pada Flutter tergantung pada library
yang tersedia di environment.
○ libGLU.so.1 - disediakan oleh mesa packages seperti,
libglu1-mesa di Ubuntu/Debian.

Jika Git untuk Windows sudah diinstal, pastikan Anda dapat menjalankan perintah git dari
Command Prompt (CMD) atau PowerShell.

● Windows
1. Unduh paket instalasi untuk mendapatkan versi stabil terbaru dari
Flutter SDK di alamat web
https://flutter.dev/docs/development/tools/sdk/releases. Ambil versi
terbaru pada stable channel sesuai sistem operasi yang digunakan.
2. Ekstrak berkas zip dan tempatkan folder flutter pada lokasi instalasi
yang diinginkan untuk Flutter SDK. Misalnya C:\Development, jangan
pasang Flutter di direktori seperti C:\Program Files atau yang
membutuhkan hak istimewa seperti administrator.
3. Temukan berkas flutter_console.bat di dalam direktori flutter tersebut.
Mulai dengan klik dua kali atau jalankan script tersebut dan Anda
sekarang siap untuk menjalankan perintah Flutter di Flutter Console.

4. Tampilan dari flutter_console.bat seperti di bawah ini:

● MacOS

1. Unduh paket instalasi untuk mendapatkan versi stabil terbaru dari


Flutter SDK di alamat web
https://flutter.dev/docs/development/tools/sdk/releases, ambil versi
terbaru pada stable channel sesuai sistem operasi yang digunakan.
2. Ekstrak berkas zip tersebut dan tempatkan folder flutter pada lokasi
instalasi yang diinginkan untuk Flutter SDK. Misalnya ~/development.
3. Tambahkan Flutter Tools pada PATH Anda.
​ export PATH="$PATH:~/development/flutter/bin"

● Linux

1. Unduh paket instalasi untuk mendapatkan versi stabil terbaru dari


Flutter SDK di alamat web
https://flutter.dev/docs/development/tools/sdk/releases, ambil versi
terbaru pada stable channel sesuai sistem operasi yang digunakan.
2. Ekstrak berkas zip tersebut dan tempatkan folder flutter pada lokasi
instalasi yang diinginkan untuk Flutter SDK. Misalnya ~/development.
3. Tambahkan Flutter Tools pada PATH Anda
​ export PATH="$PATH:~/development/flutter/bin"

Update Path
Langkah selanjutnya kita akan melakukan update path supaya perintah
Flutter bisa digunakan pada command prompt/terminal. Berikut
langkah-langkahnya:

● Windows
1. Dari bar pencarian di Start menu, ketik ‘env’ dan pilih Edit
Environment Variable untuk akun Anda.
2. Klik pada tombol Environment Variables.

3. Di bawah User variabel periksa apakah ada entri yang disebut PATH,
jika ada maka pilih lalu edit, jika tidak ada maka buat baru dengan
nama variabel Path.

4. Edit atau tambahkan value-nya dengan direktori Flutter SDK.


○ Jika terdapat entri, tambahkan path lengkap ke flutter\bin
menggunakan tanda titik koma (;) sebagai pemisah dari nilai
yang ada (jika menggunakan mode edit satu baris).
○ Jika entri tidak ditemukan, buat user variabel baru dan beri
nama Path dan beri nilai flutter\bin sebagai nilainya.
Perhatikan bahwa Anda harus menutup dan membuka kembali semua
jendela konsol yang ada agar perubahan dapat terlihat.

● MacOS

Anda dapat memperbarui variabel PATH, hanya untuk sesi command-line


saat ini (sementara). Anda mungkin ingin memperbarui variabel secara
permanen, sehingga dapat menjalankan perintah flutter di sesi terminal apa
pun.

Langkah-langkah untuk memodifikasi variabel PATH secara permanen


untuk semua sesi terminal akan tergantung perangkat yang digunakan.
Umumnya, Anda dapat menambahkan variabel PATH seperti ini:
1. Tentukan direktori tempat Anda meletakkan Flutter SDK. Anda akan
membutuhkannya di tahap ke-3 contoh ini.
2. Buka (atau buat) $HOME/.bash_profile. Direktori dan nama file
mungkin berbeda pada perangkat Anda (bisa .profile, .bash_profile,
.zshrc, .bashrc, atau yang lainnya).
3. Tambahkan baris berikut dan ubah [FLUTTER_DIRECTORY]
menjadi direktori dimana Anda menaruh Flutter SDK:
​ export PATH="$PATH:[FLUTTER_DIRECTORY]/flutter/bin"
4. Jalankan source $HOME/.bash_profile untuk memuat ulang terminal
saat ini.
5. Pastikan direktori flutter/bin sekarang ada di PATH Anda dengan cara
menjalankan perintah:
​ echo $PATH

● Linux
Anda dapat memperbarui variabel PATH, hanya untuk sesi command-line
saat ini (sementara). Anda mungkin ingin memperbarui variabel secara
permanen, sehingga dapat menjalankan perintah flutter di sesi terminal apa
pun.

Langkah-langkah untuk memodifikasi variabel PATH secara permanen


untuk semua sesi terminal akan tergantung perangkat yang digunakan.
Umumnya, Anda dapat menambahkan variabel PATH seperti ini:

1. Tentukan direktori tempat Anda meletakkan Flutter SDK. Anda akan


membutuhkannya di tahap ke-3 contoh ini.
2. Buka (atau buat) $HOME/.bash_profile. Direktori dan nama file
mungkin berbeda pada perangkat Anda (bisa .profile, .bash_profile,
.zshrc, .bashrc, atau yang lainnya).
3. Tambahkan baris berikut dan ubah [FLUTTER_DIRECTORY]
menjadi direktori dimana Anda menaruh Flutter SDK pada bagian
sebelumnya:
​ export PATH="$PATH:[FLUTTER_DIRECTORY]/flutter/bin"
4. Jalankan source $HOME/.bash_profile untuk memuat ulang jendela
saat ini.
5. Pastikan direktori flutter/bin sekarang ada di PATH Anda dengan cara
menjalankan perintah:
​ echo $PATH

Flutter Doctor
Flutter doctor adalah perintah untuk mengecek kelengkapan framework
flutter yang Anda gunakan, seperti versi flutter yang digunakan, Android
SDK yang digunakan, iOS SDK yang digunakan (hanya pada MacOS),
perangkat yang sudah terhubung, dan semacamnya. Periksa kembali dan
pastikan dependensi untuk pengaturan sudah lengkap. Jalankan perintah
berikut untuk membuka flutter doctor:

​ flutter doctor
Perintah ini memeriksa environment Anda dan menampilkan laporan ke
jendela terminal. Pada Flutter SDK sudah terdapat Dart SDK, jadi Anda
tidak perlu menginstal Dart secara terpisah. Periksa output dengan cermat
untuk perangkat lunak lain yang mungkin perlu Anda instal atau melakukan
sesuatu lebih lanjut (ditunjukkan dalam teks tebal).

Contoh :

[-] Android toolchain - develop for Android devices

• Android SDK at D:\Android\sdk

**✗ Android SDK is missing command line tools; download from


https://goo.gl/XxQghQ**

• Try re-installing or updating your Android SDK,

visit https://flutter.io/setup/#android-setup for detailed instructions.

Bagian tersebut menjelaskan cara menyelesaikan proses instalasi Flutter


SDK. Setelah memasang dependensi yang hilang, jalankan perintah flutter
doctor lagi untuk memverifikasi bahwa Anda telah mengatur semuanya
dengan benar.

Instalasi IDE
Anda dapat membuat aplikasi dengan Flutter menggunakan editor teks
yang dikombinasikan dengan command-line tools. Namun, sebaiknya
gunakan salah satu plugin editor yang direkomendasikan untuk
pengalaman yang lebih baik. Plugin ini memiliki fitur seperti penyelesaian
kode, penyorotan sintaks, bantuan pengeditan widget, dukungan untuk
menjalankan & debug, dan masih banyak lagi.

Instalasi Android Studio sangat dibutuhkan untuk membuat aplikasi


Android dengan Flutter, tetapi alternatifnya instal saja Java SDK dan
Android SDK sendiri.

Jika sudah menginstal Android Studio, disarankan untuk tidak menginstal


Java secara terpisah dan environment Android lainnya seperti
ANDROID_HOME atau ANDROID_SDK_ROOT. Jika sudah telanjur
terinstal dan terjadi eror android licenses pada flutter doctor, cobalah untuk
uninstall Java package tersebut.

Ikuti langkah-langkah di bawah ini untuk menambahkan plugin editor untuk


Android Studio, IntelliJ, dan Visual Studio Code.

● Android Studio/IntelliJ
Android Studio dan IntelliJ menawarkan pengalaman IDE yang terintegrasi
lengkap dengan plugin khusus untuk Flutter. Untuk mengunduh Android
Studio, Anda dapat mengunjungi tautan
https://developer.android.com/studio. Sedangkan jika Anda memilih IntelliJ
maka dapat melalui tautan https://www.jetbrains.com/idea/download/.

Berikut adalah cara instalasi Flutter danplugin Dart di Android


Studio/IntelliJ:

1. Pertama, buka terlebih dahulu Android Studio/IntelliJ.


2. Pada halaman Welcome pilih menu Plugins di bagian kiri.

3. Jika IDE Android Studio/IntelliJ tidak pad ahalaman Welcome, Anda


dapat pilih menu File > Settings > Plugins bagi pengguna OS
Windows dan Linux.

Sedangkan bagi pengguna MacOS, Pilih menu Android Studio >


Preferences > Plugins.

4. Pilih menu Marketplace, ketik Flutter pada pencarian plugins dan


instal. Saat Anda melakukan instalasi plugins Flutter maka secara
otomatis Dart juga terinstal.

Cek plugins pada menu Installed untuk memastikan plugins Flutter


dan Dart telah terinstal.

5. Klik Restart saat diminta. Jika tidak muncul, silakan muat ulang
secara manual aplikasi Android Studio/IntelliJ.

● Visual Studio Code

Visual Studio Code (VSCode) adalah editor ringan untuk


mengkombinasikan baris perintah khususnya aplikasi Flutter dan
dilengkapi dukungan debugging.

Untuk mengunduh Visual Studio Code, kunjungi tautan


https://code.visualstudio.com/download.
Berikut adalah cara instalasi plugin Flutter dan Dart di VSCode:

1. Buka aplikasi Visual Studio Code yang telah diinstal sebelumnya.


2. Kemudian aktifkan Command Line dengan cara pilih View >
Command Pallete…

3. Ketik “Install” lalu pilih Extensions: Install Extensions.

4. Atau langsung klik pada tab Extensions yang ada pada bagian kiri.

5. Ketik “Flutter” pada bidang pencarian Extensions, dan pilih Flutter


dalam daftar ekstensi yang tersedia. Kemudian klik tombol Install.
Secara otomatis plugin Dart juga akan terinstal.

6. Klik Reload to Activate untuk memuat ulang VSCode.

Setelah selesai, lakukan validasi plugin Flutter di VSCode dengan cara


berikut :

1. Aktifkan Command Line dengan cara pilih View>Command


Pallete…
2. Ketik “doctor”, lalu pilih Flutter: Run Flutter Doctor.

3. Tinjau panel OUTPUT untuk melihat masalah apapun.

Instalasi XCode + Simulator


Untuk menjalankan aplikasi Flutter di dalam iOS, diperlukan instalasi
XCode terlebih dahulu. XCode adalah IDE yang digunakan untuk
mengembangkan dan men-deploy aplikasi iOS.
Yang harus dilakukan untuk mengembangkan aplikasi iOS menggunakan
Flutter adalah:

1. Unduh XCode, bisa Anda unduh langsung dari AppStore.


2. Buka terminal dan masukkan perintah di bawah ini:Perintah ini akan
memastikan bahwa operasi command-line tools di XCode akan
menggunakan versi terbarunya.
​ sudo xcode-select --switch
/Applications/Xcode.app/Contents/Developer
​ sudo xcodebuild -runFirstLaunch
3. Terakhir, sign XCode license agreement-nya dengan membuka
XCode dan konfirmasi. Atau bisa juga sign dengan menggunakan
terminal dan jalankan perintah ini:
​ sudo xcodebuild -license

Pastikan juga untuk selalu mengunduh dan memperbarui versi XCode


Anda, karena Flutter tidak support versi XCode yang lama.

Catatan: Perlu diketahui bahwa XCode di sini hanya dibutuhkan untuk


proses debugging dan deployment saja. Untuk saat ini belum bisa
digunakan untuk editing kode program aplikasi Flutter di dalam XCode.

Jadi jika Anda ingin mengembangkan aplikasi iOS menggunakan Flutter,


yang disarankan adalah:

● Instalasi Android Studio atau Visual Studio Code.


● Tetap instalasi XCode.
● Lakukan editing kode program di Android Studio atau VIsual Studio
Code.
● Jalankan aplikasi menggunakan Android Studio, Visual Studio Code,
atau terminal/cmd.

Project Wizard
Setelah berhasil menginstal Flutter SDK dan IDE artinya peralatan yang
kita butuhkan telah siap. Bagian ini akan menjelaskan langkah-langkah
untuk membuat aplikasi Flutter baru, mulai dari template, cara aplikasi, dan
menggunakan "Hot Reload" setelah Anda melakukan perubahan kode
aplikasi.

Pilih alat pengembangan (IDE) favorit Anda untuk menulis baris perintah,
membangun, dan menjalankan aplikasi Flutter.

● Android Studio/IntelliJ
1. Buka Android Studio/IntelliJ.
2. Pilih New Flutter Project.

3. Pilih Flutter SDK path yang tersedia. Jika path tidak tersedia, klik
tombol titik tiga untuk cari direktori Flutter SDK. Kemudian klik tombol
Next.

4. Masukkan nama proyek yang ingin dibuat pada Project name.


Tentukan di mana proyek Anda disimpan pada Project location.
Deskripsikan proyek Anda pada kolom Descriptions. Pilih
Application pada kolom Project type untuk membuat aplikasi Flutter.
Masukkan nama domain yang ingin dibuat pada Organization. Pilih
Kotlin untuk membuat proyek Android-nya menggunakan bahasa
Kotlin dan Swift untuk membuat proyek iOS-nya menggunakan
bahasa Swift.
Centang Platform yang ingin dikembangkan saat proyek Flutter
dibuat, misal Anda ingin mengembangkan platform Android dan iOS,
centang hanya keduanya saja. Selanjutnya, centang pada Create
project offline jika Anda ingin membuat proyek dapat diakses ketika
sedang offline. Klik Finish.
5. Tunggu beberapa saat hingga Android Studio selesai membuat
project.

● Visual Studio Code


● Buka Visual Studio Code.
● Aktifkan Command Line dengan cara klik View>Command Pallete…
● Ketik “Flutter” lalu pilih Flutter: New Project.

● Masukkan nama project, dan tekan Enter.

● Buat atau pilih direktori untuk folder project baru.


● Tunggu beberapa saat sampai VSCode selesai menyiapkan project.

● Terminal & Editor


● Buka Terminal/CMD.
● Gunakan perintah flutter create untuk membuat project baru:
○ flutter create dicoding
○ cd dicoding

Perintah di atas membuat direktori project Flutter yang berisi aplikasi


demonstrasi sederhana menggunakan Material Components.

Tips: Kode Dart untuk aplikasi Anda ada di lib/main.dart. Untuk deskripsi
lebih lanjut tentang apa yang dilakukan setiap blok kode, lihat komentar
yang ada di dalam file tersebut.

Menjalankan Emulator Android atau iOS


Sebelumnya kita telah berhasil menginstal flutter SDK, IDE, dan membuat
project baru berdasarkan IDE yang digunakan. Sekarang kita akan mulai
belajar cara menjalankan project Android menggunakan emulator atau
device, maupun project iOS dengan menggunakan simulator.

Menjalankan di Android
Kita akan menggunakan project Flutter yang telah dibuat sebelumnya
untuk dijalankan di emulator atau device Android. Ada beberapa hal yang
perlu diperhatikan untuk dapat menjalankan project-nya, uraiannya adalah
sebagai berikut:

Android Emulator
Sebelum menggunakan emulator, Anda perlu memastikan beberapa
syarat.

1. Virtualization
Untuk menjalankan emulator di dalam Android Studio, pastikan
aspek virtualization. Sistem Anda harus memenuhi persyaratan,
yakni ketentuan prosesor dan sistem operasi dari laptop/PC yang
Anda gunakan.
2. Processor
a. Intel: Jika Anda menggunakan processor Intel, maka pastikan
bahwa processor tersebut mendukung Intel VT-X, Intel EM64T
(Intel 64), dan Execute Disable (XD) Bit functionality.
Processor Intel mampu menjalankan emulator di sistem
operasi Windows, Linux, mau pun MacOS.
b. AMD: Jika Anda menggunakan AMD, maka pastikan bahwa ia
mendukung AMD Virtualization (AMD-V) dan Supplemental
Streaming SIMD Extensions 3 atau yang biasa disebut dengan
SSSE3.
Processor AMD hanya bisa menjalankan emulator di sistem
operasi Linux.
Persiapan Running menggunakan Emulator
1. Untuk menjalankan aplikasi, klik menu Tools, lalu pilih Device
Manager.

2. Untuk membuat emulator baru, klik tombol Create device atau


Create virtual device.
3. Halaman Select Hardware akan muncul.
Jika Anda ingin membuat spesifikasi hardware (perangkat keras)
sendiri, pilih New Hardware Profile. Anda dapat menentukan
konfigurasi hardware sesuai dengan kebutuhan Anda. Ingatlah untuk
menggunakan konfigurasi emulator yang sesuai dengan kemampuan
laptop atau komputer yang Anda gunakan. Setelah itu klik Next.
4. Halaman System Image akan muncul.
Pada halaman ini Anda dapat memilih versi Android dari emulator
yang akan Anda buat. Pilih versi yang sudah diunduh, misal Pie.
Lihat tombol download di sebelah kanan. Anda perlu mengunduh
terlebih dahulu jika ingin menggunakannya. Setelah itu klik Next.
5. Halaman Verify Configuration akan muncul.
Pada halaman ini Anda bisa mengubah konfigurasi hardware yang
telah ditentukan sebelumnya. Tekan tombol Show Advanced
Setting pada bagian kiri bawah. Jika sudah selesai, klik Finish.
Pengaturan emulator sudah selesai dan bisa langsung dijalankan.
Anda dapat membuka emulatornya dengan menekan tombol hijau
yang ada di sebelah kanan.
Android Device
Selain menggunakan Android Emulator, Anda juga dapat melakukan run
atau debugging menggunakan perangkat smartphone asli. Menjalankan
aplikasi menggunakan perangkat fisik memiliki beberapa kelebihan jika
dibandingkan dengan emulator, yaitu:

● Lebih cepat,
● Lebih ringan, dan
● Lebih mudah.

Dengan menggunakan peranti smartphone asli, kita dapat memastikan


bahwa aplikasi kita berjalan dengan wajar ketika sudah sampai di tangan
pengguna. Kendala dari pendekatan ini adalah beragamnya model peranti
yang ada di pasaran. Namun, pembahasan mengenai hal tersebut tidak
tercakup dalam kelas ini.

Mari ikuti langkah-langkah untuk menjalankan proses run atau debugging.


Tampilan dari langkah berikut bisa dipastikan akan berbeda dengan peranti
yang Anda pakai. Akan tetapi secara garis besar langkahnya akan sama.

1. Pastikan peranti yang akan dipakai sesuai dengan target SDK atau
paling tidak mendukung versi SDK terendah yang digunakan aplikasi.
2. Buka setting dan masuk ke dalam menu About. Pada halaman
menu ini, Anda perlu menemukan informasi tentang Build number.
3. Kemudian tekan Build number sebanyak 7 kali.

4. Kembali ke menu setting di awal dan akan muncul menu baru di


bawah about yaitu Developer Options.
5. Masuk ke dalam menu Developer Options dan pastikan opsi USB
Debugging Mode sudah dalam keadaan On.

6. Setelah menyelesaikan pengaturan pada peranti, peranti pun dapat


dihubungkan dengan laptop atau komputer yang Anda pakai.

Catatan: Beberapa vendor smartphone memiliki sistem operasi yang unik.


Tampilan setting dan letak opsi bisa jadi berbeda dengan gambar di atas.
Beberapa vendor juga mengharuskan Anda untuk mengunduh driver
khusus sebelum bisa menghubungkannya ke Android Studio. Kami
sarankan untuk mengunjungi website atau membaca petunjuk yang sesuai
dengan vendor dari peranti Anda.

Menjalankan di iOS
Kita akan menggunakan project Flutter yang telah dibuat sebelumnya
untuk dijalankan di iOS simulator. Ada beberapa hal yang perlu
diperhatikan untuk dapat menjalankan project-nya, uraiannya adalah
sebagai berikut:
iOS Simulator
iOS Simulator atau Simulator adalah aplikasi yang digunakan untuk
menjalankan aplikasi iOS. Simulator merupakan aplikasi bawaan dari
XCode.

Selain dari Simulator, sebenarnya kita bisa menggunakan device iphone


asli. Akan tetapi untuk menjalankan di device asli kita memerlukan akun
Apple developer. Jadi untuk menyederhanakan masalah, di modul ini
hanya akan diajarkan dengan media Simulator saja.

Ikuti langkah berikut ini untuk membuka Simulator:

1. Buka terminal dan jalankan kode berikut ini:


​ open -a Simulator
2. Aplikasi simulator akan muncul, contohnya seperti gambar di bawah
ini:

3. Pastikan Simulator yang Anda jalankan adalah 64-bit (Iphone 5 ke


atas), jika belum maka ganti konfigurasinya dengan mengakses
lewat Device atau File -> Open Devices (versi terbaru).

Catatan: Contoh di atas diambil dengan MacOS Catalina. Jika Anda


menggunakan versi di bawahnya ada kemungkinan perbedaan tampilan.
Jika Anda menemui masalah silakan bertanya di forum diskusi.
Menjalankan Project Flutter
Setelah mengetahui persiapan sebelum menjalankan project di Android
dan di iOS, baik itu di emulator maupun device. Berikut cara untuk
menjalankannya di beberapa IDE yang sudah dijelaskan sebelumnya.

● Android Studio/IntelliJ
1. Temukan toolbar Android Studio seperti di bawah ini:

2. Pada target selector, pilih perangkat Android untuk menjalankan


aplikasi. Jika tidak ada yang terdaftar, pilih Tools > Device Manager
dan buat satu emulator di Device Manager.
3. Klik ikon run pada toolbar, atau buka item menu Run > Run
‘main.dart’.
4. Setelah build aplikasi selesai, Anda akan melihat aplikasi starter di
perangkat Anda.

● Visual Studio Code


● Temukan Status Bar pada bagian paling bawah jendela VSCode
seperti di bawah ini:

● Pilih perangkat di area Device Selector.


○ Jika tidak ada perangkat yang tersedia dan Anda ingin
menggunakan simulator perangkat, klik No Device dan
luncurkan simulator.
○ Untuk menyiapkan perangkat fisik (smartphone pribadi), ikuti
petunjuk khusus perangkat pada saat melakukan Install
Flutter SDK untuk OS Anda.
● Aktifkan Command Line dengan cara pilih Run > Start Debugging
atau tekan F5, atau pada kode void main akan muncul run dan
debug, atau pada tab Run di bagian kiri.
● Tunggu hingga aplikasi diluncurkan dan lihat progress dalam
tampilan Debug Console.
● Setelah build aplikasi selesai, Anda akan melihat aplikasi starter di
perangkat Anda.

● Terminal & Editor


● Periksa apakah perangkat Android sedang berjalan. Jika tidak ada
yang ditampilkan, ikuti petunjuk khusus perangkat pada saat
melakukan Install Flutter SDK untuk OS Anda.
○ flutter devices
● Masuk ke direktori di mana project itu berada.
● Jalankan aplikasi dengan perintah berikut:
○ flutter run
● Setelah build aplikasi selesai, Anda akan melihat aplikasi starter di
perangkat Anda.

Selamat Anda telah berhasil menjalankan aplikasi Flutter pertama Anda!

Menjalankan di Web
Selain platform mobile, Flutter juga bisa berjalan pada web browser. Dalam
proses pengembangan, untuk keperluan debugging kita perlu
menggunakan Google Chrome sebagai browser. Sejak Flutter versi 2.0
Flutter Web sudah memasuki versi stable sehingga bisa digunakan secara
langsung. Jika menggunakan versi di bawahnya, Anda perlu mengaktifkan
channel beta terlebih dahulu. Silakan ikuti langkahnya pada blog berikut.
Untuk menambahkan dukungan web pada project Anda, jalankan perintah
berikut melalui terminal dari lokasi project:

​ flutter config --enable-web


​ flutter create .
Setelah konfigurasi dan proses re-create berhasil, folder web akan
ditambahkan ke dalam folder project Anda. Di dalam folder ini akan berisi
berkas html dan konfigurasi web lainnya. Anda bisa mencoba menjalankan
aplikasi web Flutter ini dengan memilih target device chrome pada IDE
yang Anda gunakan.

Android Studio:

Visual Studio Code:

Ketika target device Chrome sudah terpilih seperti di atas, Anda dapat
menjalankan aplikasi dengan cara yang sama seperti ketika
menjalankannya pada platform mobile. Jika Anda ingin menjalankan
melalui terminal, maka perintahnya adalah seperti ini:

​ flutter run -d chrome


Setelah proses build selesai, jendela browser akan muncul dan
menampilkan aplikasi Anda:
Pada Flutter Web, perlu diperhatikan bahwa Flutter memiliki dua jenis
renderer yang berbeda, keduanya antara lain:

● HTML renderer
Renderer ini menggunakan kombinasi elemen HTML, CSS, Canvas,
dan SVG. Jenis renderer ini memiliki ukuran unduhan yang lebih
kecil.

● CanvasKit renderer
Renderer ini bekerja dengan cara yang sama dengan platform mobile
atau desktop. CanvasKit renderer memiliki performa yang lebih
tinggi, tetapi akan menambahkan ukuran hingga sekitar 2 MB.

Anda dapat menentukan renderer yang digunakan dengan menambahkan


parameter pada command line, contohnya seperti berikut:

​ flutter run -d chrome --web-renderer html


​ flutter run -d chrome --web-renderer canvaskit
Jika Anda tidak mendefinisikan parameter --web-renderer, maka renderer
akan menggunakan mode auto (default). Opsi ini akan menggunakan
HTML renderer ketika aplikasi berjalan di browser mobile dan
menggunakan CanvasKit saat aplikasi berjalan di browser desktop.
Hot reload
Fitur hot reload pada Flutter membantu Anda untuk melakukan
percobaan, membangun UI, menambahkan fitur, serta memperbaiki bug
dengan cepat dan mudah. Hot reload berfungsi dengan menyuntikkan
berkas kode yang diperbarui ke dalam Dart Virtual Machine (VM) yang
sedang berjalan. Setelah VM memperbarui class dengan versi field dan
fungsi yang baru, framework Flutter secara otomatis membangun kembali
widget. Hal ini memungkinkan Anda dengan cepat melihat efek dari
perubahan Anda.

Lebih lanjut, berikut ini cara melakukan hot reload pada aplikasi Flutter:

1. Jalankan aplikasi Flutter. Hanya aplikasi Flutter dalam mode debug


yang dapat melakukan hot reload.
2. Ubah beberapa kode kecil dalam salah satu file Dart di project Anda.
Jika perubahan yang terjadi banyak atau perubahan berada di dalam
initState atau main, Anda harus melakukan hot restart.
3. Jika Anda menggunakan IDE/editor yang didukung Flutter (Android
Studio/IntelliJ/Visual Studio Code), pilih Save/Save All (cmd+s/ctrl+s)
atau klik tombol Hot Reload pada toolbar di setiap IDE.
4. Jika Anda menjalankan aplikasi di command line menggunakan
flutter run, masukkan perintah r di jendela terminal.

Setelah operasi hot reload berhasil, Anda akan melihat pesan di konsol
seperti ini:

Performing hot reload...

Reloaded 1 of 448 libraries in 978ms.

Hot reload menyebabkan semua widget yang ada dibangun kembali.


Hanya kode yang terlibat dalam rebuilding widget yang dieksekusi ulang
secara otomatis. Seperti contoh fungsi main dan initState tidak akan
dijalankan kembali jika menggunakan hot reload.
Apa bedanya hot reload, hot restart, dan full restart?

● Hot reload memuat perubahan kode ke dalam VM dan menjalankan


kembali metode build yang ada di dalam widget. Ini akan
membangun kembali widget-widget yang ada, dan mempertahankan
status terakhir aplikasi.
● Hot restart memuat perubahan kode ke dalam VM dan mengulang
kembali aplikasi dari awal dan akan kehilangan state aplikasi
(kembali ke nilai awal).
● Full restart mengkompilasi kode dari awal, tidak ada pintasan
keyboard untuk ini, Anda harus menghentikan dan menjalankan
kembali project (klik stop kemudian run kembali).

Flutter web saat ini hanya mendukung hot restart.

● Android Studio/IntelliJ
Flutter menawarkan siklus pengembangan yang cepat dengan Hot Reload,
kemampuan memuat ulang kode aplikasi yang sedang berjalan tanpa
memulai ulang atau kehilangan status aplikasi. Untuk perubahan ke
sumber aplikasi, beritahu IDE atau alat baris perintah Anda bahwa Anda
ingin melakukan Hot Reload, kemudian lihat perubahan di simulator,
emulator, atau perangkat Anda.

1. Buka lib/main.dart.
2. Ubah string 'You have pushed the button this many times' menjadi
'You have clicked the button this many times'.
Penting: Jangan hentikan aplikasi Anda. Biarkan aplikasi berjalan.
3. Simpan perubahan baris perintah dengan cara : Save All, atau klik
Hot Reload.
Anda akan langsung melihat string yang sudah diperbarui di aplikasi yang
sedang berjalan.

Note: Jika fitur reload belum aktif, jalankan perintah flutter doctor dan
pastikan android licenses sudah terpasang. Perintah android licenses
sebagai berikut: flutter doctor --android-licenses

● Visual Studio Code


Flutter menawarkan siklus pengembangan yang cepat dengan Hot Reload.
Fitur tersebut memiliki kemampuan memuat ulang kode aplikasi yang
sedang berjalan tanpa memulai ulang atau kehilangan status aplikasi.
Caranya, Buat perubahan ke sumber aplikasi, beritahu IDE atau alat baris
perintah Anda bahwa Anda ingin melakukan Hot Reload, dan lihat
perubahan di simulator, emulator, atau perangkat Anda.

1. Buka lib/main.dart.
2. Ubah string 'You have pushed the button this many times' menjadi
'You have clicked the button this many times'.
Penting: Jangan hentikan aplikasi Anda. Biarkan aplikasi Anda
berjalan.
3. Simpan perubahan baris perintah dengan cara: Pilih File > Save All,
atau klik Hot Reload (tombol petir kuning).
Anda akan langsung melihat string yang sudah diperbarui di aplikasi yang
sedang berjalan.

● Terminal & Editor


Flutter menawarkan siklus pengembangan yang cepat dengan Hot Reload.
Ia memiliki kemampuan untuk memuat ulang kode aplikasi yang sedang
berjalan tanpa memulai ulang atau kehilangan status aplikasi. Untuk
membuat perubahan ke dalam aplikasi utama, Anda bisa memanggil IDE
atau bahwa Anda ingin melakukan Hot reload, dan lihat perubahan pada
simulator, emulator atau perangkat yang Anda gunakan.

1. Buka lib/main.dart.
2. Ubah string 'You have pushed the button this many times' menjadi
'You have clicked the button this many times'.
Penting: Jangan hentikan aplikasi Anda. Biarkan aplikasi Anda
berjalan.
3. Simpan perubahan baris perintah.
4. Ketik perintah r di jendela terminal, untuk melakukan Hot Reload.
Anda akan langsung melihat string yang sudah diperbarui di aplikasi yang
sedang berjalan.

Kasus spesial
Bagian berikutnya menjelaskan situasi umum di mana kode yang
dimodifikasi tidak akan berjalan lagi setelah hot reload. Dalam beberapa
kasus, perubahan kecil pada kode Dart akan memungkinkan Anda untuk
terus menggunakan hot reload untuk aplikasi Anda. Dalam beberapa
kasus, kita perlu melakukan hot restart atau full restart:

● Aplikasi mati atau keluar


Hot reload akan berhenti jika aplikasi terhenti atau keluar, misalnya
jika aplikasi terlalu lama berada di background.
● Compilation errors
Ketika perubahan kode melihatkan kesalahan kompilasi, hot reload
selalu menghasilkan pesan kesalahan yang seperti di bawah ini:
Hot reload was
rejected:'/Users/obiwan/Library/Developer/CoreSimulator/Devices/AC
94F0FF-16F7-46C8-B4BF-218B73C547AC/data/Containers/Data/Ap
plication/4F72B076-42AD-44A4-A7CF-57D9F93E895E/tmp/ios_test
WIDYdS/ios_test/lib/main.dart': warning: line 16 pos 38: unbalanced
'{' opens here
Widget build(BuildContext context) {
^
'/Users/obiwan/Library/Developer/CoreSimulator/Devices/AC94F0FF-
16F7-46C8-B4BF-218B73C547AC/data/Containers/Data/Application/
4F72B076-42AD-44A4-A7CF-57D9F93E895E/tmp/ios_testWIDYdS/i
os_test/lib/main.dart': error: line 33 pos 5: unbalanced ')'
);
^
Dalam situasi ini cukup perbaiki kesalahan pada baris kode Dart
yang ditentukan untuk tetap menggunakan hot reload.
● CupertinoTabView’s builder
Hot reload tidak akan berjalan ketika ada perubahan pada builder di
CupertinoTabView. Untuk lebih lanjutnya silakan baca
https://github.com/flutter/flutter/issues/43574.
● Tipe enum
Hot reload tidak akan bekerja ketika tipe enum diubah menjadi kelas
reguler atau sebaliknya.
​ // Contoh sebelum:
​ enum Level { beginner, intermediate, advanced }


​ // Contoh sesudah:
​ class Level {
​ Level(this.id);
​ int id;
​ }
● Perubahan font
Hot reload mendukung sebagian besar perubahan aset. Namun, jika
mengubah font, Anda harus menggunakan hot restart.
● Generic types
Hot reload tidak akan bekerja ketika pendeklarasian tipe generik
dimodifikasi.
​ // Contoh sebelum:
​ class A<X> {
​ X ax;
​ }

​ // Contoh sesudah:
​ class A<X, Y> {
​ X ax;
​ Y ay;
​ }
● Native code
Jika Anda mengubah kode native tiap platform (seperti Java, Kotlin,
Swift, atau Objective-C), Anda harus melakukan full restart (stop dan
run kembali project) untuk melihat perubahan.
● State sebelumnya dikombinasikan dengan kode baru
Fitur hot reload pada Flutter kadang-kadang digambarkan sebagai
stateful hot reload yang mempertahankan status aplikasi Anda. Ini
memungkinkan Anda untuk melihat efek perubahan terbaru saja
tanpa membuang kondisi saat ini. Misalnya, jika aplikasi Anda
mengharuskan pengguna untuk login, Anda dapat memodifikasi dan
melakukan hot reload beberapa halaman di bawah hierarki navigasi,
tanpa memasukkan kembali kredensial login Anda. Saat disimpan,
maka hasil output akan sesuai dengan yang kita inginkan atau kita
ubah.

Jika perubahan kode memengaruhi kondisi aplikasi Anda, seperti


data atau fungsi yang seharusnya berjalan dengan baik tiba-tiba
berhenti berfungsi. Maka disarankan untuk menggunakan Hot
Restart.

Seperti Kode di bawah ini :


​ class MyWidget extends StatelessWidget {
​ Widget build(BuildContext context) {
​ return GestureDetector(onTap: () => print('T'));
​ }
​ }
● Diubah menjadi:
​ class MyWidget extends StatefulWidget {
​ @override
​ State createState() => MyWidgetState();
​ }

​ class MyWidgetState extends State { /*...*/ }
● Kemudian tekan tombol hot reload, maka console akan menampilkan
output seperti berikut:
MyWidget is not a subtype of StatelessWidget
Dalam situasi ini, full restart diperlukan untuk melihat aplikasi yang
diperbarui.

Selamat Anda telah menyelesaikan modul pertama ini. Semoga tetap


semangat ya. Apalagi kini Anda telah membuat project dan menjalankan
aplikasi Flutter pertama Anda. Wow! Pada modul selanjutnya Anda telah
siap untuk membuat aplikasi Anda sendiri. Sudah siap? Jaga semangat
Anda dan sampai jumpa di modul berikutnya ya!
Rangkuman Materi
Selamat Anda telah menyelesaikan modul pertama ini. Semoga tetap
semangat ya. Apalagi kini Anda telah membuat project dan menjalankan
aplikasi Flutter pertama Anda. Wow! Mari kita ingat kembali apa saja yang
telah dibahas pada modul ini:

● Flutter adalah SDK (Software Development Kit) yang dikembangkan


oleh Google untuk membuat aplikasi yang bagus dan bisa berjalan
pada berbagai platform.
● Flutter 2 yang merupakan versi terbaru memberikan dukungan pada
Anda untuk membangun aplikasi pada sistem operasi Android, iOS,
Web, Windows, Linux, dan MacOS.
● Flutter menawarkan empat karakteristik atau kelebihan, antara lain:
beautiful, fast, productive, dan open.
● Flutter ditulis menggunakan bahasa Dart. Dart merupakan bahasa
pemrograman berbasis kelas dan berorientasi terhadap objek
dengan menggunakan Syntax bahasa pemrograman C.
● Untuk bisa mengembangkan aplikasi menggunakan Flutter, kita perlu
menyiapkan dan menginstal Flutter SDK. Tools ini tersedia pada
sistem operasi Windows, macOS, dan Linux.
● Flutter memiliki dukungan terhadap beberapa IDE populer, seperti
Android Studio, IntelliJ IDEA, dan Visual Studio Code.
● Kita dapat menguji coba dan menjalankan aplikasi Flutter
menggunakan perangkat fisik, Android Emulator, iOS Simulator, atau
web browser.
● Flutter Web memiliki dua jenis renderer yang berbeda, yaitu HTML
renderer dan CanvasKit renderer. Jika tidak mendefinisikan renderer
apa yang digunakan, maka mode auto yang akan dipakai.
● Flutter memiliki fitur yang sangat berguna untuk meningkatkan
produktivitas pengembangan, yaitu hot reload. Fitur ini akan secara
langsung mengaplikasikan perubahan kode tanpa harus melakukan
proses build ulang.
Dasar-Dasar Flutter

Pengenalan Dasar-Dasar Flutter


Sebelumnya kita telah belajar bagaimana cara menginstal Flutter pada
komputer dan menjalankan aplikasi Flutter untuk pertama kalinya. Nah, kali
ini kita akan mempelajari struktur project Flutter dan membuat aplikasi
pertama kita. Mari kita simak uraian berikut ini.

Struktur Project Flutter


Setelah membuat project Flutter pertama kali, flutter akan membuatkan
struktur project. Ketika membuka folder project Flutter pada berkas
explorer, kita akan mendapati folder-folder seperti berikut:

Untuk tingkat pemula kita tidak perlu mengetahui seluk-beluk struktur


project secara mendetail. Di sini Anda hanya cukup mengenal beberapa
dari folder-folder tersebut.

Berikut beberapa folder yang harus Anda ketahui:

● android/
Folder ini merupakan tempat Anda untuk mengatur konfigurasi untuk
aplikasi android. Di dalamnya terdapat file gradle, AndroidManifest,
dan lain-lain. File-file tersebut sangat umum ketika Anda membuat
aplikasi android native (menggunakan bahasa pemrograman Java
atau Kotlin), nanti Anda akan melakukan beberapa setting pada
folder android seiring waktu.

● ios/
Sama halnya dengan folder android, hanya saja ini untuk iOS. Folder
ini merupakan tempat konfigurasi untuk aplikasi iOS. Ketika kita
hendak membuat project flutter yang dapat berjalan pada iPhone,
Anda akan berkutat dengan folder ini.

● build/
Ketika Anda melakukan build project flutter, hasil build akan ada
pada folder ini. Sebagai contoh, ketika Anda ingin membuat file APK
untuk project flutter, maka hasil file tersebut ada dalam folder ini.
Folder ini hanya akan ada ketika sudah pernah mem-build project,
dan akan terhapus jika menjalankan flutter clean.

● lib/
Ini merupakan folder utama ketika Anda mengerjakan project flutter.
Seluruh source code flutter Anda akan berada pada folder ini.

● test/
Folder ini tempat Anda menyimpan source code testing. Untuk
pemula tidak akan berkutat pada folder ini.

Hello World App


Ketika pertama kali membuat project Flutter, kita akan diberikan aplikasi
contoh yaitu aplikasi counter. Source code utama Flutter kita ada pada file
lib/main.dart dan kodenya kurang lebih seperti berikut:

​ import 'package:flutter/material.dart';

​ void main() {
​ runApp(MyApp());
​ }

​ class MyApp extends StatelessWidget {
​ @override
​ Widget build(BuildContext context) {
​ return MaterialApp(
​ title: 'Flutter Demo',
​ theme: ThemeData(
​ primarySwatch: Colors.blue,
​ visualDensity: VisualDensity.adaptivePlatformDensity,
​ ),
​ home: MyHomePage(title: 'Flutter Demo Home Page'),
​ );
​ }
​ }

​ class MyHomePage extends StatefulWidget {
​ MyHomePage({Key key, this.title}) : super(key: key);

​ final String title;

​ @override
​ _MyHomePageState createState() => _MyHomePageState();
​ }

​ class _MyHomePageState extends State<MyHomePage> {
​ int _counter = 0;

​ void _incrementCounter() {
​ setState(() {
​ _counter++;
​ });
​ }

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ appBar: AppBar(
​ title: Text(widget.title),
​ ),
​ body: Center(
​ child: Column(
​ mainAxisAlignment: MainAxisAlignment.center,
​ children: <Widget>[
​ Text(
​ 'You have pushed the button this many times:',
​ ),
​ Text(
​ '$_counter',
​ style: Theme.of(context).textTheme.headline4,
​ ),
​ ],
​ ),
​ ),
​ floatingActionButton: FloatingActionButton(
​ onPressed: _incrementCounter,
​ tooltip: 'Increment',
​ child: Icon(Icons.add),
​ ),
​ );
​ }
​ }
Kode di atas merupakan kode starter yang di-generate sebagai contoh
ketika Anda membuat project. Anda dapat mempelajari kode tersebut untuk
mengetahui bagaimana sebuah aplikasi Flutter disusun.

Untuk saat ini kita tidak akan menggunakan kode tersebut. Jadi, hapus
semua isi berkas main.dart tersebut dan tulis kode untuk aplikasi kita
sendiri, yaitu aplikasi sederhana untuk menampilkan teks Hello world.

​ import 'package:flutter/material.dart';

​ void main() {
​ runApp(MyApp());
​ }

​ class MyApp extends StatelessWidget {
​ @override
​ Widget build(BuildContext context) {
​ return MaterialApp(
​ title: 'Flutter Demo',
​ theme: ThemeData(
​ primarySwatch: Colors.blue,
​ ),
​ home: Scaffold(
​ appBar: AppBar(
​ title: Text('Hello, world!'),
​ ),
​ body: Center(
​ child: Text('Hello, world!'),
​ ),
​ ),
​ );
​ }
​ }
Mari kita bahas satu per satu kodenya!

​ import 'package:flutter/material.dart';
Import digunakan untuk memanggil fungsi-fungsi dari berkas Flutter yang
lain. Pada kode di atas, kita meng-import kode-kode yang terdapat dalam
library material bawaan Flutter. Library tersebut menyediakan widget yang
termasuk dalam material design. Pastikan kode ini ada pada bagian atas
sebelum kode lain dijalankan.

​ void main() => runApp(MyApp());


main() merupakan kode dasar dari project Flutter kita. Flutter akan
menjalankan fungsi main() pertama kali, yang nantinya akan menjalankan
runApp() dan memanggil MyApp().

​ class MyApp extends StatelessWidget {


​ @override
​ Widget build(BuildContext context) {
​ return MaterialApp(
​ title: 'Flutter Demo',
​ theme: ThemeData(
​ primarySwatch: Colors.blue,
​ ),
​ home: Scaffold(
​ appBar: AppBar(
​ title: Text('Hello, world!'),
​ ),
​ body: Center(
​ child: Text('Hello, world!'),
​ ),
​ ),
​ );
​ }
​ }
MyApp di sini merupakan sebuah class yang menampilkan komponen
Flutter ke layar atau dikenal dengan Widgets.

Jika aplikasi dijalankan maka akan seperti berikut:

Setiap komponen di dalam Flutter terdiri dari widget. Bahkan aplikasi


Flutter itu sendiri merupakan sebuah widget. Pada contoh kode di atas
kelas MyApp yang merupakan sebuah widget mengembalikan atau
menampilkan widget MaterialApp. MaterialApp ini adalah widget bawaan
Flutter yang akan menjadi fondasi dari aplikasi Anda. Ia umum digunakan
supaya aplikasi bisa menerapkan material design. Widget ini mengatur
beberapa hal, termasuk tema aplikasi, tampilan utama aplikasi, rute untuk
navigasi antar halaman, dan lain-lain.
Selanjutnya Scaffold memungkinkan kita mendesain struktur layout dasar
yang sesuai dengan material design. Dengan Scaffold Anda dapat
mengatur App Bar, Floating Action Button, Drawer, dan lain-lain.

Ibarat rumah, MaterialApp ini adalah sebagai tanah yang umumnya


digunakan hanya sekali saja, sedangkan Scaffold ini adalah sebagai
tembok atau bangunannya.

Terakhir, seharusnya sudah cukup jelas kita menggunakan widget Text


untuk menampilkan teks. Pada contoh di atas widget Text dibungkus
dengan widget Center yang berfungsi supaya widget di dalamnya (child)
tampil pada posisi tengah.

Tips: Documentation is your best friend.

Flutter memiliki banyak sekali widget dan tentunya akan menjadi tugas
yang berat untuk menghafalkan semuanya. Namun tenang, Flutter
dilengkapi dengan dokumentasi yang cukup lengkap sebagai panduan kita
dalam mempelajari dan mengembangkan Flutter. Misalnya, penjelasan
lebih lengkap tentang widget yang kita gunakan di atas dapat dilihat pada
tautan berikut:

● https://api.flutter.dev/flutter/material/MaterialApp-class.html
● https://api.flutter.dev/flutter/material/Scaffold-class.html
● https://api.flutter.dev/flutter/widgets/Center-class.html
● https://api.flutter.dev/flutter/widgets/Text-class.html

Menggunakan Packages
Package Dependencies
Dalam pengembangan suatu aplikasi, kita tidak akan lepas dari package/library (selanjutnya
akan disebut package). Package di sini merupakan sebuah kode yang dibuat untuk
menyelesaikan suatu masalah. Contohnya ketika aplikasi yang kita buat membutuhkan fitur
kalender sementara fitur tersebut tidak di-support oleh Flutter. Alih-alih membuat fitur
kalender dari nol, kita dapat menggunakan package yang telah dibuat oleh developer lain.
Waktu pembuatan fitur menjadi lebih singkat!
Package dependencies merupakan sekumpulan package yang dibutuhkan dalam
pengembangan aplikasi. Package tersebut akan diatur oleh package manager. Setiap
bahasa pemrograman memiliki package manager-nya masing-masing, contohnya NodeJS
memiliki npm atau yarn, Java dengan maven atau gradle, PHP dengan composer. Begitu
pula dengan Flutter yang ditulis dengan bahasa dart memiliki package manager bernama
pub.

Kali ini kita akan membahas mengenai package manager pub, bagaimana menggunakan
pub, dan di mana mencari package yang dapat digunakan untuk aplikasi kita.

Dart Pub
Seperti yang telah kita singgung sebelumnya Pub merupakan sebuah package manager.
Pub memiliki tugas untuk mengatur package apa saja yang dibutuhkan dalam
pengembangan aplikasi. Pada package manager kita dapat mengatur versi package yang
ingin kita gunakan. Pengaturan versi sangat penting karena ketika versi flutter/dart yang
digunakan tidak cocok dengan package yang kita butuhkan akan berpengaruh pada
jalannya aplikasi yang kita buat. Oleh karena itu, kita harus memastikan versi yang
kompatibel dengan versi Flutter yang terinstal.

Lalu bagaimana kita menggunakan pub pada project Flutter kita? Untuk mengatur
package-package yang akan kita gunakan, cukup buka berkas pubspec.yaml yang ada
pada folder project.

Ketika membuka berkas pubspec.yaml kita akan melihat begitu banyak pengaturan tapi
tidak perlu khawatir karena yang kita bahas hanya mengenai package dependencies-nya
saja.
Coba kita fokus pada kode yang ada pada pubspec.yaml 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

​ dev_dependencies:
​ flutter_test:
​ sdk: flutter
Kode di atas merupakan package-package yang digunakan pada project Flutter kita. Jika
kita perhatikan, terdapat 2 jenis dependency yaitu dependencies dan dev_dependencies.
Fungsi dev_dependencies digunakan untuk package-package yang berkaitan ketika proses
pengembangan aplikasi Flutter, contohnya seperti flutter_test yang digunakan untuk testing.
Package di dalam dev_dependencies tidak akan disertakan ketika aplikasi dirilis pada play
store atau app store. Fungsi dependencies digunakan untuk package-package yang
langsung berkaitan dengan fitur aplikasi Flutter, contohnya seperti cupertino_icons yang
digunakan untuk mendapatkan ikon-ikon cupertino (icon untuk iOS) dan contoh lainnya
seperti cloud_firestore yang merupakan package untuk firebase firestore.

Sekarang kita akan fokus pada dependencies. Untuk mendaftarkan package yang
dibutuhkan kita cukup menulis seperti di bawah ini pada bagian dependencies:

​ nama_package: versi
nama_package merupakan nama package yang kita butuhkan, lalu disambung dengan
versinya. Penulisan versi bisa langsung seperti contoh 0.1.2, atau kita menambahkan simbol
caret (^) seperti ^0.1.2 . Simbol caret (^) artinya: gunakan versi patch terbaru dari versi yang
telah ditentukan. Jika versi nya ^0.1.2 artinya kita akan gunakan versi minimal 0.1.2 dan
maksimal versi terbaru. Karena itu, jika versi package tersebut sekarang sudah update,
maka package yang digunakan merupakan versi terbaru.

Catatan: Hanya pada versi patch atau pada angka terakhir yaitu angka 2 jika pada contoh
cupertino_icons: ^0.1.2. Atau kita juga bisa gunakan versi minimal dan maksimal seperti
contoh ‘>=0.1.2 <2.0.0’ yang artinya kita akan menggunakan versi terbaru yang ada pada
saat ini yang masih berada di dalam range tersebut yaitu lebih besar sama dengan 0.1.2
dan lebih kecil dari 2.0.0.

Okay sebagai contoh kita akan menambahkan sebuah package provider yang nantinya akan
kita gunakan.
​ dependencies:
​ flutter:
​ sdk: flutter

​ cupertino_icons: ^0.1.2
​ provider: ^4.0.1
Yang perlu diperhatikan dalam menulis berkas .yaml adalah pada indentasinya. Indentasi
atau penggunaan spasi ini sangat penting karena menunjukkan urutan dan blok kode yaml
yang dibaca oleh komputer. Sebagai contoh, ketika kita menambahkan package provider,
maka kita harus menuliskannya sejajar dengan package lainnya dan juga lebih menjorok ke
dalam jika dibandingkan dengan dependencies. Ini menunjukkan bahwa package seperti
cupertino_icons dan provider merupakan bagian dari dependencies yang akan ditambahkan.
Setiap jaraknya adalah 2 spasi, jika dependencies menempel pada ujung kiri, maka
cupertino_icons dan provider berjarak 2 spasi dari ujung kiri.

Setelah menambahkan package yang dibutuhkan, kita dapat melakukan get package
tersebut. Jika Anda menggunakan visual studio code cukup simpan berkas pubspec.yaml,
maka nanti akan secara otomatis mensinkronisasi pubspec tersebut. Atau, bisa dengan
menekan tombol seperti gambar unduh pada pojok kanan atas paling kiri.

Bila menggunakan Android Studio Anda cukup menekan tombol " Pub get" pada Android
Studio seperti berikut:
Kita juga bisa secara manual menggunakan terminal dengan menjalankan perintah flutter
pub get di dalam project tersebut. Setelah melakukan pub get, maka package tersebut
sudah dapat digunakan.

Tempat-tempat mencari package flutter


Flutter memiliki segudang package yang telah dibuat oleh komunitas, lantas di mana kita
dapat mencari package-package untuk kita gunakan? Berikut website-website yang dapat
Anda gunakan untuk mencari package.

● https://pub.dev
Website ini merupakan web official untuk Anda mencari package.

● https://flutterawesome.com
Berisi package-package yang dibuat oleh komunitas, di sini banyak sekali package
UI keren yang dapat Anda coba.
Private Packages
Selain menggunakan package yang ada pada pub.dev Anda juga bisa menggunakan
package yang tidak dipublikasikan pada pub.dev tersebut dengan cara menggunakan url git
package tersebut:

​ dependencies:
​ plugin1:
​ git:
​ url: git://github.com/flutter/plugin1.git
Atau path direktori package tersebut yang tersimpan secara offline di komputer Anda.
​ dependencies:
​ plugin1:
​ path: ../plugin1/

Rangkuman Materi
Mari kita ingat kembali apa saja yang telah dipelajari di modul ini.

● Struktur project Flutter memiliki beberapa folder utama yang penting


untuk kita ketahui, yaitu:
○ android/
Folder berisi konfigurasi aplikasi untuk platform Android.
○ ios/
Folder berisi konfigurasi aplikasi untuk platform iOS.
○ build/
Folder berisi hasil build dari aplikasi Flutter, misalnya seperti
file APK, IPA, atau folder web yang bisa di-host.
○ lib/
Folder utama untuk mengerjakan project Flutter. Seluruh
source code Flutter berada pada folder ini.
○ test/
Folder berisi source code untuk melakukan testing aplikasi
Flutter secara otomatis.
● Ketika pertama kali membuat project Flutter, kita akan diberikan
aplikasi starter bertema counter. Kode starter ini memiliki contoh dan
komentar yang menjelaskan bagaimana sebuah aplikasi Flutter
disusun.
● Flutter memiliki package manager bernama pub. Dengan package
manager, kita dapat menambahkan package/library yang dibuat oleh
developer lain ke dalam project kita.

Pada modul selanjutnya, kita akan mulai berkenalan dengan widget dan
menyusunnya menjadi sebuah tampilan halaman. Sudah siap? Sampai
bertemu di modul selanjutnya!
Pengenalan Widget

Apa itu Widget


Kita telah berkenalan, menginstal, dan belajar fundamental Flutter. Itu
entrée alias hidangan pembukanya. Nah, sekarang kita akan menyibak ke
plat du jour alias menu utamanya. Apa inti dari Flutter? Yap, widget
jawabnya! Sebagian besar yang ada pada Flutter adalah widget. Jadi,
relevan jika kita bilang bahwa “Flutter is all about widget.” Text sendiri
adalah widget. Button juga widget. Selain itu widget juga dapat dibangun
dari kumpulan beberapa widget. Lantas mengapa widget begitu penting
dalam flutter? Penasaran? Mari kita bahas!

World of components
Perlu kita ketahui bahwa konsep Widget pada Flutter itu terinspirasi oleh
salah satu framework JavaScript yang digunakan untuk membangun
sebuah website yaitu ReactJS. ReactJS memiliki konsep Component. Mari
kita analogikan dengan mainan Lego! Di Lego terdapat block-block kecil
yang nantinya kita susun untuk membangun sebuah istana Lego. Berarti
component dalam programming adalah sekumpulan block-block code yang
digunakan untuk membangun sebuah aplikasi.

Widget sama halnya dengan component yang merupakan kumpulan block


code untuk membangun aplikasi Flutter. Ketika membangun aplikasi Flutter
kita harus berpikir layaknya bermain Lego. Kita harus bisa membuat dan
menyusun widget-widget dengan tepat. Tujuannya, agar aplikasi yang kita
buat lebih mudah untuk dikembangkan.
Bagaimana cara menulis widget
Sebetulnya pada pembahasan sebelumnya secara tidak sadar kita telah
membuat sebuah widget dan menggunakan widget yang telah disediakan.

​ class MyApp extends StatelessWidget {


​ const MyApp({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return MaterialApp(
​ title: 'Flutter Demo',
​ theme: ThemeData(
​ primarySwatch: Colors.blue,
​ ),
​ home: Scaffold(
​ appBar: AppBar(
​ title: const Text('Hello, world!'),
​ ),
​ body: const Center(
​ child: Text('Hello, world!'),
​ ),
​ ),
​ );
​ }
​ }
Pada kode di atas kita telah membuat sebuah Widget MyApp dan telah
menggunakan widget-widget bawaan Flutter di antaranya MaterialApp,
Scaffold, Center, dan Text. Ketika menggunakan widget, kita tinggal
panggil nama widget dan bila ada properti atau parameter pada widget
tersebut tinggal kita isikan properti atau parameternya.

​ Center(
​ child: Text('Hello world!'),
​ )
Kode di atas merupakan contoh pemanggilan widget Center. Widget
Center ini digunakan untuk membuat widget yang ada di dalamnya berada
di posisi tengah (mirip seperti alignment center). Tinggal ketikkan Center
lalu tambahkan properti child di dalamnya.

Perlu diketahui bahwa sebagian besar widget bawaan memiliki pola


parent-child, seperti halnya Center yang memiliki child yang artinya di
dalam child bisa terdapat widget lagi. Maka penulisan parent child akan
seperti di bawah ini.

​ Center( // parent dari Button


​ child: TextButton( // child dari Center dan parent dari Text
​ child: Text(), // child dari TextButton
​ ),
​ )
Pada contoh di atas widget Center dan Button hanya dapat memiliki satu
anak atau bisa disebut child. Ada pula widget yang dapat memiliki banyak
anak atau bisa disebut children, seperti Row, Column, ListView, GridView,
dan semacamnya.

​ Row(
​ children: <Widget>[
​ //di dalam children akan berisi banyak widget
​ ]
​ )
Contoh di atas adalah widget Row yang memiliki children. Di dalam
children nantinya kita bisa menambahkan banyak widget. Berbeda dengan
child yang diisi langsung dengan sebuah Widget, children akan berisi
sebuah list yang di dalamnya diisi dengan banyak widget.

StatelessWidget dan StatefulWidget


Seperti yang kita tahu jantung dari aplikasi Flutter adalah widget. Sebagian
besar yang ada pada Flutter merupakan widget. Membuat tombol,
menampilkan gambar, text, dan membuat tampilan berada di tengah pada
Flutter semuanya menggunakan widget. Kita juga dapat membuat widget
sendiri untuk dapat digunakan lain waktu ataupun dibagikan kepada Flutter
developer lain (dalam bentuk packages).

Widget pada Flutter memiliki dua jenis, yaitu StatelessWidget dan


StatefulWidget. Sebagai developer Flutter, kita harus memahami betul
kedua jenis widget tersebut, maka pada bagian ini kita akan mempelajari
apa itu StatelessWidget dan StatefulWidget.

Apa itu State?


Sebelum membahas kedua jenis widget tersebut, kita harus berkenalan
terlebih dahulu dengan istilah State. Kenapa demikian? Widget kita akan
terus berurusan dengan State. Lalu apa itu State?

Untuk teman-teman dengan background frontend web developer, tentu tak


akan asing dengan istilah State ini, terutama menggunakan framework
ReactJS. Tetapi untuk Anda tanpa background tersebut tidak perlu risau.
State tidaklah sulit untuk dimengerti. Jadi State adalah data yang ada pada
suatu widget. Widget menyimpan data yang nantinya dapat berubah sesuai
interaksi pengguna.

Karena Flutter menggunakan paradigma OOP (Object-Oriented


Programming), state biasanya menjadi suatu properti dari sebuah class.
Contohnya sebagai berikut:

​ class ContohWidget extends StatelessWidget{


​ final String _judul;
​ ...
​ }
Variabel _judul di atas merupakan contoh pendeklarasian state pada suatu
widget.

StatelessWidget
Setelah mengenal apa itu state, maka yang pertama kita akan bahas
adalah StatelessWidget. StatelessWidget adalah widget yang nilai
state-nya tidak dapat berubah (immutable) maka widget tersebut lebih
bersifat statis dan memiliki interaksi yang terbatas.

Sekarang kita akan membuat sebuah Widget sederhana:


​ class Heading extends StatelessWidget {

​ final String text;

​ const Heading({Key? key, required this.text}) : super(key:
key);

​ @override
​ Widget build(BuildContext context){
​ return Text(
​ text,
​ style: const TextStyle(
​ fontSize: 24.0,
​ fontWeight: FontWeight.bold,
​ ),
​ );
​ }
​ }
Widget di atas merupakan sebuah widget untuk membuat Heading (sebuah
text yang digunakan untuk judul). Kita akan panggil widget yang telah
diubah ke kode project pertama kita.

​ import 'package:flutter/material.dart';

​ void main() => runApp(MyApp());

​ class MyApp extends StatelessWidget {
​ const MyApp({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return MaterialApp(
​ title: 'Flutter Demo',
​ theme: ThemeData(
​ primarySwatch: Colors.blue,
​ ),
​ home: const Scaffold(
​ body: Center(
​ child: Text("Hello world!"),
​ ),
​ ),
​ );
​ }
​ }

​ class Heading extends StatelessWidget {
​ final String text;

​ const Heading({Key? key, required this.text}) : super(key:
key);

​ @override
​ Widget build(BuildContext context){
​ return Text(
​ text,
​ style: const TextStyle(
​ fontSize: 24.0,
​ fontWeight: FontWeight.bold,
​ ),
​ );
​ }
​ }
Kita coba ubah widget Text yang menampilkan "Hello world!" dengan
widget Heading yang kita buat.

​ import 'package:flutter/material.dart';

​ void main() => runApp(MyApp());

​ class MyApp extends StatelessWidget {
​ @override
​ Widget build(BuildContext context) {
​ return MaterialApp(
​ title: 'Flutter Demo',
​ theme: ThemeData(
​ primarySwatch: Colors.blue,
​ ),
​ home: const Scaffold(
​ body: Center(
​ child: Heading( // mengubah widget Text
​ text:"Hello world!",
​ ),
​ ),
​ ),
​ );
​ }
​ }

​ class Heading extends StatelessWidget {
​ final String text;

​ const Heading({Key? key, required this.text}) : super(key:
key);

​ @override
​ Widget build(BuildContext context){
​ return Text(
​ text,
​ style: const TextStyle(
​ fontSize: 24.0,
​ fontWeight: FontWeight.bold,
​ ),
​ );
​ }
​ }
Maka ketika kita ubah Text dengan widget Heading, hasilnya akan
berubah. Tulisan "Hello world!" jadi lebih besar.
Sesuai definisi StatelessWidget, state-nya tidak dapat berubah
(immutable), maka state yang ada di dalam kelas tersebut harus dibuat
final. Nilainya hanya dapat diisi melalui constructor class-nya.

​ final String text; // state text bersifat final



​ const Heading({Key? key, required this.text}) : super(key:
key); // lalu state text masuk ke constructor

StatefulWidget
Kebalikan dari StatelessWidget, StatefulWidget ialah widget yang state-nya
dapat berubah-ubah nilainya, yang berarti StatefulWidget bersifat dinamis
dan memiliki interaksi yang tak terbatas.

Penulisan StatefulWidget sangat berbeda dengan StatelessWidget, berikut


penulisannya:

​ class ContohStateful extends StatefulWidget {



​ final String parameterWidget; // ini parameter widget

​ const ContohStateful({Key? key, required
this.parameterWidget}) : super(key: key);

​ @override
​ _ContohStatefulState createState() =>
_ContohStatefulState();
​ }

​ class _ContohStatefulState extends State<ContohStateful>{
​ String _dataState; // ini state dari Widget
ContohStateful

​ @override
​ Widget build(BuildContext context){
​ // isi sebuah widget
​ }
​ }
Contoh di atas adalah cara penulisan StatefulWidget. Seperti yang kita
lihat, penulisan StatefulWidget lebih panjang. Tetapi yang harus
diperhatikan adalah properti dari setiap class-nya. Pada class
ContohStateful propertinya hanya berupa parameter ketika memanggil
ContohStateful, parameter tersebut tidak wajib ada. Sedangkan pada class
_ContohStatefulState, properti _dataState merupakan state yang
sebenarnya. Kita akan mengubah nilai _dataState.

Misalnya kita coba membuat contoh StatefulWidget sederhana:

​ class BiggerText extends StatefulWidget {


​ final String text;

​ const BiggerText({Key? key, required this.text}) :
super(key: key);

​ @override
​ _BiggerTextState createState() => _BiggerTextState();
​ }


​ class _BiggerTextState extends State<BiggerText> {
​ double _textSize = 16.0;

​ @override
​ Widget build(BuildContext context) {
​ return Column(
​ mainAxisAlignment: MainAxisAlignment.center,
​ children: <Widget>[
​ Text(widget.text, style: TextStyle(fontSize:
_textSize)),
​ ElevatedButton(
​ child: const Text("Perbesar"),
​ onPressed: () {
​ setState(() {
​ _textSize = 32.0;
​ });
​ },
​ )
​ ],
​ );
​ }
​ }
Letakkan kode di atas setelah StatelessWidget Heading yang telah kita
buat sebelumnya lalu panggil StatefulWidget BiggerText pada MyApp.

​ import 'package:flutter/material.dart';

​ void main() => runApp(MyApp());

​ class MyApp extends StatelessWidget {
​ @override
​ Widget build(BuildContext context) {
​ return MaterialApp(
​ title: 'Flutter Demo',
​ theme: ThemeData(
​ primarySwatch: Colors.blue,
​ ),
​ home: const Scaffold(
​ body: Center(
​ child: BiggerText(text:"Hello world!"), // Ubah
widget Heading ke PerubahanText
​ ),
​ ),
​ );
​ }
​ }

​ class Heading extends StatelessWidget {
​ final String text;

​ const Heading({Key? key, required this.text}) : super(key:
key);

​ @override
​ Widget build(BuildContext context) {
​ return Text(
​ text,
​ style: const TextStyle(
​ fontSize: 24.0,
​ fontWeight: FontWeight.bold,
​ ),
​ );
​ }
​ }

​ class BiggerText extends StatefulWidget {
​ final String text;

​ const BiggerText({Key? key, required this.text}) : super(key:
key);

​ @override
​ _BiggerTextState createState() => _BiggerTextState();
​ }

​ class _BiggerTextState extends State<BiggerText> {
​ double _textSize = 16.0;

​ @override
​ Widget build(BuildContext context) {
​ return Column(
​ mainAxisAlignment: MainAxisAlignment.center,
​ children: <Widget>[
​ Text(widget.text, style: TextStyle(fontSize:
_textSize)),
​ ElevatedButton(
​ child: const Text("Perbesar"),
​ onPressed: () {
​ setState(() {
​ _textSize = 32.0;
​ });
​ },
​ )
​ ],
​ );
​ }
​ }
Maka hasilnya akan seperti berikut:
Ketika tombol "Perbesar" ditekan, text "Hello world!" akan membesar
karena state _textSize diubah nilainya. Mengubah nilai state harus
dilakukan pada fungsi setState seperti berikut:

​ setState(() {
​ _textSize = 32.0; // ukuran text diubah menjadi 32
​ });
Anda dapat memahami lebih dalam terkait Stateless dan Stateful Widget
dengan membaca dokumentasi berikut ini:

● StatelessWidget Class
● StatefulWidget Class

Widget - Widget Umum


Setelah sebelumnya kita belajar mengenai StatelessWidget dan
StatefulWidget, kali ini kita akan belajar widget-widget yang umum
digunakan dalam pengembangan aplikasi Flutter. Dengan widget-widget
tersebut, kita akan belajar bagaimana membuat sebuah tampilan (material
design), layout sederhana, button, serta input control.

Untuk mencoba widget-widget umum tersebut, buatlah project baru lalu


pada file lib/main.dart buatlah kode seperti yang ada pada pembahasan
Aplikasi Hello World.

Scaffold
Scaffold merupakan sebuah widget yang digunakan untuk membuat
tampilan dasar material design pada aplikasi Flutter, yang dapat disebut
juga dasar sebuah halaman pada aplikasi Flutter. Tampilan dasar tersebut
seperti berikut:
Tampilan di atas merupakan implementasi dari Scaffold. Scaffold di atas
memiliki 3 bagian yaitu AppBar, Body, dan FloatingActionButton. Ketiga
bagian tersebut diilustrasikan seperti berikut:
Pada gambar di atas kotak berwarna merah merupakan AppBar; kotak
berwarna hijau merupakan body; dan kotak berwarna biru merupakan
FloatingActionButton.

Untuk membuat sebuah Scaffold kita hanya cukup memanggil class


Scaffold seperti berikut:

​ class FirstScreen extends StatelessWidget {


​ const FirstScreen({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold();
​ }
​ }
Pada kode di atas kita membuat sebuah StatelessWidget bernama
FirstScreen, yang merupakan widget tampilan kita. Kemudian di dalam
method build kita panggil Scaffold.
Jangan lupa untuk memanggil FirstScreen pada Widget MyApp seperti
berikut:

​ import 'package:flutter/material.dart';

​ void main() => runApp(const MyApp());

​ class MyApp extends StatelessWidget {
​ const MyApp({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return MaterialApp(
​ title: 'Flutter Demo',
​ theme: ThemeData(
​ primarySwatch: Colors.blue,
​ ),
​ home: const FirstScreen(),// Panggil FirstScreen di sini

​ );
​ }
​ }

​ class FirstScreen extends StatelessWidget {
​ const FirstScreen({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold();
​ }
​ }
Ketika kita menjalankan aplikasi Flutter, pada layar akan hanya muncul
tampilan berwarna putih.
AppBar
Setelah kita membuat Scaffold pertama kita pada Widget FirstScreen,
sekarang kita akan menambahkan AppBar pada Scaffold. Seperti yang kita
tahu AppBar merupakan Header (bagian paling atas) aplikasi atau biasa
dikenal dengan toolbar. Pada AppBar umumnya terdapat judul dan
ActionButton.

Berikut adalah cara menambahkan AppBar pada Scaffold:

​ class FirstScreen extends StatelessWidget {


​ const FirstScreen({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ appBar: AppBar(
​ title: const Text('First Screen'),
​ ),
​ );
​ }
​ }
Pada kode di atas kita menambahkan parameter appBar pada Scaffold dan
menambahkan title pada AppBar tersebut. Title di sini tidak hanya spesifik
Text saja, melainkan juga dapat diisi dengan widget lainnya seperti
TextField untuk kolom pencarian atau yang lainnya. Setelah menambahkan
kode di atas, coba refresh atau hot reload aplikasi Flutter Anda. Selain
menambahkan title kita dapat menambahkan widget-widget actions seperti
pada kode berikut:

​ class FirstScreen extends StatelessWidget {


​ const FirstScreen({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ appBar: AppBar(
​ title: const Text('First Screen'),
​ actions: <Widget>[
​ IconButton(
​ icon: const Icon(
​ Icons.search,
​ color: Colors.white,
​ ),
​ onPressed: () {},
​ ),
​ ],
​ ),
​ );
​ }
​ }
Pada kode di atas kita menambahkan Icon search pada bagian kanan
AppBar. Lalu kita juga dapat menambahkan action pada bagian kiri AppBar
misalnya untuk tombol yang menampilkan menu (drawer), seperti pada
kode berikut:

​ class FirstScreen extends StatelessWidget {


​ const FirstScreen({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ appBar: AppBar(
​ title: const Text('First Screen'),
​ actions: [
​ IconButton(
​ icon: const Icon(
​ Icons.search,
​ color: Colors.white,
​ ),
​ onPressed: () {},
​ ),
​ ],
​ leading: IconButton(
​ icon: const Icon(
​ Icons.menu,
​ color: Colors.white,
​ ),
​ onPressed: () {},
​ ),
​ ),
​ );
​ }
​ }
Tidak seperti pada actions, leading hanya dapat menampung satu widget
saja. Secara default, leading akan berisi tombol untuk kembali ke halaman
sebelumnya (jika tersedia), atau tombol untuk menu drawer (jika kita
mengatur untuk drawer pada Scaffold tersebut). Untuk melihat hasilnya
lakukan refresh atau hot reload pada aplikasi Flutter Anda.
Body
Setelah menambahkan AppBar kita akan menambahkan body. Seperti
pada ilustrasi sebelumnya, body merupakan bagian utama dari Scaffold
dan kita akan banyak menuliskan kode pada bagian body ini. Untuk
implementasi body kita akan menambahkan parameter body pada Scaffold
seperti berikut:

​ class FirstScreen extends StatelessWidget {


​ const FirstScreen({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ appBar: AppBar(
​ title: const Text('First Screen'),
​ actions: [
​ IconButton(
​ icon: const Icon(
​ Icons.search,
​ color: Colors.white,
​ ),
​ onPressed: () {},
​ ),
​ ],
​ leading: IconButton(
​ icon: const Icon(
​ Icons.menu,
​ color: Colors.white,
​ ),
​ onPressed: () {},
​ ),
​ ),
​ body: const Center(
​ child: Text('Hello world!'),
​ ),
​ );
​ }
​ }
Pada kode di atas kita telah menambahkan body yang di dalamnya kita
memanggil widget Center yang akan menampilkan Text "Hello World!".
FloatingActionButton
Selanjutnya, kita akan menambahkan sebuah tombol bulat pada bagian
kanan bawah seperti ilustrasi sebelumnya yaitu FloatingActionButton.
FloatingActionButton ini merupakan bagian dari Scaffold yang digunakan
untuk menampilkan sebuah tombol aksi yang posisinya floating (melayang
dan posisinya tetap). Untuk menggunakan FloatingActionButton
tambahkan kode Anda seperti berikut:

​ class FirstScreen extends StatelessWidget {


​ const FirstScreen({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ appBar: AppBar(
​ title: const Text('First Screen'),
​ actions: [
​ IconButton(
​ icon: const Icon(
​ Icons.search,
​ color: Colors.white,
​ ),
​ onPressed: () {},
​ ),
​ ],
​ leading: IconButton(
​ icon: const Icon(
​ Icons.menu,
​ color: Colors.white,
​ ),
​ onPressed: () {},
​ ),
​ ),
​ body: const Center(
​ child: Text('Hello world!'),
​ ),
​ floatingActionButton: FloatingActionButton(
​ child: const Icon(Icons.add),
​ onPressed: () {},
​ ),
​ );
​ }
​ }

Hasil Akhir
Setelah kita menambahkan AppBar, body, dan FloatingActionButton maka
hasil akhirnya akan seperti berikut:
Untuk memahami Scaffold lebih dalam, Anda bisa membaca tautan berikut:

● Scaffold Class
● Scaffold Sample Apps

Pengenalan Container
Bagaimana sejauh ini? Semoga materinya dapat Anda praktikkan dengan
mulus ya.

Pada bagian sebelumnya kita mempelajari widget Scaffold. Kini saatnya


belajar tentang widget Container. Container adalah widget yang digunakan
untuk melakukan styling, membuat sebuah shape (bentuk), dan layout
pada widget child-nya. Sebagai contoh:

​ Container(
​ color: Colors.blue,
​ child: const Text(
​ 'Hi',
​ style: TextStyle(fontSize: 40),
​ ),
​ ),
Pada kode di atas kita membuat sebuah Text "Hi" yang dibungkus oleh
widget Container dan kita beri parameter color dengan nilai Colors.blue.
Kita letakkan Container di dalam parameter body. Apa hasilnya? Text "Hi"
akan memiliki background berwarna biru. Jalankan project Anda untuk
menampilkan hasil seperti berikut:

Width & Height


Kita dapat mengatur lebar (width) dan tinggi (height) suatu Container
seperti berikut:
​ Container(
​ color: Colors.blue,
​ width: 200,
​ height: 100,
​ child: const Text(
​ 'Hi',
​ style: TextStyle(fontSize: 40),
​ ),
​ ),
Kode di atas ketika dijalankan hasilnya akan seperti berikut:

Padding & Margin


Container menyediakan padding & margin. Padding merupakan jarak
antara konten (child) dengan Container, sedangkan margin merupakan
jarak antara Container dengan bagian luar container.
Penggunaan padding adalah seperti berikut:

​ Container(
​ color: Colors.blue,
​ padding: const EdgeInsets.all(10),
​ child: const Text(
​ 'Hi',
​ style: TextStyle(fontSize: 40),
​ ),
​ ),
Pada kode di atas kita menambahkan padding pada semua sisi container
secara merata dengan nilai 10. Maka jika me-refresh aplikasi flutter, akan
ada jarak antara Text "Hi" dengan batas (border) dari container.
Lalu penggunaan margin pun sama seperti halnya padding, maka contoh
kodenya seperti berikut:

​ Container(
​ color: Colors.blue,
​ margin: const EdgeInsets.all(10),
​ child: const Text(
​ 'Hi',
​ style: TextStyle(fontSize: 40),
​ ),
​ ),
Maka hasil dari kode di atas Container akan bergeser lebih ke dalam
karena ada jarak antara Container dengan bagian luar Container.
Dekorasi Container
Decoration merupakan bagian dari Container untuk styling. Pada
decoration kita dapat menentukan warna background (solid/gradient color),
shadow, border, border radius (membulatkan sudut), mengatur shape
(bentuk), dan lain-lain.

Color
Contoh menentukan warna background dari container dengan decoration
seperti berikut:

​ Container(
​ decoration: BoxDecoration(
​ color: Colors.red,
​ ),
​ child: const Text(
​ 'Hi',
​ style: TextStyle(fontSize: 40),
​ ),
​ ),
Ketika dijalankan maka tampilan aplikasi akan seperti berikut:
Untuk menggunakan decoration cukup menambahkan parameter
decoration pada Container lalu beri nilai BoxDecoration. Pada contoh di
atas kita merubah warna Container menjadi merah dengan memberi
parameter color pada BoxDecoration. Ada catatan penting ketika
menggunakan color pada BoxDecoration, yaitu pastikan tidak memberi
parameter color pada Container.

Shape
Contoh selanjutnya pada decoration adalah kita akan mengatur shape
(bentuk) dari Container, contohnya sebagai berikut:

​ Container(
​ decoration: BoxDecoration(
​ color: Colors.red,
​ shape: BoxShape.circle,
​ ),
​ child: const Text(
​ 'Hi',
​ style: TextStyle(fontSize: 40),
​ ),
​ ),
Pada kode di atas kita menambahkan parameter shape dengan nilai
BoxShape.circle. Artinya, bentuk dari Container tersebut akan berbentuk
lingkaran. BoxShape memiliki opsi circle atau rectangle.

Shadow
Untuk menambahkan shadow pada Container kita akan menambahkan
parameter boxShadow pada BoxDecoration, seperti berikut:

​ Container(
​ decoration: BoxDecoration(
​ color: Colors.red,
​ boxShadow: const [
​ BoxShadow(
​ color: Colors.black,
​ offset: Offset(3, 6),
​ blurRadius: 10,
​ ),
​ ],
​ ),
​ child: const Text(
​ 'Hi',
​ style: TextStyle(fontSize: 40),
​ ),
​ ),
Pada kode di atas parameter boxShadow merupakan sebuah Array. Di
dalamnya terdapat BoxShadow yang artinya pada Container kita dapat
memberikan banyak bayangan atau shadow.
Border
Border merupakan batas garis dengan content (child). Begini cara
menambahkan border pada container:

​ Container(
​ decoration: BoxDecoration(
​ color: Colors.red,
​ border: Border.all(color: Colors.green, width: 3),
​ ),
​ child: const Text(
​ 'Hi',
​ style: TextStyle(fontSize: 40),
​ ),
​ ),
Apabila Anda ingin membuat border yang tidak berujung lancip cukup
tambahkan parameter borderRadius Pada BoxDecoration seperti berikut:

​ Container(
​ decoration: BoxDecoration(
​ color: Colors.red,
​ border: Border.all(color: Colors.green,width: 3),
​ borderRadius: BorderRadius.circular(10),
​ ),
​ child: const Text(
​ 'Hi',
​ style: TextStyle(fontSize: 40),
​ ),
​ ),
Kesimpulan
Dengan menggunakan Widget Container kita dapat membuat variasi
widget yang kita buat. Sebenarnya banyak sekali parameter-parameter
yang dapat digunakan pada Container dan juga pada BoxDecoration. Anda
dapat mengeksplorasi hal tersebut dengan mencarinya di Google atau
pada dokumentasi resmi flutter.

● Container Class

Padding
Sebelumnya kita telah belajar banyak hal mengenai Container. Kali ini kita
akan belajar widget Padding. Seperti namanya widget Padding merupakan
sebuah widget yang khusus untuk memberikan padding pada suatu widget.

Contoh penggunaan widget Padding seperti berikut:

​ Padding(
​ padding: const EdgeInsets.all(30),
​ child: const Text('Ini Padding')
​ )
Pada kode di atas widget Padding harus memiliki child. Child di sini
merupakan sebuah widget yang nantinya akan diberi padding. Parameter
padding ditambahkan untuk menentukan besaran padding yang diinginkan.

Anda dapat membaca informasi detail tentang Padding pada dokumentasi


berikut:

● Padding Class

Center
Widget Center merupakan sebuah widget yang digunakan untuk membuat suatu widget
berada pada posisi tengah. Penggunaan widget Center sangatlah simpel, yakni seperti
berikut:

​ Center(
​ child: const Text('Text berada di tengah'),
​ )
Widget Center hanya membutuhkan parameter child untuk membuat
widget di dalamnya berada pada posisi tengah. Hasil dari Center seperti
berikut:

Row dan Column


Selanjutnya kita akan mempelajari bagaimana cara membuat widget yang
kita gunakan berjajar secara vertikal atau horizontal. Lalu apa yang
dimaksud dengan membuat widget yang berjajar? Perhatikan gambar
berikut:
Pada gambar di atas kita memiliki tampilan ikon-ikon yang merupakan
kumpulan tombol, di antaranya share, like dan dislike. Tombol-tombol
tersebut tersusun berjajar secara horizontal. Nah, untuk membuat berjajar
horizontal atau membentuk baris kita menggunakan widget Row.
Sedangkan untuk menyusun widget yang membentuk kolom atau vertikal,
kita bisa menggunakan widget Column.

Widget Row
Seperti yang dicontohkan sebelumnya, widget Row merupakan suatu
widget yang digunakan untuk membuat widget-widget tersusun berjajar
secara horizontal. Row memiliki sintaks seperti berikut:

​ Row(
​ children: <Widget>[
​ //di sini berisi widget-widget
​ ],
​ )
Untuk membuat widget-widget berjajar secara horizontal kita harus
memasukkan widget-widget tersebut ke dalam parameter children.
Parameter children berisi kumpulan atau list dari widget karena kita dapat
menyusun beberapa widget sekaligus di dalamnya. Jika mengacu pada
contoh tombol-tombol di atas kodenya seperti berikut:

​ Row(
​ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
​ children: const <Widget>[
​ Icon(Icons.share),
​ Icon(Icons.thumb_up),
​ Icon(Icons.thumb_down),
​ ],
​ )
Seperti yang kita lihat, kita membuat sebuah IconButton berada di dalam
parameter children. Kita menambahkan pula mainAxisAlignment yang
merupakan parameter alignment pada Row. Parameter mainAxisAlignment
yang berfungsi untuk mengatur alignment vertikal dari Row (alignment
utama). Selain itu Row juga memiliki parameter crossAxisAlignment yang
berfungsi untuk mengatur alignment secara horizontal. Kedua parameter ini
juga berlaku sebaliknya untuk widget Column.
Berikut ini adalah contoh penerapan mainAxisAlignment pada Row:
Widget Column
Kebalikan dari Row, Column merupakan suatu widget yang digunakan
untuk membuat widget-widget tersusun berjajar secara vertikal. Column
memiliki sintaks mirip dengan Row, seperti berikut:

​ Column(
​ children: <Widget>[
​ //di sini berisi widget-widget
​ ]
​ )
Contoh penerapan Column seperti berikut:
​ Column(
​ children: const <Widget>[
​ Text(
​ 'Sebuah Judul',
​ style: TextStyle(fontSize: 32, fontWeight:
FontWeight.bold),
​ ),
​ Text('Lorem ipsum dolor sit amet'),
​ ],
​ )
Maka akan menghasilkan tampilan seperti berikut:

Kesimpulan
Untuk membuat sebuah widget-widget berjajar kita dapat menggunakan
widget Row atau Column. Sebenarnya penggunaan Row dan Column
dapat dipadukan sehingga dapat membuat sebuah layout yang kompleks
seperti berikut:

Untuk memahami Row, Column, dan bagaimana menyusun layout dengan


Flutter secara mendalam, silakan pelajari dokumentasi berikut:

● Row Class
● Column Class
● Layouts in Flutter
Codelab 1
Pada kelas ini kita akan mengembangkan sebuah aplikasi yang
menampilkan tempat-tempat wisata di Bandung. Hasil akhir dari
keseluruhan codelab akan seperti berikut:

Dalam codelab pertama ini kita akan membuat sebuah tampilan yang
menggabungkan semua widget-widget yang sebelumnya kita pelajari.
Tampilannya adalah seperti berikut:
Sebelum kita membuat tampilan di atas, kita akan bedah terlebih dahulu
layout-nya.

Pada layout di atas kita dapat memetakan widget-widget dalam bentuk


diagram seperti di bawah ini:
1. Buat project Flutter baru dan berikan nama yang sesuai, misalnya
wisatabandung. Hapus kode aplikasi counter yang diberikan ketika
project dibuat.
2. Tuliskan kode dasar yang menampilkan widget MaterialAppseperti
berikut:
​ import 'package:flutter/material.dart';

​ void main() => runApp(const MyApp());

​ class MyApp extends StatelessWidget {
​ const MyApp({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return MaterialApp(
​ title: 'Wisata Bandung',
​ theme: ThemeData(),
​ );
​ }
​ }
3. Lalu kita akan membuat kode untuk susunan widget sesuai diagram
yang telah kita buat. Untuk membuat kode kita lebih rapi kita akan
membuat kelas Stateless Widget baru untuk menampung kode
tampilan kita. Mari namakan kelas ini DetailScreen.
​ class DetailScreen extends StatelessWidget {
​ const DetailScreen({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold();
​ }
​ }
4. Jangan lupa untuk menambahkan widget DetailScreen sebagai
home dari MaterialApp.
​ class MyApp extends StatelessWidget {
​ const MyApp({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return MaterialApp(
​ title: 'Wisata Bandung',
​ theme: ThemeData(),
​ home: const DetailScreen(),
​ );
​ }
​ }
5. Sesuai diagram di atas, kita akan menyusun beberapa widget secara
vertikal sehingga kita perlu menggunakan widget Column.
​ class DetailScreen extends StatelessWidget {
​ const DetailScreen({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ body: Column(),
​ );
​ }
​ }
6. Jalankan aplikasi Anda. Saat ini device atau emulator Anda memang
masih belum menampilkan apa pun. Namun, kita akan
memanfaatkan fitur hot reload untuk melihat perubahan-perubahan
yang akan kita lakukan ke depan.
7. Komponen pertama yang akan kita buat adalah bagian judul dari
halaman. Tentunya untuk menampilkan teks kita akan menggunakan
widget Text.
​ class DetailScreen extends StatelessWidget {
​ const DetailScreen({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ body: Column(
​ children: <Widget>[
​ const Text('Farm House Lembang'),
​ ],
​ ),
​ );
​ }
​ }
8. Ketika Anda menyimpan project atau menjalankan hot reload,
tampilan aplikasi Anda sekarang mungkin tidak sesuai dengan
keinginan, seperti teks terlalu ke atas dan juga terlalu kecil.

Untuk itulah kita perlu membungkus widget Text ke dalam Container


supaya kita dapat memberikan property seperti margin atau padding.
Jika Anda menggunakan IDE Android Studio, Anda dapat
memanfaatkan shortcut Alt+Enter untuk membungkus widget ke
widget lain.

9. Tambahkan margin atas supaya teks memiliki jarak terhadap bagian


atas layar.
​ class DetailScreen extends StatelessWidget {
​ const DetailScreen({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ body: Column(
​ children: <Widget>[
​ Container(
​ margin: const EdgeInsets.only(top: 16.0),
​ child: const Text('Farm House Lembang'),
​ ),
​ ],
​ ),
​ );
​ }
​ }
10. Pada kode di atas kita hanya memberikan margin atas sebesar
sebesar 16.0. Anda dapat memanfaatkan metode EdgeInsets lain
seperti all() untuk memberikan margin ke semua sisi atau
symmetric() apabila Anda ingin memberikan margin ke sisi vertikal
atau horizontal.
11.Jika Anda kesulitan menentukan margin atas, khususnya pada
perangkat yang memiliki notch yang umumnya memiliki status bar
yang lebih besar, Anda dapat memanfaatkan widget SafeArea.
​ class DetailScreen extends StatelessWidget {
​ const DetailScreen({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ body: SafeArea(
​ child: Column(
​ children: <Widget>[
​ Container(
​ margin: const EdgeInsets.only(top: 16.0),
​ child: const Text('Farm House Lembang'),
​ )
​ ],
​ ),
​ ),
​ );
​ }
​ }
12. Widget ini akan memberikan padding yang secara otomatis
menyesuaikan perangkat yang digunakan.

13. Selanjutnya, sesuai contoh kita akan membuat teks judul berada
di tengah. Tambahkan parameter atau properti textAlign pada widget
Text. Selain itu, tambahkan juga style dengan memperbesar ukuran
teks agar tulisan dapat dibaca.
​ Container(
​ margin: EdgeInsets.only(top: 16.0),
​ child: const Text(
​ 'Farm House Lembang',
​ textAlign: TextAlign.center,
​ style: TextStyle(
​ fontSize: 30.0,
​ fontWeight: FontWeight.bold,
​ ),
​ ),
​ ),
14. Lakukan hot reload. Tidak ada perubahan, apa sebabnya? Jika
menggunakan Android Studio Anda dapat memanfaatkan fitur Flutter
Inspector untuk melihat layout widget di dalam aplikasi.
Dari gambar di atas bisa kita lihat ternyata layout aplikasi kita tidak
penuh hingga seluruh halaman. Ini disebabkan sisi horizontal dari
Column hanya menyesuaikan dengan konten yang ada di dalamnya.
Untuk memaksimalkan ukuran lebar dari Column, tambahkan kode
berikut:

​ body: SafeArea(
​ child: Column(
​ crossAxisAlignment: CrossAxisAlignment.stretch,
​ children: <Widget>[
​ Container(
​ margin: EdgeInsets.only(top: 16.0),
​ child: Text(
​ 'Farm House Lembang',
​ textAlign: TextAlign.center,
​ style: TextStyle(
​ fontSize: 30.0,
​ fontWeight: FontWeight.bold,
​ ),
​ ),
​ ),
​ ],
​ ),
​ ),
15. Setelah menyelesaikan judul, selanjutnya kita akan membuat
bagian kedua yaitu informasi dari tempat wisata.

Seperti yang terlihat kita perlu menyusun widget secara horizontal


dan vertikal. Mari tambahkan child kedua dari Column dengan
sebuah Container berisi Row. Tambahkan juga margin pada sisi atas
dan bawah untuk memberikan jarak antar widget.

​ class DetailScreen extends StatelessWidget {


​ const DetailScreen({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ body: SafeArea(
​ child: Column(
​ crossAxisAlignment:
CrossAxisAlignment.stretch,
​ children: <Widget>[
​ Container(...),
​ Container(
​ margin: const EdgeInsets.symmetric(vertical:
16.0),
​ child: Row(
​ children: <Widget>[],
​ ),
​ ),
​ ],
​ ),
​ ),
​ );
​ }
​ }
16. Buat widget Column untuk menyusun Icon dan Text.
​ class DetailScreen extends StatelessWidget {
​ const DetailScreen({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ body: SafeArea(
​ child: Column(
​ crossAxisAlignment:
CrossAxisAlignment.stretch,
​ children: <Widget>[
​ Container(...),
​ Container(
​ margin: EdgeInsets.symmetric(vertical:
16.0),
​ child: Row(
​ children: <Widget>[
​ Column(
​ children: const <Widget>[
​ Icon(Icons.calendar_today),
​ Text('Open Everyday'),
​ ],
​ ),
​ ],
​ ),
​ ),
​ ],
​ ),
​ ),
​ );
​ }
​ }
17. Jika Anda merasa jarak antara Icon dan Text terlalu rapat, Anda
dapat menambahkan widget SizedBoxuntuk membuat “kotak” yang
berguna untuk memberikan jarak.
​ Column(
​ children: const <Widget>[
​ Icon(Icons.calendar_today),
​ SizedBox(height: 8.0),
​ Text('Open Everyday'),
​ ],
​ ),
18. Selanjutnya sebagai tantangan, lengkapilah informasi tempat
wisata dengan pasangan ikon dan teks sesuai contoh yang
diberikan.

19. Untuk menyusun Row seperti di atas, pastikan menggunakan


mainAxisAlignment seperti ini:
​ class DetailScreen extends StatelessWidget {
​ const DetailScreen({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ body: SafeArea(
​ child: Column(
​ crossAxisAlignment:
CrossAxisAlignment.stretch,
​ children: <Widget>[
​ Container(...),
​ Container(
​ margin: const
EdgeInsets.symmetric(vertical: 16.0),
​ child: Row(
​ mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
​ children: <Widget>[
​ Column(...),
​ Column(...),
​ Column(...)
​ ],
​ ),
​ ),
​ ],
​ ),
​ ),
​ );
​ }
​ }
20. Pada langkah ini harusnya Anda sudah bisa menampilkan teks
deskripsi sesuai langkah yang diberikan sebelumnya. Anda cukup
menambahkan widget Container dan Textuntuk menampilkan konten
deskripsi. Anda juga dapat menambahkan style sesuai selera Anda.
​ Container(
​ padding: const EdgeInsets.all(16.0),
​ child: const Text(
​ 'Berada di jalur utama Bandung-Lembang, Farm House
menjadi objek wisata yang tidak pernah sepi pengunjung.
Selain karena letaknya strategis, kawasan ini juga
menghadirkan nuansa wisata khas Eropa. Semua itu
diterapkan dalam bentuk spot swafoto Instagramable.',
​ textAlign: TextAlign.center,
​ style: TextStyle(fontSize: 16.0),
​ ),
​ ),
21. Yey, Anda berhasil menyusun widget dengan baik. Tampilan
aplikasi akan seperti gambar berikut.
Keseluruhan kode Anda
akan seperti berikut:
​ import 'package:flutter/material.dart';

​ void main() => runApp(const MyApp());

​ class MyApp extends StatelessWidget {
​ const MyApp({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return MaterialApp(
​ title: 'Wisata Bandung',
​ theme: ThemeData(),
​ home: const DetailScreen(),
​ );
​ }
​ }

​ class DetailScreen extends StatelessWidget {
​ const DetailScreen({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ body: SafeArea(
​ child: Column(
​ crossAxisAlignment: CrossAxisAlignment.stretch,
​ children: <Widget>[
​ Container(
​ margin: const EdgeInsets.only(top: 16.0),
​ child: const Text(
​ 'Farm House Lembang',
​ textAlign: TextAlign.center,
​ style: TextStyle(
​ fontSize: 30.0,
​ fontWeight: FontWeight.bold,
​ ),
​ ),
​ ),
​ Container(
​ margin: const
EdgeInsets.symmetric(vertical: 16.0),
​ child: Row(
​ mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
​ children: <Widget>[
​ Column(
​ children: const <Widget>[
​ Icon(Icons.calendar_today),
​ SizedBox(height: 8.0),
​ Text('Open Everyday'),
​ ],
​ ),
​ Column(
​ children: const <Widget>[
​ Icon(Icons.access_time),
​ SizedBox(height: 8.0),
​ Text('09:00 - 20:00')
​ ],
​ ),
​ Column(
​ children: const <Widget>[
​ Icon(Icons.monetization_on),
​ SizedBox(height: 8.0),
​ Text('Rp 25.000'),
​ ],
​ )
​ ],
​ ),
​ ),
​ Container(
​ padding: const EdgeInsets.all(16.0),
​ child: const Text(
​ 'Berada di jalur utama Bandung-Lembang,
Farm House menjadi objek wisata yang tidak pernah sepi
pengunjung. Selain karena letaknya strategis, kawasan
ini juga menghadirkan nuansa wisata khas Eropa. Semua
itu diterapkan dalam bentuk spot swafoto
Instagramable.',
​ textAlign: TextAlign.center,
​ style: TextStyle(fontSize: 16.0),
​ ),
​ )
​ ],
​ ),
​ ),
​ );
​ }
​ }
Anda juga dapat mengunduh keseluruhan kodenya pada tautan berikut:

● Codelab-1

Button
Kali ini kita akan belajar menggunakan widget button. Widget button ini
adalah widget yang dapat menerima trigger sentuhan atau dapat
melakukan suatu fungsi ketika disentuh, widget-widget button tersebut
antara lain:

ElevatedButton
ElevatedButton merupakan bagian dari Material Design widget dari Flutter.
Untuk menggunakan ElevatedButton caranya seperti berikut:

​ ElevatedButton(
​ child: const Text("Tombol"),
​ onPressed: () {
​ // Aksi ketika button diklik
​ },
​ ),
Pada kode di atas ElevatedButton memiliki 2 parameter yaitu onPressed
dan child. Parameter onPressed merupakan sebuah function event ketika
tombol ditekan dan sebenarnya ada event lain seperti onLongPress dan
onHighlightChanged. Parameter child diisi oleh widget pada umumnya.
TextButton
TextButton merupakan widget button yang memiliki tampilan yang polos
selayaknya Text. TextButton umumnya digunakan pada toolbars, dialog,
atau bersama komponen button lain. Contoh kode dari TextButton adalah
seperti berikut:

​ TextButton(
​ child: const Text('Text Button'),
​ onPressed: () {
​ // Aksi ketika button diklik
​ },
​ ),
Sama halnya ElevatedButton, TextButton juga memiliki parameter
onPressed dan child.

OutlinedButton
OutlinedButton juga merupakan bagian dari material design yang
menyediakan tampilan TextButton dengan tambahan outline.
OutlinedButton umumnya digunakan untuk tombol atau aksi yang penting,
tetapi bukan aksi utama dalam aplikasi.

Berikut ini adalah contoh widget OutlinedButton:


​ OutlinedButton(
​ child: const Text('Outlined Button'),
​ onPressed: () {
​ // Aksi ketika button diklik
​ },
​ ),
Tampilan OutlinedButton sendiri akan seperti berikut:

IconButton
IconButton merupakan widget button dengan icon. Tak seperti widget
tombol lainnya, widget IconButton ini tidak memiliki child. Perhatikan kode
di bawah ini:
​ IconButton(
​ icon: const Icon(Icons.volume_up),
​ tooltip: 'Increase volume by 10',
​ onPressed: () {
​ // Aksi ketika button diklik
​ },
​ ),
Seperti yang kita lihat di atas, IconButton tidak menggunakan child untuk isi
(content) melainkan menggunakan parameter icon dan tooltip (penunjuk)
untuk memberikan hint pada tombol.

DropdownButton
DropdownButton merupakan tombol yang saat diklik, akan muncul pop-up
daftar beberapa item yang dapat kita pilih salah satu. Berikut contoh
kodenya:

​ class FirstScreen extends StatefulWidget {


​ const FirstScreen({Key? key}) : super(key: key);

​ @override
​ State<FirstScreen> createState() => _FirstScreenState();
​ }

​ class _FirstScreenState extends State<FirstScreen> {
​ String? language;

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ appBar: AppBar(
​ title: const Text('First Screen'),
​ ),
​ body: DropdownButton<String>(
​ items: const <DropdownMenuItem<String>>[
​ DropdownMenuItem<String>(
​ value: 'Dart',
​ child: Text('Dart'),
​ ),
​ DropdownMenuItem<String>(
​ value: 'Kotlin',
​ child: Text('Kotlin'),
​ ),
​ DropdownMenuItem<String>(
​ value: 'Swift',
​ child: Text('Swift'),
​ ),
​ ],
​ value: language,
​ hint: const Text('Select Language'),
​ onChanged: (String? value) {
​ setState(() {
​ language = value;
​ });
​ },
​ ),
​ );
​ }
​ }
Pada contoh tersebut DropdownButton tidak menggunakan child maupun
children, akan tetapi menggunakan items di mana berisi list dari widget
DropdownMenuItem. Pada widget DropdownMenuItem terdapat child untuk
tiap itemnya dan value yang ada pada DropdownMenuItem adalah nilai
dari tiap itemnya. Nantinya akan dibutuhkan parameter onChanged ketika
ada perubahan atau ketika memilih salah satu dari item tersebut dan
mengubah nilai language atau value dari DropdownButton tersebut.
Sedangkan hint berfungsi ketika nilai value dari DropdownButton null atau
kosong.

Selengkapnya tentang berbagai widget Button, bacalah pada tautan


berikut:

● Button Material Components


● ElevatedButton Class
● TextButton Class
● OutlinedButton
● IconButton Class
● DropdownButton Class

Input Widget
Salah satu bentuk interaksi dengan pengguna adalah dengan menerima
input. Ada beberapa input widget yang bisa digunakan supaya pengguna
bisa berinteraksi dengan aplikasi. Perhatikan bahwa input pengguna ini
berkaitan dengan state yang dapat sering berubah. Karena itu umumnya
input widget akan ditempatkan di dalam StatefulWidget.

TextField
TextField merupakan sebuah widget yang digunakan untuk menerima input
berupa teks yang berasal dari keyboard. Terdapat beberapa cara yang bisa
Anda gunakan untuk mendapatkan nilai dari TextField. Salah satunya
adalah melalui parameter onChanged.

​ String _name = '';



​ TextField(
​ onChanged: (String value) {
​ setState(() {
​ _name = value;
​ });
​ },
​ )
Parameter onChanged berisi sebuah fungsi yang akan dipanggil setiap
terjadi perubahan inputan pada TextField. Pada fungsi ini, kita dapat
mengubah nilai variabel state dengan memanggil fungsi setState().

Jika Anda tidak ingin mengambil nilai setiap perubahan, tetapi hanya ketika
seluruh input sudah selesai di-submit, Anda dapat menggunakan
parameter onSubmitted seperti berikut:

​ String _name = '';



​ TextField(
​ onSubmitted: (String value) {
​ setState(() {
​ _name = value;
​ });
​ },
​ )
Cara lain yang bisa kita gunakan adalah dengan TextEditingController.
Dengan controller, kita cukup membuat variabel TextEditingController lalu
menambahkannya ke widget TextField.

​ TextEditingController _controller = TextEditingController();



​ TextField(
​ controller: _controller,
​ ),
Ketika menggunakan controller, pastikan untuk menghapus controller
ketika halaman atau widget sudah tidak digunakan. Ini bertujuan supaya
tidak menimbulkan kebocoran memori (memory leak).

​ @override
​ void dispose() {
​ _controller.dispose();
​ super.dispose();
​ }
Berikut ini adalah contoh penerapan widget TextField:
Untuk membuat TextField seperti di atas, Anda bisa menggunakan kode
seperti berikut:

● onChanged
​ class _FirstScreenState extends State<FirstScreen> {
​ String _name = '';

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ appBar: AppBar(
​ title: const Text('First Screen'),
​ ),
​ body: Padding(
​ padding: const EdgeInsets.all(16.0),
​ child: Column(
​ children: [
​ TextField(
​ decoration: const InputDecoration(
​ hintText: 'Write your name here...',
​ labelText: 'Your Name',
​ ),
​ onChanged: (String value) {
​ setState(() {
​ _name = value;
​ });
​ },
​ ),
​ const SizedBox(height: 20),
​ ElevatedButton(
​ child: const Text('Submit'),
​ onPressed: () {
​ showDialog(
​ context: context,
​ builder: (context) {
​ return AlertDialog(
​ content: Text('Hello, $_name'),
​ );
​ });
​ },
​ )
​ ],
​ ),
​ ),
​ );
​ }
​ }

● Controller
● class _FirstScreenState extends State<FirstScreen> {
● TextEditingController _controller =
TextEditingController();

● @override
● Widget build(BuildContext context) {
● return Scaffold(
● appBar: AppBar(
● title: const Text('First Screen'),
● ),
● body: Padding(
● padding: const EdgeInsets.all(16.0),
● child: Column(
● children: [
● TextField(
● controller: _controller,
● decoration: const InputDecoration(
● hintText: 'Write your name here...',
● labelText: 'Your Name',
● ),
● ),
● const SizedBox(height: 20),
● ElevatedButton(
● child: const Text('Submit'),
● onPressed: () {
● showDialog(
● context: context,
● builder: (context) {
● return AlertDialog(
● content: Text('Hello,
${_controller.text}'),
● );
● });
● },
● )
● ],
● ),
● ),
● );
● }

● @override
● void dispose() {
● _controller.dispose();
● super.dispose();
● }
● }

Switch
Switch merupakan inputan yang mengembalikan nilai boolean true atau
false. Perhatikan contoh berikut:

​ class _FirstScreenState extends State<FirstScreen> {


​ bool lightOn = false;

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ appBar: AppBar(
​ title: const Text('First Screen'),
​ ),
​ body: Switch(
​ value: lightOn,
​ onChanged: (bool value) {
​ setState(() {
​ lightOn = value;
​ });

​ ScaffoldMessenger.of(context).showSnackBar(
​ SnackBar(
​ content: Text(lightOn ? 'Light On' : 'Light
Off'),
​ duration: Duration(seconds: 1),
​ ),
​ );
​ },
​ ),
​ );
​ }
​ }
Pada contoh tersebut value dari Switch berupa boolean di mana ketika
boolean tersebut false maka Switch akan berada pada posisi nonaktif.
Switch umumnya digunakan sebagai konfigurasi on/off pada halaman
setting.

Radio
Radio merupakan inputan yang digunakan untuk memilih salah satu dari
beberapa pilihan dalam suatu kelompok. Berikut contohnya:

​ class _FirstScreenState extends State<FirstScreen> {


​ String? language;

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ appBar: AppBar(
​ title: const Text('First Screen'),
​ ),
​ body: Column(
​ mainAxisSize: MainAxisSize.min,
​ children: <Widget>[
​ ListTile(
​ leading: Radio<String>(
​ value: 'Dart',
​ groupValue: language,
​ onChanged: (String? value) {
​ setState(() {
​ language = value;
​ showSnackbar();
​ });
​ },
​ ),
​ title: Text('Dart'),
​ ),
​ ListTile(
​ leading: Radio<String>(
​ value: 'Kotlin',
​ groupValue: language,
​ onChanged: (String? value) {
​ setState(() {
​ language = value;
​ showSnackbar();
​ });
​ },
​ ),
​ title: Text('Kotlin'),
​ ),
​ ListTile(
​ leading: Radio<String>(
​ value: 'Swift',
​ groupValue: language,
​ onChanged: (String? value) {
​ setState(() {
​ language = value;
​ showSnackbar();
​ });
​ },
​ ),
​ title: Text('Swift'),
​ ),
​ ],
​ ),
​ );
​ }

​ void showSnackbar() {
​ ScaffoldMessenger.of(context).showSnackBar(
​ SnackBar(
​ content: Text('$language selected'),
​ duration: Duration(seconds: 1),
​ ),
​ );
​ }
​ }
Pada contoh tersebut terdapat variable language yang digunakan pada
groupValue tiap Radio. Language inilah yang menyimpan nilai Radio yang
dipilih. Nilainya akan berubah ketika fungsi onChanged terpanggil.
Checkbox
Checkbox merupakan inputan benar atau salah. Checkbox akan berisi
centang jika nilainya adalah benar dan kosong jika salah. Seperti pada
contoh berikut:

​ class _FirstScreenState extends State<FirstScreen> {


​ bool agree = false;

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ appBar: AppBar(
​ title: const Text('First Screen'),
​ ),
​ body: ListTile(
​ leading: Checkbox(
​ value: agree,
​ onChanged: (bool? value) {
​ setState(() {
​ agree = value!;
​ });
​ },
​ ),
​ title: Text('Agree / Disagree'),
​ ),
​ );
​ }
​ }
Kode di atas jika dijalankan akan tampil seperti berikut:

Ada beberapa tautan yang dapat Anda baca untuk memahami tentang
widget-widget input yang ada pada Flutter, antara lain:

● Input and selections widgets


● TextField Class
● Switch Class
● Radio Class
● Checkbox Class
Image
Dalam pengembangan suatu aplikasi kita tidak akan lepas dari image atau
gambar untuk membuat tampilan semakin menarik. Pada materi kali ini kita
akan belajar bagaimana menampilkan gambar dari internet dan project
asset.

Image.network
Untuk menampilkan gambar yang bersumber dari internet, kita akan
menggunakan method Image.network. Cara penulisan method ini sebagai
berikut:

​ Image.network(url)
Method ini cukup menambahkan URL gambar dari internet dan kita pun
dapat menambahkan width dan height juga. Di bawah ini adalah contoh
penggunaan Image.network:

​ class FirstScreen extends StatelessWidget {


​ const FirstScreen({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ appBar: AppBar(
​ title: const Text('First Screen'),
​ ),
​ body: Center(
​ child: Image.network(
​ 'https://picsum.photos/200/300',
​ width: 200,
​ height: 200,
​ ),
​ ),
​ );
​ }
​ }
Pada kode di atas kita panggil method Image.network dengan url
https://picsum.photos/200/300 lalu beri width dan height masing-masing
200. Sehingga hasilnya seperti berikut:

Image.asset
Selain melalui internet, kita juga dapat menampilkan gambar yang
bersumber dari asset project.. Asset di sini berupa gambar-gambar yang
nantinya didaftarkan pada project. Untuk mendaftarkan asset gambar pada
project kita harus menambahkannya pada berkas pubspec.yaml.

Pertama kita harus menambahkan terlebih dahulu gambar yang akan


didaftarkan ke dalam folder project kita. Saat ini Flutter mendukung
beberapa jenis format gambar, seperti JPEG, PNG, GIF, Animated GIF,
WebP, Animated WebP, BMP, dan WBMP. Di luar format tersebut, Flutter
akan memanfaatkan API dari masing-masing platform. Jika platform native
mendukung format gambar yang digunakan, maka gambar tersebut akan
bisa di-render oleh Flutter.

Pada contoh berikut kita menambahkan folder images/ pada folder project.

Masukkan berkas gambar yang ingin Anda gunakan ke dalam folder


image. Sebagai contoh kita menggunakan gambar bernama android.png.

Setelah menambahkan gambar pada project, saatnya kita mendaftarkan


gambar tersebut pada pubspec.yaml.

Di dalam berkas pubspec.yaml, kita bisa mendaftarkan aset gambar pada


bagian flutter seperti di bawah ini:

​ ...
​ flutter:

​ uses-material-design: true

​ # To add assets to your application, add an assets section,
like this:
​ # assets:
​ # - images/a_dot_burr.jpeg
​ # - images/a_dot_ham.jpeg

​ ...
Daftarkan asset gambar seperti berikut:

​ ...
​ flutter:
​ uses-material-design: true

​ assets:
​ - images/android.png
​ ...
Hapus juga tanda pagar (#) atau komentar yang tidak diperlukan.
Perhatikan pula indentasi kodenya. assets: berada sejajar dengan
uses-material-design: yaitu berjarak 2 spasi dari ujung dan berada di dalam
flutter: sedangkan - images/android.png berada di dalam assets: dan
berjarak 4 spasi dari ujung.

Pada contoh di atas kita telah menambahkan asset yang berisi lokasi
gambar atau aset yang ingin kita gunakan. Karena kita menambahkan
gambar android.png pada folder images, maka lokasi gambar tersebut
adalah images/android.png.

Apabila ada banyak gambar yang kita masukkan ke dalam lokasi folder,
dibandingkan menuliskan lokasi gambar satu per satu, kita bisa langsung
menuliskan folder images/ seperti berikut:

​ ...
​ flutter:

​ uses-material-design: true

​ assets:
​ - images/
​ ...
Setelah menambahkan assets, kita harus me-refresh pubspec.yaml
dengan cara save file pubspec.yaml bila menggunakan Visual Studio Code
atau menekan 'Packages get' yang ada di pojok kanan atas untuk Android
Studio.
Setelah kita menambahkan asset ke dalam pubspec.yaml kita perlu
melakukan full restart agar asset yang baru dapat digunakan dalam
aplikasi.

Kita telah mendaftarkan suatu asset. Sekarang kita akan panggil asset
tersebut pada kode kita dengan method Image.asset. Cara penulisannya
seperti berikut:

​ Image.asset(lokasi_asset)
Contoh dalam kodenya akan seperti berikut:

​ class FirstScreen extends StatelessWidget {


​ const FirstScreen({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ appBar: AppBar(
​ title: const Text('First Screen'),
​ ),
​ body: Center(
​ child: Image.asset('images/android.png', width: 200,
height: 200),
​ ),
​ );
​ }
​ }
Jika kita jalankan aplikasi Flutter, maka gambar akan tampil seperti berikut:
Untuk mempelajari widget Image lebih lanjut, Anda dapat membaca
dokumentasinya pada tautan Image Class.

Font
Dalam pengembangan suatu aplikasi, seorang User Interface desainer
dapat menggunakan font berbeda dengan default font yang ada. Sebagai
pengembang aplikasi kita diharuskan menambahkan font pada aplikasi
yang dirancang oleh desainer agar sesuai dengan desain User Interface.

Pada pembelajaran kali ini kita akan belajar bagaimana menambahkan font
pada Flutter. Sebelum kita memulai pembelajaran, kita akan mengunduh
font yang ada di internet atau menggunakan font yang telah dimiliki. Pada
contoh ini kita akan mengunduh salah satu font dari Google Fonts yaitu
Oswald.

Menambahkan Font ke Project


Setelah mengunduh font, langkah selanjutnya kita akan memasukkan
file-file font tersebut ke folder project. Pada contoh ini kita akan membuat
folder fonts pada project kita, dan masukkan file-file font yang telah
diunduh, seperti berikut:

Mendaftarkan Font di pubspec.yaml


Sama halnya dengan gambar, kita perlu mendaftarkan font pada berkas
pubspec.yaml sebagai asset seperti berikut:

​ flutter:

​ uses-material-design: true
​ assets:
​ - images/

​ fonts:
​ - family: Oswald
​ fonts:
​ - asset: fonts/Oswald/Oswald-Regular.ttf
Sama halnya dengan gambar, font ada dalam bagian flutter. Untuk
mendaftarkan font, kita membuat bagian fonts yang ada dalam blok flutter.

Untuk mendaftarkan font Oswald kita tuliskan Oswald pada bagian family
yang nantinya akan menjadi nama font yang kita panggil pada kode dart.
Lalu dalam family kita masukkan fonts yang di dalamnya terdapat asset
yang nanti akan mengarah pada file font.ttf. Contoh di atas kita
menambahkan asset fonts/oswald/Oswald-Regular.ttf.

Menggunakan Font pada Kode


Setelah kita mendaftarkan font pada pubspec.yaml kita akan gunakan font
tersebut pada kode kita. Seperti contoh di bawah ini kita akan
menggunakan font pada widget Text:

​ Text(
​ 'Custom Font',
​ style: TextStyle(
​ fontFamily: 'Oswald',
​ fontSize: 30,
​ ),
​ ),
Pada kode di atas kita menambahkan fontFamily pada TextStyle. Kita
cukup panggil nama font family yang telah kita daftarkan pada
pubspec.yaml. Hasilnya akan seperti berikut:
Tulisan "Custom Font" akan berubah menjadi font Oswald sesuai dengan
yang telah kita daftarkan.

Jangan lupa! Setelah kita menambahkan package atau pun asset ke dalam
pubspec.yaml kita perlu melakukan full restart agar asset yang baru
dapat digunakan dalam aplikasi.

Mengubah Font Default


Selain kita dapat mengubah font family pada satu per satu widget Text, kita
dapat membuat font yang kita daftarkan menjadi default. Caranya dengan
menambahkan parameter fontFamily pada kelas ThemeData yang ada
pada parameter theme di MaterialApp seperti berikut:
​ class MyApp extends StatelessWidget {
​ const MyApp({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return MaterialApp(
​ title: 'Flutter Demo',
​ theme: ThemeData(
​ fontFamily: 'Oswald',
​ primarySwatch: Colors.blue,
​ ),
​ home: FirstScreen(),
​ );
​ }
​ }

Codelab 2: Perbaikan Tampilan Aplikasi


Setelah mempelajari beberapa materi tambahan, sekarang saatnya kita
melanjutkan project aplikasi wisata kita. Pada codelab ini kita akan
membuat aplikasi dengan tampilan seperti berikut:
1. Mari kita mulai dengan membuka dan melanjutkan codelab kita
sebelumnya.
2. Untuk memudahkan dalam membaca sekaligus merapikan kode,
mari kita pindahkan widget atau kelas DetailScreen ke sebuah file
dart baru. Anda dapat membuat file baru dengan cara klik kanan
pada folder lib -> New -> Dart File. Berikan nama
detail_screen.dart.

3. Anda akan mendapati beberapa eror akibat adanya library atau


package yang belum terpasang. Pada file detail_screen.dart
tambahkan kode import berikut di baris paling atas untuk
menggunakan package material design di dalam file.
​ import 'package:flutter/material.dart';
4. Selanjutnya karena kita akan menggunakan file widget DetailScreen
di file main.dart, maka kita juga perlu melakukan import berkas
detail_screen.dart ke dalam berkasi main.dart.
​ import 'package:wisatabandung/detail_screen.dart';
5. Kemudian kita akan menambahkan sebuah gambar ke tampilan
paling atas halaman. Gambar ini akan kita ambil dari asset. Untuk itu,
kita perlu menambahkan berkas yang ingin ditampilkan ke dalam
project dan menambahkannya pada file pubspec.yaml. Aset gambar
dapat Anda unduh pada tautan berikut.
​ flutter:
​ uses-material-design: true
​ assets:
​ - images/
6. Tambahkan widget Image di child paling atas dari Column.
​ class DetailScreen extends StatelessWidget {
​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ body: SafeArea(
​ child: Column(
​ crossAxisAlignment:
CrossAxisAlignment.stretch,
​ children: <Widget>[
​ Image.asset('images/farm-house.jpg'),
​ Container(...),
​ Container(...),
​ Container(...),
​ ],
​ ),
​ ),
​ );
​ }
​ }
7. Jalankan aplikasi Anda untuk melihat perubahan.
8. Selanjutnya kita akan menampilkan beberapa gambar lagi di bagian
bawah. Kali ini kita akan mengambil gambar melalui url. Mari kita
mulai dengan satu gambar terlebih dahulu.
​ class DetailScreen extends StatelessWidget {
​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ body: SafeArea(
​ child: Column(
​ crossAxisAlignment:
CrossAxisAlignment.stretch,
​ children: <Widget>[
​ Image.asset('images/farm-house.jpg'),
​ Container(...),
​ Container(...),
​ Container(...),
​ Image.network(

'https://media-cdn.tripadvisor.com/media/photo-s/0d/7c/59
/70/farmhouse-lembang.jpg'),
​ ],
​ ),
​ ),
​ );
​ }
​ }
9. Apabila gambar yang kita tampilkan terlalu besar sementara layar
pada perangkat terlalu kecil, maka akan terlihat tampilan garis
hitam-kuning yang menunjukkan terjadi overflow. Kondisi overflow ini
terjadi ketika konten yang kita tampilkan melebihi luas layar yang
ada.

10. Sebagai solusi, tentunya kita bisa mengubah ukuran dari gambar,
namun tentunya tidak praktis jika kita harus mengubah ukuran setiap
gambar yang ditampilkan. Tentu ada banyak sekali ukuran layar yang
tersedia, bukan? Solusi lainnya yaitu dengan menerapkan scrolling.
Salah satu widget scrolling yang bisa kita manfaatkan adalah
SingleChildScrollView. Widget ini membutuhkan satu child yang
nantinya bisa di-scroll pada layar. Pindahkan widget Column ke
dalam SingleChildScrollView supaya nantinya bisa di-scroll.
​ class DetailScreen extends StatelessWidget {
​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ body: SafeArea(
​ child: SingleChildScrollView(
​ child: Column(
​ crossAxisAlignment:
CrossAxisAlignment.stretch,
​ children: <Widget>[
​ Image.asset('images/farm-house.jpg'),
​ Container(...),
​ Container(...),
​ Container(...),
​ Image.network(

'https://media-cdn.tripadvisor.com/media/photo-s/0d/7c/5
9/70/farmhouse-lembang.jpg'),
​ ],
​ ),
​ ),
​ ),
​ );
​ }
​ }
11.Jalankan hot reload. Seharusnya masalah overflow sudah teratasi
dengan adanya scrolling.
12. Selanjutnya kita akan menambahkan beberapa gambar lagi yang
disusun secara horizontal. Anda mungkin mengira untuk
menggunakan widget Row supaya gambar bisa tersusun secara
horizontal. Namun, perlu diingat bahwa kita juga memerlukan fitur
scrolling agar tidak terjadi overflow. Oleh karena itu, kita akan
menggunakan ListView. Widget ini memungkinkan kita untuk
menerapkan scrolling terhadap beberapa item (children).
​ class DetailScreen extends StatelessWidget {
​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ body: SafeArea(
​ child: SingleChildScrollView(
​ child: Column(
​ crossAxisAlignment:
CrossAxisAlignment.stretch,
​ children: <Widget>[
​ Image.asset('images/farm-house.jpg'),
​ Container(...),
​ Container(...),
​ Container(...),
​ ListView(
​ children: [
​ Image.network(

'https://media-cdn.tripadvisor.com/media/photo-s/0d/7c/59
/70/farmhouse-lembang.jpg'),
​ Image.network(

'https://media-cdn.tripadvisor.com/media/photo-w/13/f0/22
/f6/photo3jpg.jpg'),
​ Image.network(

'https://media-cdn.tripadvisor.com/media/photo-m/1280/16/
a9/33/43/liburan-di-farmhouse.jpg'),
​ ],
​ ),
​ ],
​ ),
​ ),
​ ),
​ );
​ }
​ }
13. Jika Anda menjalankan aplikasi atau melakukan hot reload,
aplikasi Anda akan menjadi blank dan muncul pesan eror pada log.
Kenapa ya? ListView diletakkan di dalam Column, di mana keduanya
sama-sama memiliki atribut height yang memakan space di
sepanjang layar. Sebagai solusi kita perlu memberikan ukuran tinggi
yang statis terhadap ListView. Namun ListView tidak memiliki
parameter height, lantas bagaimana nih? Caranya, gunakan widget
lain yang memiliki parameter height. Anda dapat membungkus
widget ListView ke dalam Container atau pun SizedBox. Ukuran
tinggi ini nantinya juga digunakan sebagai tinggi Image yang tampil.
​ class DetailScreen extends StatelessWidget {
​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ body: SafeArea(
​ child: SingleChildScrollView(
​ child: Column(
​ crossAxisAlignment:
CrossAxisAlignment.stretch,
​ children: <Widget>[
​ Image.asset('images/farm-house.jpg'),
​ Container(...),
​ Container(...),
​ Container(...),
​ SizedBox(
​ height: 150,
​ child: ListView(
​ children: [
​ Image.network(

'https://media-cdn.tripadvisor.com/media/photo-s/0d/7c/5
9/70/farmhouse-lembang.jpg'),
​ Image.network(

'https://media-cdn.tripadvisor.com/media/photo-w/13/f0/2
2/f6/photo3jpg.jpg'),
​ Image.network(

'https://media-cdn.tripadvisor.com/media/photo-m/1280/16
/a9/33/43/liburan-di-farmhouse.jpg'),
​ ],
​ ),
​ ),
​ ],
​ ),
​ ),
​ ),
​ );
​ }
​ }
14. Secara default arah scroll dari ListView adalah vertikal. Untuk
mengubahnya menjadi horizontal kita cukup menambahkan
parameter scrollDirection bernilai Axis.horizontal.
​ class DetailScreen extends StatelessWidget {
​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ body: SafeArea(
​ child: SingleChildScrollView(
​ child: Column(
​ crossAxisAlignment:
CrossAxisAlignment.stretch,
​ children: <Widget>[
​ Image.asset('images/farm-house.jpg'),
​ Container(...),
​ Container(...),
​ Container(...),
​ SizedBox(
​ height: 150,
​ child: ListView(
​ scrollDirection: Axis.horizontal,
​ children: [
​ Image.network(

'https://media-cdn.tripadvisor.com/media/photo-s/0d/7c/5
9/70/farmhouse-lembang.jpg'),
​ Image.network(

'https://media-cdn.tripadvisor.com/media/photo-w/13/f0/2
2/f6/photo3jpg.jpg'),
​ Image.network(

'https://media-cdn.tripadvisor.com/media/photo-m/1280/16
/a9/33/43/liburan-di-farmhouse.jpg'),
​ ],
​ ),
​ ),
​ ],
​ ),
​ ),
​ ),
​ );
​ }
​ }
15. Selanjutnya, kita akan sedikit merapikan tampilan gambar supaya
terlihat lebih rapi dan menarik. Tambahkan Padding pada
masing-masing Image supaya antar gambar tidak terlalu rapat.
​ SizedBox(
​ height: 150,
​ child: ListView(
​ scrollDirection: Axis.horizontal,
​ children: <Widget>[
​ Padding(
​ padding: const EdgeInsets.all(4.0),
​ child: Image.network(
'https://media-cdn.tripadvisor.com/media/photo-s/0d/7c/59
/70/farmhouse-lembang.jpg'),
​ ),
​ Padding(
​ padding: const EdgeInsets.all(4.0),
​ child: Image.network(
'https://media-cdn.tripadvisor.com/media/photo-w/13/f0/22
/f6/photo3jpg.jpg'),
​ ),
​ Padding(
​ padding: const EdgeInsets.all(4.0),
​ child: Image.network(
'https://media-cdn.tripadvisor.com/media/photo-m/1280/16/
a9/33/43/liburan-di-farmhouse.jpg'),
​ ),
​ ],
​ ),
​ ),
16. Bagaimana membuat gambar memiliki sudut yang membulat
seperti pada contoh? Sekali lagi, dokumentasi adalah sahabat
terbaik Anda dalam mengembangkan aplikasi Flutter. Anda dapat
memanfaatkan mesin pencari untuk menemukan widget sesuai
keinginan. Misalnya, dengan memanfaatkan Google Anda dapat
menemukan bahwa ada widget yang memungkinkan gambar
memiliki radius, yaitu ClipRRect. Masukkan widget Image Anda
sebagai child dari ClipRRect dan berikan borderRadius, maka
Anda akan mendapatkan Image dengan sudut yang tak bersiku.

17. Terakhir, kita akan menggunakan custom Font. Anda bebas


menggunakan font kesukaan Anda. Pada contoh ini akan
menggunakan font Staatliches dan Oxygen. Tambahkan font yang
akan digunakan ke dalam project dan daftarkan pada
pubscpec.yaml.
​ flutter:
​ uses-material-design: true

​ assets:
​ - images/

​ fonts:
​ - family: Staatliches
​ fonts:
​ - asset: fonts/Staatliches-Regular.ttf
​ - family: Oxygen
​ fonts:
​ - asset: fonts/Oxygen-Regular.ttf
18. Tambahkan parameter fontFamily pada widget TextStyle untuk
menerapkan style pada Text.
​ Container(
​ margin: EdgeInsets.only(top: 16.0),
​ child: Text(
​ 'Farm House Lembang',
​ textAlign: TextAlign.center,
​ style: TextStyle(
​ fontSize: 30.0,
​ fontFamily: 'Staatliches',
​ ),
​ ),
​ ),
19. Jika Anda memiliki beberapa teks dengan style yang sama, Anda
dapat menggunakan variabel untuk menyimpan TextStyle dan
meringkas kode.
​ var informationTextStyle = const TextStyle(fontFamily:
'Oxygen');
20. Gunakan variabel tersebut pada masing-masing widget yang
membutuhkan.
​ children: <Widget>[
​ Column(
​ children: <Widget>[
​ const Icon(Icons.calendar_today),
​ const SizedBox(height: 8.0),
​ Text(
​ 'Open Everyday',
​ style: informationTextStyle,
​ ),
​ ],
​ ),
​ Column(
​ children: <Widget>[
​ const Icon(Icons.access_time),
​ const SizedBox(height: 8.0),
​ Text(
​ '09:00 - 20:00',
​ style: informationTextStyle,
​ ),
​ ],
​ ),
​ Column(
​ children: <Widget>[
​ const Icon(Icons.monetization_on),
​ const SizedBox(height: 8.0),
​ Text(
​ 'Rp 25.000',
​ style: informationTextStyle,
​ ),
​ ],
​ ),
​ ],
21. Jalankan aplikasi untuk melihat hasil akhir dari codelab ini.

Anda dapat menghapus widget SafeArea jika dirasa tampilan tanpa


SafeArea jadi lebih baik.

22. Seluruh kodenya adalah seperti berikut:


​ import 'package:flutter/material.dart';

​ var informationTextStyle = const TextStyle(fontFamily:
'Oxygen');

​ class DetailScreen extends StatelessWidget {
​ const DetailScreen({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ body: SingleChildScrollView(
​ child: Column(
​ crossAxisAlignment:
CrossAxisAlignment.stretch,
​ children: <Widget>[
​ Image.asset('images/farm-house.jpg'),
​ Container(
​ margin: const EdgeInsets.only(top: 16.0),
​ child: const Text(
​ 'Farm House Lembang',
​ textAlign: TextAlign.center,
​ style: TextStyle(
​ fontSize: 30.0,
​ fontFamily: 'Staatliches',
​ ),
​ ),
​ ),
​ Container(
​ margin: const
EdgeInsets.symmetric(vertical: 16.0),
​ child: Row(
​ mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
​ children: <Widget>[
​ Column(
​ children: <Widget>[
​ const Icon(Icons.calendar_today),
​ const SizedBox(height: 8.0),
​ Text(
​ 'Open Everyday',
​ style: informationTextStyle,
​ ),
​ ],
​ ),
​ Column(
​ children: <Widget>[
​ const Icon(Icons.access_time),
​ const SizedBox(height: 8.0),
​ Text(
​ '09:00 - 20:00',
​ style: informationTextStyle,
​ )
​ ],
​ ),
​ Column(
​ children: <Widget>[
​ const Icon(Icons.monetization_on),
​ const SizedBox(height: 8.0),
​ Text(
​ 'Rp 25.000',
​ style: informationTextStyle,
​ ),
​ ],
​ )
​ ],
​ ),
​ ),
​ Container(
​ padding: const EdgeInsets.all(16.0),
​ child: const Text(
​ 'Berada di jalur utama Bandung-Lembang,
Farm House menjadi objek wisata yang tidak pernah sepi
pengunjung. Selain karena letaknya strategis, kawasan
ini juga menghadirkan nuansa wisata khas Eropa. Semua
itu diterapkan dalam bentuk spot swafoto
Instagramable.',
​ textAlign: TextAlign.center,
​ style: TextStyle(
​ fontSize: 16.0,
​ fontFamily: 'Oxygen',
​ ),
​ ),
​ ),
​ SizedBox(
​ height: 150,
​ child: ListView(
​ scrollDirection: Axis.horizontal,
​ children: <Widget>[
​ Padding(
​ padding: const EdgeInsets.all(4.0),
​ child: ClipRRect(
​ borderRadius:
BorderRadius.circular(10),
​ child: Image.network(

'https://media-cdn.tripadvisor.com/media/photo-s/0d/7c/5
9/70/farmhouse-lembang.jpg'),
​ ),
​ ),
​ Padding(
​ padding: const EdgeInsets.all(4.0),
​ child: ClipRRect(
​ borderRadius:
BorderRadius.circular(10),
​ child: Image.network(

'https://media-cdn.tripadvisor.com/media/photo-w/13/f0/2
2/f6/photo3jpg.jpg'),
​ ),
​ ),
​ Padding(
​ padding: const EdgeInsets.all(4.0),
​ child: ClipRRect(
​ borderRadius:
BorderRadius.circular(10),
​ child: Image.network(

'https://media-cdn.tripadvisor.com/media/photo-m/1280/16
/a9/33/43/liburan-di-farmhouse.jpg'),
​ ),
​ ),
​ ],
​ ),
​ ),
​ ],
​ ),
​ ),
​ );
​ }
​ }

Anda juga dapat mengunduh seluruh kodenya pada tautan berikut:

● Codelab-2

ListView
Pada Codelab kedua kita telah menggunakan dan menyinggung sedikit
tentang widget ListView. Widget ini digunakan untuk menampilkan
beberapa item dalam bentuk baris atau kolom dan bisa di-scroll.

Cara penggunaan ListView ini mirip dengan Column atau Row di mana
Anda memasukkan widget yang ingin disusun sebagai children dari
ListView.

​ class ScrollingScreen extends StatelessWidget {


​ const ScrollingScreen({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ body: ListView(
​ children: <Widget>[
​ Container(
​ height: 250,
​ decoration: BoxDecoration(
​ color: Colors.grey,
​ border: Border.all(color: Colors.black),
​ ),
​ child: const Center(
​ child: Text(
​ '1',
​ style: TextStyle(fontSize: 50),
​ ),
​ ),
​ ),
​ Container(
​ height: 250,
​ decoration: BoxDecoration(
​ color: Colors.grey,
​ border: Border.all(color: Colors.black),
​ ),
​ child: const Center(
​ child: Text(
​ '2',
​ style: TextStyle(fontSize: 50),
​ ),
​ ),
​ ),
​ Container(
​ height: 250,
​ decoration: BoxDecoration(
​ color: Colors.grey,
​ border: Border.all(color: Colors.black),
​ ),
​ child: const Center(
​ child: Text(
​ '3',
​ style: TextStyle(fontSize: 50),
​ ),
​ ),
​ ),
​ Container(
​ height: 250,
​ decoration: BoxDecoration(
​ color: Colors.grey,
​ border: Border.all(color: Colors.black),
​ ),
​ child: const Center(
​ child: Text(
​ '4',
​ style: TextStyle(fontSize: 50),
​ ),
​ ),
​ ),
​ ],
​ ),
​ );
​ }
​ }
Ketika dijalankan, aplikasi akan menjadi seperti berikut:

Menampilkan Item Secara Dinamis


Selain memasukkan widget satu per satu ke dalam children dari ListView,
Anda juga dapat menampilkan list secara dinamis. Ini sangat berguna
ketika Anda memiliki banyak item dengan jumlah yang tidak menentu.

Misalnya kita ingin menampilkan daftar angka dari 1 sampai 10.

​ final List<int> numberList = const <int>[1, 2, 3, 4, 5, 6, 7,


8, 9, 10];
Caranya, masukkan variabel atau list Anda sebagai children lalu panggil
fungsi map(). Fungsi map ini berguna untuk memetakan atau mengubah
setiap item di dalam list menjadi objek yang kita inginkan. Fungsi map ini
membutuhkan satu buah parameter berupa fungsi atau lambda.

​ class ScrollingScreen extends StatelessWidget {


​ const ScrollingScreen({Key? key}) : super(key: key);

​ final List<int> numberList = const <int>[1, 2, 3, 4, 5, 6,
7, 8, 9, 10];

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ body: ListView(
​ children: numberList.map((number) {}),
​ ),
​ );
​ }
​ }
Karena parameter children ini membutuhkan nilai berupa list widget, maka
kita perlu mengembalikan setiap item dari numberList menjadi widget
yang akan ditampilkan. Ubah fungsi lambda Anda menjadi seperti berikut:

​ class ScrollingScreen extends StatelessWidget {


​ final List<int> numberList = [1, 2, 3, 4, 5, 6, 7, 8, 9,
10];

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ body: ListView(
​ children: numberList.map((number) {
​ return Container(
​ height: 250,
​ decoration: BoxDecoration(
​ color: Colors.grey,
​ border: Border.all(color: Colors.black),
​ ),
​ child: Center(
​ child: Text(
​ '$number', // Ditampilkan sesuai item
​ style: const TextStyle(fontSize: 50),
​ ),
​ ),
​ );
​ }).toList(),
​ ),
​ );
​ }
​ }
Perhatikan di akhir kita perlu mengembalikan fungsi map menjadi objek List
lagi dengan fungsi .toList(). Lakukan hot reload pada aplikasi Anda untuk
melihat hasil perubahan.

Menggunakan ListView.builder
Selain mengisi parameter children dari ListView seperti sebelumnya, kita
juga bisa memanfaatkan method ListView.builder. ListView.builder lebih
cocok digunakan pada ListView dengan jumlah item yang cukup besar. Ini
karena Flutter hanya akan merender tampilan item yang terlihat di layar
dan tidak me-render seluruh item ListView di awal.

ListView.builder memerlukan dua parameter yaitu itemBuilder dan


itemCount. Parameter itemBuilder merupakan fungsi yang mengembalikan
widget untuk ditampilkan. Sedangkan itemCount kita isi dengan jumlah
seluruh item yang ingin ditampilkan.

Berikut ini adalah contoh kode penggunaan ListView.builder:

​ ListView.builder(
​ itemCount: numberList.length,
​ itemBuilder: (BuildContext context, int index) {
​ return Container(
​ height: 250,
​ decoration: BoxDecoration(
​ color: Colors.grey,
​ border: Border.all(color: Colors.black),
​ ),
​ child: Center(
​ child: Text(
​ '${numberList[index]}',
​ style: const TextStyle(fontSize: 50),
​ ),
​ ),
​ );
​ },
​ ),

ListView.separated
Cara lain untuk membuat ListView adalah dengan metode
ListView.separated. ListView jenis ini akan menampilkan daftar item yang
dipisahkan dengan separator. Penggunaan ListView.separated mirip
dengan builder, yang membedakan adalah terdapat satu parameter
tambahan wajib yaitu separatorBuilder yang mengembalikan Widget yang
akan berperan sebagai separator.

Berikut ini adalah contoh kode dari ListView.separated:

​ ListView.separated(
​ itemCount: numberList.length,
​ itemBuilder: (BuildContext context, int index) {
​ return Container(
​ height: 250,
​ decoration: BoxDecoration(
​ color: Colors.grey,
​ border: Border.all(color: Colors.black),
​ ),
​ child: Center(
​ child: Text(
​ '${numberList[index]}',
​ style: const TextStyle(fontSize: 50),
​ ),
​ ),
​ );
​ },
​ separatorBuilder: (BuildContext context, int index) {
​ return const Divider();
​ },
​ ),
Jika kode di atas dijalankan, maka tampilan aplikasi adalah seperti ini:

Expanded & Flexible


Sejauh ini kita telah mempelajari beberapa widget dasar dan bagaimana
menyusunnya secara horizontal maupun vertikal. Dalam pengembangan
aplikasi mobile kita tahu bahwa terdapat banyak sekali perangkat dengan
ukuran layar yang berbeda pula. Untuk itu penting bagi kita untuk bisa
menyusun tampilan yang responsif terhadap ukuran layar.

Kira-kira bagaimana Anda akan menyusun layout dengan tampilan seperti


berikut?
Tentunya akan sangat merepotkan apabila kita mengatur tinggi dari
masing-masing kotak, bukan? Belum lagi jika harus mengembangkan
aplikasi di ukuran yang lebih besar seperti perangkat tablet.

Expanded
Flutter memiliki widget Expanded yang dapat mengembangkan child dari
Row atau Column sesuai dengan ruang yang tersedia. Cara
menggunakannya Anda cukup membungkus masing-masing child ke
dalam Expanded.

​ class Rainbow extends StatelessWidget {


​ const Rainbow({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Column(
​ children: <Widget>[
​ Expanded(
​ child: Container(
​ color: Colors.red,
​ ),
​ ),
​ Expanded(
​ child: Container(
​ color: Colors.orange,
​ ),
​ ),
​ Expanded(
​ child: Container(
​ color: Colors.yellow,
​ ),
​ ),
​ Expanded(
​ child: Container(
​ color: Colors.green,
​ ),
​ ),
​ Expanded(
​ child: Container(
​ color: Colors.blue,
​ ),
​ ),
​ Expanded(
​ child: Container(
​ color: Colors.indigo,
​ ),
​ ),
​ Expanded(
​ child: Container(
​ color: Colors.purple,
​ ),
​ ),
​ ],
​ );
​ }
​ }
Saat aplikasi dijalankan, masing-masing container akan menempati ruang
kosong yang ada. Jika Anda menjalankan di ukuran layar yang berbeda,
maka ukuran container juga akan menyesuaikan.

Bisa kita lihat seluruh container menempati ruang dengan ukuran yang
sama. Ini disebabkan Expanded memiliki parameter flex yang memiliki nilai
default 1. Anda dapat mengubah nilai flex ini sesuai perbandingan yang
diinginkan. Misalnya Anda memberikan nilai flex 2 pada salah satu
container.

​ Expanded(
​ flex: 2,
​ child: Container(
​ color: Colors.blue,
​ ),
​ ),
Maka container berwarna biru ini akan menjadi lebih besar dengan
perbandingan 2/(1 + 1 + 1 + 1 + 2 + 1 + 1) atau 2/8 dari halaman.
Flexible
Sama seperti Expanded, widget Flexible digunakan untuk mengatur ukuran
widget di dalam Row atau Column secara fleksibel. Perbedaan Flexible
dan Expanded adalah widget Flexible memungkinkan child widget-nya
berukuran lebih kecil dibandingkan ukuran ruang yang tersisa. Sementara,
child widget dari Expanded harus menempati ruang yang tersisa dari
Column atau Row.

Berikut ini adalah contoh perbedaan antara Expanded dan Flexible:


Kode untuk tampilan seperti di atas adalah seperti berikut:

​ class ExpandedFlexiblePage extends StatelessWidget {


​ const ExpandedFlexiblePage({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ body: SafeArea(
​ child: Column(
​ children: [
​ Row(
​ children: const [
​ ExpandedWidget(),
​ FlexibleWidget(),
​ ],
​ ),
​ Row(
​ children: const [
​ ExpandedWidget(),
​ ExpandedWidget(),
​ ],
​ ),
​ Row(
​ children: const [
​ FlexibleWidget(),
​ FlexibleWidget(),
​ ],
​ ),
​ Row(
​ children: const [
​ FlexibleWidget(),
​ ExpandedWidget(),
​ ],
​ ),
​ ],
​ ),
​ ),
​ );
​ }
​ }

​ class ExpandedWidget extends StatelessWidget {
​ const ExpandedWidget({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Expanded(
​ child: Container(
​ decoration: BoxDecoration(
​ color: Colors.teal,
​ border: Border.all(color: Colors.white),
​ ),
​ child: Padding(
​ padding: const EdgeInsets.all(16.0),
​ child: Text(
​ 'Expanded',
​ style: TextStyle(
​ color: Colors.white,
​ fontSize: 24,
​ ),
​ ),
​ ),
​ ),
​ );
​ }
​ }

​ class FlexibleWidget extends StatelessWidget {
​ const FlexibleWidget({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Flexible(
​ child: Container(
​ decoration: BoxDecoration(
​ color: Colors.tealAccent,
​ border: Border.all(color: Colors.white),
​ ),
​ child: Padding(
​ padding: const EdgeInsets.all(16.0),
​ child: Text(
​ 'Flexible',
​ style: TextStyle(
​ color: Colors.teal,
​ fontSize: 24,
​ ),
​ ),
​ ),
​ ),
​ );
​ }
​ }

Dokumentasi berikut ini dapat Anda pelajari untuk memaksimalkan


penggunaan widget Expanded dan Flexible:

● Expanded Class
● Flexible Class

Navigation
Kita telah bisa membuat satu tampilan screen (layar/page) pada
pembelajaran sebelumnya. Namun, pada saat membangun sebuah
aplikasi kita akan membuat banyak sekali screen dan kita akan berpindah
dari satu screen ke screen lainnya.

Dalam pemrograman Android kita mengenal Intent lalu pada pemrograman


website terdapat tag untuk berpindah dari satu page ke page lain. Pada
Flutter kita akan menggunakan sebuah class bernama Navigator. Dengan
Navigator ini kita akan berpindah dari satu screen ke screen lainnya.
Berikut ini contohnya:
Perlu kita ketahui bahwa konsep navigasi pada Flutter mirip sekali dengan
pemrograman Android, yakni bahwa ketika berpindah screen/activity akan
menjadi tumpukan (stack). Jadi ketika berpindah dari satu screen ke
screen lain (push), maka screen pertama akan ditumpuk oleh screen
kedua. Kemudian apabila kembali dari screen kedua ke pertama, maka
screen kedua akan dihapus (pop).

Kita akan membuat kode seperti contoh di atas. Kita membutuhkan


halaman kedua yang kodenya seperti berikut:

​ class SecondScreen extends StatelessWidget {


​ const SecondScreen({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ appBar: AppBar(
​ title: const Text('Second Screen'),
​ ),
​ body: Center(
​ child: OutlinedButton(
​ child: const Text('Kembali'),
​ onPressed: () {},
​ ),
​ ),
​ );
​ }
​ }
Lalu, kode untuk halaman pertama akan seperti berikut:

​ class FirstScreen extends StatelessWidget {


​ const FirstScreen({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ appBar: AppBar(
​ title: const Text('First Screen'),
​ ),
​ body: Center(
​ child: ElevatedButton(
​ child: const Text('Pindah Screen'),
​ onPressed: () {},
​ ),
​ ),
​ );
​ }
​ }

Navigator.push
Untuk berpindah ke screen kedua kita akan menggunakan sebuah method
Navigator.push, method tersebut ditulis seperti berikut:

​ Navigator.push(context, MaterialPageRoute(builder: (context)


{
​ return WidgetScreen();
​ }));
Pada kode di atas Navigator.push memiliki dua parameter. Pertama ialah
context dan yang kedua Route. Parameter context ini merupakan variabel
BuildContext yang ada pada method build. Parameter route berguna untuk
menentukan tujuan ke mana kita akan berpindah screen. Route tersebut
kita isikan dengan MaterialPageRoute yang di dalamnya terdapat builder
yang nantinya akan diisi dengan tujuan screen-nya. Maka untuk melakukan
perpindahan screen kita akan membuat event onPressed pada tombol
ElevatedButton yang ada pada screen pertama:

​ class FirstScreen extends StatelessWidget {


​ const FirstScreen({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ appBar: AppBar(
​ title: const Text('First Screen'),
​ ),
​ body: Center(
​ child: ElevatedButton(
​ child: const Text('Pindah Screen'),
​ onPressed: () {
​ Navigator.push(context,
MaterialPageRoute(builder: (context) {
​ return const SecondScreen();
​ }));
​ },
​ ),
​ ),
​ );
​ }
​ }

Navigator.pop
Setelah dapat berpindah ke screen lain maka kita akan belajar
menggunakan Navigator.pop untuk kembali ke screen sebelumnya.
Penulisan Navigator.pop seperti berikut.

​ Navigator.pop(context)
Pada Navigator.pop kita hanya cukup menambahkan parameter context
yang merupakan variabel dari method build.
Untuk kembali dari screen kedua kita dapat menambahkan event
onPressed pada OutlinedButton yang ada pada screen kedua dan kita
masukkan Navigator.pop pada event, seperti berikut:

​ class SecondScreen extends StatelessWidget {


​ const SecondScreen({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ appBar: AppBar(
​ title: const Text('Second Screen'),
​ ),
​ body: Center(
​ child: OutlinedButton(
​ child: const Text('Kembali'),
​ onPressed: () {
​ Navigator.pop(context);
​ },
​ ),
​ ),
​ );
​ }
​ }

Mengirimkan Data Antar Halaman


Seringkali beberapa halaman pada aplikasi perlu saling berinteraksi
dengan berbagi dan saling mengirimkan data. Pada Flutter kita
memanfaatkan constructor dari sebuah class untuk mengirimkan data antar
halaman.

Sebagai contoh kita memiliki pesan yang akan dikirimkan dari First Screen
menuju Second Screen.

​ final String message = 'Hello from First Screen!';


Untuk mengirimkan variabel message tersebut ke Second Screen, maka
kita akan mengirimkannya sebagai parameter dari constructor kelas
SecondScreen seperti berikut:
​ class FirstScreen extends StatelessWidget {
​ final String message = 'Hello from First Screen!';

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ appBar: AppBar(
​ title: const Text('First Screen'),
​ ),
​ body: Center(
​ child: ElevatedButton(
​ child: const Text('Pindah Screen'),
​ onPressed: () {
​ Navigator.push(context,
​ MaterialPageRoute(builder: (context) =>
SecondScreen(message)));
​ },
​ ),
​ ),
​ );
​ }
​ }
Agar Second Screen bisa menerima data tersebut, maka kita perlu
mengubah default constructor-nya dan menambahkan variabel untuk
menampung datanya.

​ class SecondScreen extends StatelessWidget {


​ final String message;

​ const SecondScreen(this.message, {Key? key}) : super(key:
key);
​ }
Kemudian kita dapat menampilkan data yang diterima melalui variabel
yang kita buat.

​ class SecondScreen extends StatelessWidget {


​ final String message;

​ const SecondScreen(this.message, {Key? key}) : super(key:
key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ appBar: AppBar(
​ title: const Text('Second Screen'),
​ ),
​ body: Center(
​ child: Column(
​ mainAxisAlignment: MainAxisAlignment.center,
​ children: [
​ Text(message),
​ OutlinedButton(
​ child: const Text('Kembali'),
​ onPressed: () {
​ Navigator.pop(context);
​ },
​ ),
​ ],
​ ),
​ ),
​ );
​ }
​ }
Sehingga tampilan Second Screen akan menampilkan pesan dari First
Screen seperti berikut:
Anda dapat memahami Navigation secara mendalam dengan membaca
dokumentasi Navigation Cookbook.

Responsive Layout
Seperti yang kita tahu, Flutter merupakan framework untuk
mengembangkan aplikasi pada berbagai platform. Pada platform mobile
sendiri tersedia banyak ukuran layar dari ukuran jam hingga tablet.
Ditambah Flutter baru saja mengumumkan dukungan untuk platform web
dan desktop. Itu artinya, satu hal yang penting untuk kita pahami adalah
bagaimana menerapkan layout yang mampu beradaptasi dengan berbagai
ukuran layar yang berbeda.

Pada materi ini kita akan mulai membahas bagaimana


mengimplementasikan layout yang responsif.
MediaQuery
Pendekatan pertama yang akan kita lakukan adalah menggunakan Media
Query. Jika Anda sudah familier dengan pengembangan web, mungkin
Anda sudah tidak asing dengan konsep ini. MediaQuery adalah kelas yang
dapat kita gunakan untuk mendapatkan ukuran dan juga orientasi layar.

Mari kita lihat contoh penerapan MediaQuery.

​ class HomePage extends StatelessWidget {


​ const HomePage({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ Size screenSize = MediaQuery.of(context).size;
​ Orientation orientation =
MediaQuery.of(context).orientation;

​ return Scaffold(
​ backgroundColor: Colors.blueGrey,
​ body: Column(
​ mainAxisAlignment: MainAxisAlignment.center,
​ crossAxisAlignment: CrossAxisAlignment.stretch,
​ children: [
​ Text(
​ 'Screen width:
${screenSize.width.toStringAsFixed(2)}',
​ style: const TextStyle(color: Colors.white,
fontSize: 18),
​ textAlign: TextAlign.center,
​ ),
​ Text(
​ 'Orientation: $orientation',
​ style: const TextStyle(color: Colors.white,
fontSize: 18),
​ textAlign: TextAlign.center,
​ ),
​ ],
​ ),
​ );
​ }
​ }
Sekarang jalankan aplikasi untuk melihat ukuran layarnya.
LayoutBuilder
Cara lain yang bisa kita gunakan adalah dengan widget LayoutBuilder.
Perbedaan umum antara MediaQuery dan Layout Builder adalah
MediaQuery akan mengembalikan ukuran layar yang digunakan,
sedangkan LayoutBuilder mengembalikan ukuran maksimum dari widget
tertentu.

Berikut ini adalah contoh kode yang menunjukkan perbedaan antara


MediaQuery dan LayoutBuilder:

​ class HomePage extends StatelessWidget {


​ const HomePage({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ Size screenSize = MediaQuery.of(context).size;

​ return Scaffold(
​ backgroundColor: Colors.blueGrey,
​ body: Row(
​ children: [
​ Expanded(
​ child: LayoutBuilder(
​ builder: (BuildContext context, BoxConstraints
constraints) {
​ return Column(
​ mainAxisAlignment:
MainAxisAlignment.center,
​ crossAxisAlignment:
CrossAxisAlignment.stretch,
​ children: [
​ Text(
​ 'MediaQuery:
${screenSize.width.toStringAsFixed(2)}',
​ style: const TextStyle(color:
Colors.white, fontSize: 18),
​ textAlign: TextAlign.center,
​ ),
​ Text(
​ 'LayoutBuilder:
${constraints.maxWidth}',
​ style: const TextStyle(color:
Colors.white, fontSize: 18),
​ textAlign: TextAlign.center,
​ ),
​ ],
​ );
​ },
​ ),
​ ),
​ Expanded(
​ flex: 3,
​ child: LayoutBuilder(
​ builder: (BuildContext context, BoxConstraints
constraints) {
​ return Container(
​ color: Colors.white,
​ child: Column(
​ mainAxisAlignment:
MainAxisAlignment.center,
​ crossAxisAlignment:
CrossAxisAlignment.stretch,
​ children: [
​ Text(
​ 'MediaQuery:
${screenSize.width.toStringAsFixed(2)}',
​ style: const TextStyle(color:
Colors.blueGrey, fontSize: 18),
​ textAlign: TextAlign.center,
​ ),
​ Text(
​ 'LayoutBuilder:
${constraints.maxWidth}',
​ style: const TextStyle(color:
Colors.blueGrey, fontSize: 18),
​ textAlign: TextAlign.center,
​ ),
​ ],
​ ),
​ );
​ },
​ ),
​ ),
​ ],
​ ),
​ );
​ }
​ }
Hasil ketika dijalankan pada browser akan seperti ini:
Ubahlah ukuran jendela browser untuk melihat perubahan ukuran layar
atau media yang digunakan.

Dengan mendapatkan ukuran lebar dan tinggi layar seperti di atas, kita
bisa menentukan tampilan konten berdasarkan ukuran layar yang
digunakan. Dalam responsive design, terdapat breakpoint yang merupakan
“titik” di mana layout akan beradaptasi untuk memberikan pengalaman
pengguna sebaik mungkin.

Dengan kode di bawah ini berarti akan terdapat tiga model tampilan
berdasarkan ukuran layar:

​ class ResponsivePage extends StatelessWidget {


​ const ResponsivePage({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ appBar: AppBar(),
​ body: LayoutBuilder(
​ builder: (BuildContext context, BoxConstraints
constraints) {
​ if (constraints.maxWidth < 600) {
​ return ListView(
​ children: _generateContainers(),
​ );
​ } else if (constraints.maxWidth < 900) {
​ return GridView.count(
​ crossAxisCount: 2,
​ children: _generateContainers(),
​ );
​ } else {
​ return GridView.count(
​ crossAxisCount: 6,
​ children: _generateContainers(),
​ );
​ }
​ },
​ ),
​ );
​ }

​ List<Widget> _generateContainers() {
​ return List<Widget>.generate(20, (index) {
​ return Container(
​ margin: const EdgeInsets.all(8),
​ color: Colors.blueGrey,
​ height: 200,
​ );
​ });
​ }
​ }
Berikut adalah tampilan dari kode di atas ketika dijalankan:
Codelab 3: Menampilkan Daftar Tempat Wisata
Sekarang kita telah sampai pada codelab ketiga. Di akhir codelab ini kita
akan menyelesaikan project aplikasi Wisata Bandung. Hasil akhir aplikasi
akan seperti berikut:
Mari kita mulai. Buka kembali project codelab Anda sebelumnya.

1. Pertama kali yang kita lakukan adalah membuat halaman baru untuk
menampilkan daftar tempat wisata. Buat berkas baru
main_screen.dart lalu buat widget untuk halaman MainScreen.
​ import 'package:flutter/material.dart';

​ class MainScreen extends StatelessWidget {
​ const MainScreen({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold();
​ }
​ }
2. Jangan lupa untuk mengganti halaman utama yang ditampilkan pada
berkas main.dart.
​ void main() => runApp(const MyApp());

​ class MyApp extends StatelessWidget {
​ @override
​ Widget build(BuildContext context) {
​ return MaterialApp(
​ title: 'Wisata Bandung',
​ theme: ThemeData(),
​ home: const MainScreen(),
​ );
​ }
​ }
3. Pada MainScreen tambahkan AppBar untuk judul halaman.
​ class MainScreen extends StatelessWidget {
​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ appBar: AppBar(
​ title: const Text('Wisata Bandung'),
​ ),
​ );
​ }
​ }
4. Sebagai body dari Scaffold kita akan menggunakan widget Card.
Widget ini adalah widget material design yang menghasilkan
tampilan seperti kartu dengan ujung yang membulat dan bayangan di
belakang. Kemudian susun Row dan Column seperti contoh untuk
menyusun child dari Card. Kodenya akan seperti berikut:
​ class MainScreen extends StatelessWidget {
​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ appBar: AppBar(
​ title: const Text('Wisata Bandung'),
​ ),
​ body: Card(
​ child: Row(
​ crossAxisAlignment: CrossAxisAlignment.start,
​ children: <Widget>[
​ Image.asset('images/farm-house.jpg'),
​ Padding(
​ padding: const EdgeInsets.all(8.0),
​ child: Column(
​ crossAxisAlignment:
CrossAxisAlignment.start,
​ mainAxisSize: MainAxisSize.min,
​ children: <Widget>[
​ Text(
​ 'Farm House Lembang',
​ style: const TextStyle(fontSize:
16.0),
​ ),
​ const SizedBox(
​ height: 10,
​ ),
​ Text('Lembang'),
​ ],
​ ),
​ )
​ ],
​ ),
​ ),
​ );
​ }
​ }
5. Jalankan aplikasinya. Aplikasi akan mengalami overflow karena aset
gambar yang terlalu besar. Kita bisa saja mengatur tinggi gambar
secara manual, namun kali ini kita akan memanfaatkan widget
Expanded agar tampilan juga dapat menyesuaikan di perangkat
yang lebih besar atau kecil. Bungkus masing-masing item dari widget
Row ke dalam Expanded. Berikan parameter flexyang menurut
Anda cocok.
​ class MainScreen extends StatelessWidget {
​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ appBar: AppBar(
​ title: const Text('Wisata Bandung'),
​ ),
​ body: Card(
​ child: Row(
​ crossAxisAlignment: CrossAxisAlignment.start,
​ children: <Widget>[
​ Expanded(
​ flex: 1,
​ child:
Image.asset('images/farm-house.jpg'),
​ ),
​ Expanded(
​ flex: 2,
​ child: Padding(
​ padding: const EdgeInsets.all(8.0),
​ child: Column(
​ crossAxisAlignment:
CrossAxisAlignment.start,
​ mainAxisSize: MainAxisSize.min,
​ children: <Widget>[
​ Text(
​ 'Farm House Lembang',
​ style: const TextStyle(fontSize:
16.0),
​ ),
​ const SizedBox(
​ height: 10,
​ ),
​ Text('Lembang'),
​ ],
​ ),
​ ),
​ ),
​ ],
​ ),
​ ),
​ );
​ }
​ }
6. Item pertama Anda sudah siap. Selanjutnya kita akan membuat kartu
ini bisa diklik untuk berpindah ke halaman detail. Kita bisa
menggunakan widget InkWell yang menyediakan parameter onTap.
Pindahkan widget Card Anda menjadi child dari InkWell.
​ class MainScreen extends StatelessWidget {
​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ appBar: AppBar(
​ title: Text('Wisata Bandung'),
​ ),
​ body: InkWell(
​ onTap: () {},
​ child: Card(...),
​ ),
​ );
​ }
​ }
7. Parameter onTapmenerima argumen berupa sebuah fungsi lambda.
Di sini kita akan menambahkan Navigator untuk berpindah ke
halaman detail.
​ onTap: () {
​ Navigator.push(context, MaterialPageRoute(builder:
(context) {
​ return DetailScreen();
​ }));
​ },
8. Jalankan aplikasi. Seharusnya sampai langkah ini aplikasi Anda
sudah dapat berpindah halaman ketika item diklik.
9. Selanjutnya kita akan menampilkan beberapa item ke MainScreen.
Di kelas ini kita masih menggunakan data statis dan lokal yang
disimpan pada objek List. Sebelumnya, buatlah kelas sebagai
blueprint untuk menyimpan objek tempat wisata kita. Buat folder baru
di dalam folder lib dengan cara klik kanan folder lib -> New ->
Package, lalu berikan nama model. Di dalam folder model buat
berkas dart bernama tourism_place.dart.
10. Di dalam tourism_place.dart buat data class yang akan menjadi
blueprint objek tempat wisata.
​ class TourismPlace {
​ String name;
​ String location;
​ String description;
​ String openDays;
​ String openTime;
​ String ticketPrice;
​ String imageAsset;
​ List<String> imageUrls;

​ TourismPlace({
​ required this.name,
​ required this.location,
​ required this.description,
​ required this.openDays,
​ required this.openTime,
​ required this.ticketPrice,
​ required this.imageAsset,
​ required this.imageUrls,
​ });
​ }
11.Siapkan data statis yang ingin ditampilkan Anda dapat menyalin kode
berikut dan taruh di berkas tourism_place.dart paling bawah.
​ var tourismPlaceList = [
​ TourismPlace(
​ name: 'Farm House Lembang',
​ location: 'Lembang',
​ description:
​ 'Berada di jalur utama Bandung-Lembang, Farm
House menjadi objek wisata yang tidak pernah sepi
pengunjung. Selain karena letaknya strategis, kawasan
ini juga menghadirkan nuansa wisata khas Eropa. Semua
itu diterapkan dalam bentuk spot swafoto
Instagramable.',
​ openDays: 'Open Everyday',
​ openTime: '09:00 - 20:00',
​ ticketPrice: 'Rp 25000',
​ imageAsset: 'images/farm-house.jpg',
​ imageUrls: [

'https://media-cdn.tripadvisor.com/media/photo-s/0d/7c/5
9/70/farmhouse-lembang.jpg',

'https://media-cdn.tripadvisor.com/media/photo-w/13/f0/2
2/f6/photo3jpg.jpg',

'https://media-cdn.tripadvisor.com/media/photo-m/1280/16
/a9/33/43/liburan-di-farmhouse.jpg'
​ ],
​ ),
​ TourismPlace(
​ name: 'Observatorium Bosscha',
​ location: 'Lembang',
​ description:
​ 'Memiliki beberapa teleskop, antara lain,
Refraktor Ganda Zeiss, Schmidt Bimasakti, Refraktor
Bamberg, Cassegrain GOTO, dan Teleskop Surya. Refraktor
Ganda Zeiss adalah jenis teleskop terbesar untuk
meneropong bintang. Benda ini diletakkan pada atap kubah
sehingga saat teropong digunakan, atap tersebut harus
dibuka. Observatorium Bosscha boleh dikunjungi oleh
siapa pun, tanpa tiket. Namun, bagi yang ingin
menggunakan teleskop Zeiss, wajib mendaftarkan diri.
Untuk instansi atau lembaga pendidikan, diberikan jadwal
hari Selasa sampai Jumat. Sementara itu, kunjungan
individu dibuka setiap hari Sabtu.',
​ openDays: 'Open Tuesday - Saturday',
​ openTime: '09:00 - 14:30',
​ ticketPrice: 'Rp 20000',
​ imageAsset: 'images/bosscha.jpg',
​ imageUrls: [

'https://media-cdn.tripadvisor.com/media/photo-o/12/6b/6
3/0b/bosscha-observatory.jpg',

'https://media-cdn.tripadvisor.com/media/photo-p/0d/6a/8
8/9b/photo3jpg.jpg',

'https://media-cdn.tripadvisor.com/media/photo-o/11/3f/0
4/39/p-20171111-110220-largejpg.jpg',
​ ],
​ ),
​ TourismPlace(
​ name: 'Jalan Asia Afrika',
​ location: 'Kota Bandung',
​ description:
​ 'Jalan Asia Afrika di Bandung memiliki kaitan
yang sangat erat dengan pendirian kota Kembang ini.
Karena pada saat itu, Gubernur Jenderal Herman Willem
Deaendels dari Belanda menancapkan tongkatnya saat
memerintahkan pendirian kota ini, yang kemudian
diabadikan menjadi tugu Bandung Nol Kilometer.',
​ openDays: 'Open Everyday',
​ openTime: '24 hours',
​ ticketPrice: 'Free',
​ imageAsset: 'images/jalan-asia-afrika.jpg',
​ imageUrls: [

'https://media-cdn.tripadvisor.com/media/photo-o/0d/c2/e
7/e6/quotes-kota-bandung.jpg',

'https://media-cdn.tripadvisor.com/media/photo-w/17/f4/4
4/01/jalan-asia-afrika.jpg',

'https://media-cdn.tripadvisor.com/media/photo-s/0a/ef/3
6/e2/jalan-asia-afrika.jpg',
​ ],
​ ),
​ TourismPlace(
​ name: 'Stone Garden',
​ location: 'Padalarang',
​ description:
​ 'Stone Garden atau Taman Batu di Padalarang –
Bandung ini adalah nama secara harafiah untuk apa yang
akan kita lihat jika berada di sana. Hamparan batu yang
artistik membuat kita merasa tidak sedang berada di
Bandung, apalagi di Padalarang. Hamparan batu yang
dimaksud bukan terhampar begitu saja di atas tanah luas
yang menjadi permukaannya. Batu-batu besar yang ukuran
pastinya bervariasi tersusun seperti memiliki suatu
formasi matematis.',
​ openDays: 'Open Everyday',
​ openTime: '06:00 - 17:00',
​ ticketPrice: 'Rp 3000',
​ imageAsset: 'images/stone-garden.jpg',
​ imageUrls: [

'https://media-cdn.tripadvisor.com/media/photo-o/15/01/d
7/4b/p-20180510-153310-01.jpg',

'https://media-cdn.tripadvisor.com/media/photo-w/15/68/0
0/32/stone-garden-citatah.jpg',

'https://media-cdn.tripadvisor.com/media/photo-o/0d/a2/c
b/05/stone-garden-citatah.jpg',
​ ],
​ ),
​ TourismPlace(
​ name: 'Taman Film Pasopati',
​ location: 'Kota Bandung',
​ description:
​ 'Menjadi salah satu tempat wisata di Bandung
yang favorit, tentu Taman Film ini memiliki fasilitas
cukup memadai. Pemberian fasilitas ini memiliki harapan
para pengunjung akan merasa nyaman dan tak segan2 untuk
kembali berkunjung terus menerus kesini. Beberapa
fasilitas taman yang bisa kamu nikmati diantaranya
seperti layar videotron besar berukuran 4×8 untuk
memutar berbagai macam pilihan film seperti Film
Indonesia, Bollywood, Korea, ataupun Indie Bandung.',
​ openDays: 'Open Everyday',
​ openTime: '24 hours',
​ ticketPrice: 'Free',
​ imageAsset: 'images/taman-film.jpg',
​ imageUrls: [

'https://media-cdn.tripadvisor.com/media/photo-o/08/8b/8
7/50/bandung-movie-park.jpg',

'https://media-cdn.tripadvisor.com/media/photo-o/17/67/d
5/53/img-20190505-114509-largejpg.jpg',

'https://media-cdn.tripadvisor.com/media/photo-w/09/73/3
3/05/taman-film-pasopati.jpg',
​ ],
​ ),
​ TourismPlace(
​ name: 'Museum Geologi',
​ location: 'Kota Bandung',
​ description:
​ 'Museum Geologi didirikan pada tanggal 16 Mei
1929. Museum ini telah direnovasi dengan dana bantuan
dari JICA (Japan International Cooperation Agency).
Setelah mengalami renovasi, Museum Geologi dibuka
kembali dan diresmikan oleh Wakil Presiden RI, Megawati
Soekarnoputri pada tanggal 23 Agustus 2000. Sebagai
salah satu monumen bersejarah, museum berada di bawah
perlindungan pemerintah dan merupakan peninggalan
nasional. Dalam Museum ini, tersimpan dan dikelola
materi-materi geologi yang berlimpah, seperti fosil,
batuan, mineral. Kesemuanya itu dikumpulkan selama kerja
lapangan di Indonesia sejak 1850.',
​ openDays: 'Open Saturday - Thursday',
​ openTime: '09:00 - 15:30',
​ ticketPrice: 'Rp 3000',
​ imageAsset: 'images/museum-geologi.jpg',
​ imageUrls: [

'https://media-cdn.tripadvisor.com/media/photo-w/19/1c/8
e/f7/geology-museum.jpg',

'https://media-cdn.tripadvisor.com/media/photo-o/11/a7/3
5/b7/geology-museum.jpg',

'https://media-cdn.tripadvisor.com/media/photo-s/1a/55/e
0/dc/geology-museum.jpg',
​ ],
​ ),
​ TourismPlace(
​ name: 'Floating Market',
​ location: 'Lembang',
​ description:
​ 'Tempat wisata ini sepertinya memang ditujukan
untuk wisata keluarga di Bandung. Di sini kita bisa
menikmati suasana kawasan yang tertata rapi dan alami.
Pada awalnya, floating market Lembang tidak begitu luas.
Tapi sekarang sudah ekspansi dan memiliki banyak objek
menarik baru. Nama floating market ini sepertinya
merujuk pada stand tempat jualan makanan yang berada
dalam perahu.',
​ openDays: 'Open Everyday',
​ openTime: '09:00 - 17:00',
​ ticketPrice: 'Rp 20000',
​ imageAsset: 'images/floating-market.png',
​ imageUrls: [

'https://media-cdn.tripadvisor.com/media/photo-o/17/f9/f
f/f8/floating-market-bandung.jpg',

'https://media-cdn.tripadvisor.com/media/photo-p/1a/86/d
3/cd/20200103-125059-largejpg.jpg',

'https://media-cdn.tripadvisor.com/media/photo-p/19/ce/b
4/9b/img20181224120857-largejpg.jpg',
​ ],
​ ),
​ TourismPlace(
​ name: 'Kawah Putih',
​ location: 'Ciwidey',
​ description:
​ 'Kawah Putih adalah tempat wisata di Bandung
yang paling terkenal. Berlokasi di Ciwidey, Jawa Barat,
kurang lebih sekitar 50 KM arah selatan kota Bandung,
Kawah Putih adalah sebuah danau yang terbentuk akibat
dari letusan Gunung Patuha. Sesuai dengan namanya, tanah
yang ada di kawasan ini berwarna putih akibat dari
pencampuran unsur belerang.',
​ openDays: 'Open Everyday',
​ openTime: '07:00 - 17:00',
​ ticketPrice: 'Rp 15000',
​ imageAsset: 'images/kawah-putih.jpg',
​ imageUrls: [

'https://media-cdn.tripadvisor.com/media/photo-o/0b/6e/7
c/ce/rocks-sticking-out-of.jpg',

'https://media-cdn.tripadvisor.com/media/photo-p/0b/35/3
0/14/white-crater.jpg',

'https://media-cdn.tripadvisor.com/media/photo-o/0a/8b/9
a/79/2945-t00572-www-initempatwisat.jpg',
​ ],
​ ),
​ TourismPlace(
​ name: 'Ranca Upas',
​ location: 'Ciwidey',
​ description:
​ 'Ranca Upas Ciwidey adalah kawasan bumi
perkemahan di bawah pengelolaan perhutani. Tempat ini
berada di kawasan wisata Bandung Selatan, satu lokasi
dengan kawah putih, kolam Cimanggu dan situ Patenggang.
Banyak hal yang bisa dilakukan di kawasan wisata ini,
seperti berkemah, berinteraksi dengan rusa, sampai
bermain di water park dan mandi air panas.',
​ openDays: 'Open Everyday',
​ openTime: '24 hours',
​ ticketPrice: 'Rp 20000',
​ imageAsset: 'images/ranca-upas.jpg',
​ imageUrls: [

'https://media-cdn.tripadvisor.com/media/photo-o/1a/e0/7
f/9c/kampung-cai-ranca-upas.jpg',

'https://media-cdn.tripadvisor.com/media/photo-w/13/ee/2
f/87/ranca-upas.jpg',

'https://media-cdn.tripadvisor.com/media/photo-w/13/ee/2
7/0a/ranca-upas.jpg',
​ ],
​ ),
​ ];
12. Jangan lupa untuk menambahkan import berkas
tourism_place.dart pada file main_screen.dart.
​ import 'package:wisatabandung/model/tourism_place.dart';
13. Untuk berkas aset yang digunakan dapat Anda unduh pada tautan
berikut.
14. Sesuai yang telah kita pelajari pada materi ListView, kita akan
menampilkan variabel tourismPlaceList di atas menjadi item card
yang dapat diklik. Tambahkan widget ListView sebagai body dari
Scaffold. Pindahkan widget FlatButton dan seluruh konten di
dalamnya sebagai widget dari setiap item di dalam tourismPlaceList.
​ class MainScreen extends StatelessWidget {
​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ appBar: AppBar(
​ title: Text('Wisata Bandung'),
​ ),
​ body: ListView.builder(
​ itemBuilder: (context, index) {
​ final TourismPlace place =
tourismPlaceList[index];
​ return InkWell(
​ onTap: () {
​ Navigator.push(context,
MaterialPageRoute(builder: (context) {
​ return DetailScreen();
​ }));
​ },
​ child: Card(
​ child: Row(
​ crossAxisAlignment:
CrossAxisAlignment.start,
​ children: <Widget>[
​ Expanded(
​ flex: 1,
​ child: Image.asset(place.imageAsset),
​ ),
​ Expanded(
​ flex: 2,
​ child: Padding(
​ padding: const
EdgeInsets.all(8.0),
​ child: Column(
​ crossAxisAlignment:
CrossAxisAlignment.start,
​ children: <Widget>[
​ Text(
​ place.name,
​ style: const
TextStyle(fontSize: 16.0),
​ ),
​ const SizedBox(
​ height: 10,
​ ),
​ Text(place.location),
​ ],
​ ),
​ ),
​ )
​ ],
​ ),
​ ),
​ );
​ },
​ itemCount: tourismPlaceList.length,
​ ),
​ );
​ }
​ }
15. Jangan lupa untuk mengganti tampilan item secara dinamis
sesuai data dari objek TourismPlace. Jalankan aplikasi untuk
melihat hasil perubahan.
16. Agar halaman detail bisa menampilkan informasi sesuai tempat
wisata yang dipilih, kita perlu mengirimkan data TourismPlace melalui
constructor. Buka berkas detail_screen.dart lalu tambahkan variabel
serta constructor-nya.
​ class DetailScreen extends StatelessWidget {
​ final TourismPlace place;

​ const DetailScreen({Key? key, required this.place}) :
super(key: key);

​ @override
​ Widget build(BuildContext context) {...}
​ }
17. Tambahkan keyword required agar parameter place wajib
disertakan ketika membuat objek DetailScreen. Sesuaikan juga
informasi yang ditampilkan dengan property yang didapat dari
constructor.
​ class DetailScreen extends StatelessWidget {
​ final TourismPlace place;

​ const DetailScreen({Key? key, required this.place}) :
super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ backgroundColor: Colors.black,
​ body: SingleChildScrollView(
​ child: Column(
​ crossAxisAlignment:
CrossAxisAlignment.stretch,
​ children: <Widget>[
​ Image.asset(place.imageAsset),
​ Container(
​ margin: const EdgeInsets.only(top: 16.0),
​ child: Text(
​ place.name,
​ textAlign: TextAlign.center,
​ style: TextStyle(
​ fontSize: 30.0,
​ fontFamily: 'Staatliches',
​ ),
​ ),
​ ),
​ Container(
​ margin: const
EdgeInsets.symmetric(vertical: 16.0),
​ child: Row(
​ mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
​ children: <Widget>[
​ Column(
​ children: <Widget>[
​ const Icon(Icons.calendar_today),
​ const SizedBox(height: 8.0),
​ Text(
​ place.openDays,
​ style: informationTextStyle,
​ ),
​ ],
​ ),
​ Column(
​ children: <Widget>[
​ const Icon(Icons.access_time),
​ const SizedBox(height: 8.0),
​ Text(
​ place.openTime,
​ style: informationTextStyle,
​ ),
​ ],
​ ),
​ Column(
​ children: <Widget>[
​ const Icon(Icons.monetization_on),
​ const SizedBox(height: 8.0),
​ Text(
​ place.ticketPrice,
​ style: informationTextStyle,
​ ),
​ ],
​ ),
​ ],
​ ),
​ ),
​ Container(
​ padding: const EdgeInsets.all(16.0),
​ child: Text(
​ place.description,
​ textAlign: TextAlign.center,
​ style: const TextStyle(
​ fontSize: 16.0,
​ fontFamily: 'Oxygen',
​ ),
​ ),
​ ),
​ Container(
​ height: 150,
​ child: ListView(
​ scrollDirection: Axis.horizontal,
​ children: place.imageUrls.map((url) {
​ return Padding(
​ padding: const EdgeInsets.all(4.0),
​ child: ClipRRect(
​ borderRadius:
BorderRadius.circular(10),
​ child: Image.network(url),
​ ),
​ );
​ }).toList(),
​ ),
​ ),
​ ],
​ ),
​ ),
​ );
​ }
​ }
18. Jangan lupa untuk menambahkan data place pada constructor.
​ Navigator.push(context, MaterialPageRoute(builder:
(context) {
​ return DetailScreen(place: place);
​ }));
19. Selanjutnya, kita akan menambahkan tombol navigasi untuk
kembali ke halaman daftar tempat wisata. Tombol ini akan kita taruh
di atas gambar utama atau gambar dari aset. Kita akan
menggunakan widget Stack. Widget ini digunakan untuk menyusun
widget seperti Column atau Row, bedanya widget pada Stack
disusun secara bertumpuk (stacked). Ubah kode Anda menjadi
seperti berikut:
​ Stack(
​ children: <Widget>[
​ Image.asset(place.imageAsset),
​ IconButton(icon: const Icon(Icons.arrow_back),
onPressed: () {})
​ ],
​ ),
20. Tambahkan fungsionalitas agar ketika icon back ini diklik akan
kembali ke halaman sebelumnya.
​ IconButton(
​ icon: const Icon(Icons.arrow_back),
​ onPressed: () {
​ Navigator.pop(context);
​ },
​ ),
21. Jika Anda jalankan aplikasi, ikon ini akan sedikit menabrak
notification bar pada perangkat Android. Hal ini akan semakin terlihat
apabila Anda menggunakan perangkat yang memiliki notch.
Lalu bagaimana mengatasinya? Masih ingat dengan SafeArea? Kita
akan memanfaatkan widget SafeArea yang akan memberikan
padding sesuai dengan sistem operasi yang digunakan sehingga
widget akan berada di area yang aman. Buat widget SafeArea lalu
pindahkan IconButton ke dalamnya.
​ SafeArea(
​ child: IconButton(
​ icon: Icon(Icons.arrow_back),
​ onPressed: () {
​ Navigator.pop(context);
​ },
​ ),
​ ),
22. Lakukan juga beberapa perubahan tampilan supaya ikon navigasi
tidak bertabarakan dengan latar belakangnya.
​ SafeArea(
​ child: Padding(
​ padding: const EdgeInsets.all(8.0),
​ child: CircleAvatar(
​ backgroundColor: Colors.grey,
​ child: IconButton(
​ icon: const Icon(
​ Icons.arrow_back,
​ color: Colors.white,
​ ),
​ onPressed: () {
​ Navigator.pop(context);
​ },
​ ),
​ ),
​ ),
​ ),
23. Terakhir, kita akan membuat fitur untuk menambahkan favorit.
Fitur favorit ini memang belum lengkap, namun setidaknya cukup
memberikan Anda gambaran bagaimana mengubah state aplikasi
dan bagaimana widget dapat tampil sesuai dengan state yang ada.

Buat StatefulWidget pada berkas detail_screen.dart. Widget ini


akan kita gunakan untuk menampilkan ikon favorit.
​ class FavoriteButton extends StatefulWidget {
​ const FavoriteButton({Key? key}) : super(key: key);

​ @override
​ _FavoriteButtonState createState() =>
_FavoriteButtonState();
​ }

​ class _FavoriteButtonState extends State<FavoriteButton>
{
​ @override
​ Widget build(BuildContext context) {
​ return IconButton(
​ icon: Icon(Icons.favorite_border),
​ onPressed: () {},
​ );
​ }
​ }
24. Tambahkan variabel boolean pada class _FavoriteButtonState.
Variabel ini merupakan sebuah state yang dapat berubah dan widget
kita akan tampil sesuai state-nya.
​ bool isFavorite = false;
25. State isFavorite ini akan berubah ketika ikon favorit diklik.
Sehingga tambahkan kode untuk mengubah variabel isFavorite.
Pastikan Anda memanggil fungsi setState untuk mengubah state.
​ onPressed: () {
​ setState(() {
​ isFavorite = !isFavorite;
​ });
​ },
26. Ubah ikon yang ditampilkan sesuai dengan kondisi state. Pada
kode di bawah ini kita menggunakan ekspresi ternary.
​ icon: Icon(
​ isFavorite ? Icons.favorite : Icons.favorite_border,
​ color: Colors.red,
​ ),
27. Tambahkan widget FavoriteButton ini sejajar dengan icon navigasi
back.
​ SafeArea(
​ child: Padding(
​ padding: const EdgeInsets.all(8.0),
​ child: Row(
​ mainAxisAlignment: MainAxisAlignment.spaceBetween,
​ children: [
​ CircleAvatar(
​ backgroundColor: Colors.grey,
​ child: IconButton(
​ icon: const Icon(
​ Icons.arrow_back,
​ color: Colors.white,
​ ),
​ onPressed: () {
​ Navigator.pop(context);
​ },
​ ),
​ ),
​ const FavoriteButton(),
​ ],
​ ),
​ ),
​ ),
28. Jalankan aplikasi dan lihat perubahannya. Ketika ikon favorit diklik
dan fungsi setState() dipanggil, maka method build akan kembali
dijalankan dan widget akan dibuat dan ditampilkan menurut
state-nya.

Selamat! Anda telah menyelesaikan seluruh codelab dan projek Wisata


Bandung. Seluruh kodenya adalah seperti berikut:
● main_screen.dart
​ import 'package:flutter/material.dart';
​ import 'package:wisatabandung/detail_screen.dart';
​ import 'package:wisatabandung/model/tourism_place.dart';

​ class MainScreen extends StatelessWidget {
​ const MainScreen({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ appBar: AppBar(
​ title: const Text('Wisata Bandung'),
​ ),
​ body: ListView.builder(
​ itemBuilder: (context, index) {
​ final TourismPlace place = tourismPlaceList[index];
​ return InkWell(
​ onTap: () {
​ Navigator.push(context,
MaterialPageRoute(builder: (context) {
​ return DetailScreen(place: place);
​ }));
​ },
​ child: Card(
​ child: Row(
​ crossAxisAlignment: CrossAxisAlignment.start,
​ children: <Widget>[
​ Expanded(
​ flex: 1,
​ child: Image.asset(place.imageAsset),
​ ),
​ Expanded(
​ flex: 2,
​ child: Padding(
​ padding: const EdgeInsets.all(8.0),
​ child: Column(
​ crossAxisAlignment:
CrossAxisAlignment.start,
​ children: <Widget>[
​ Text(
​ place.name,
​ style: const TextStyle(fontSize:
16.0),
​ ),
​ const SizedBox(
​ height: 10,
​ ),
​ Text(place.location),
​ ],
​ ),
​ ),
​ )
​ ],
​ ),
​ ),
​ );
​ },
​ itemCount: tourismPlaceList.length,
​ ),
​ );
​ }
​ }

​ detail_screen.dart

​ import 'package:flutter/material.dart';
​ import 'package:wisatabandung/model/tourism_place.dart';

​ var informationTextStyle = const TextStyle(fontFamily:
'Oxygen');

​ class DetailScreen extends StatelessWidget {
​ final TourismPlace place;

​ const DetailScreen({Key? key, required this.place}) :
super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ body: SingleChildScrollView(
​ child: Column(
​ crossAxisAlignment: CrossAxisAlignment.stretch,
​ children: <Widget>[
​ Stack(
​ children: <Widget>[
​ Image.asset(place.imageAsset),
​ SafeArea(
​ child: Padding(
​ padding: const EdgeInsets.all(8.0),
​ child: Row(
​ mainAxisAlignment:
MainAxisAlignment.spaceBetween,
​ children: [
​ CircleAvatar(
​ backgroundColor: Colors.grey,
​ child: IconButton(
​ icon: const Icon(
​ Icons.arrow_back,
​ color: Colors.white,
​ ),
​ onPressed: () {
​ Navigator.pop(context);
​ },
​ ),
​ ),
​ const FavoriteButton(),
​ ],
​ ),
​ ),
​ ),
​ ],
​ ),
​ Container(
​ margin: const EdgeInsets.only(top: 16.0),
​ child: Text(
​ place.name,
​ textAlign: TextAlign.center,
​ style: const TextStyle(
​ fontSize: 30.0,
​ fontFamily: 'Staatliches',
​ ),
​ ),
​ ),
​ Container(
​ margin: const EdgeInsets.symmetric(vertical:
16.0),
​ child: Row(
​ mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
​ children: <Widget>[
​ Column(
​ children: <Widget>[
​ const Icon(Icons.calendar_today),
​ const SizedBox(height: 8.0),
​ Text(
​ place.openDays,
​ style: informationTextStyle,
​ ),
​ ],
​ ),
​ Column(
​ children: <Widget>[
​ const Icon(Icons.access_time),
​ const SizedBox(height: 8.0),
​ Text(
​ place.openTime,
​ style: informationTextStyle,
​ ),
​ ],
​ ),
​ Column(
​ children: <Widget>[
​ const Icon(Icons.monetization_on),
​ const SizedBox(height: 8.0),
​ Text(
​ place.ticketPrice,
​ style: informationTextStyle,
​ ),
​ ],
​ ),
​ ],
​ ),
​ ),
​ Container(
​ padding: const EdgeInsets.all(16.0),
​ child: Text(
​ place.description,
​ textAlign: TextAlign.center,
​ style: const TextStyle(
​ fontSize: 16.0,
​ fontFamily: 'Oxygen',
​ ),
​ ),
​ ),
​ SizedBox(
​ height: 150,
​ child: ListView(
​ scrollDirection: Axis.horizontal,
​ children: place.imageUrls.map((url) {
​ return Padding(
​ padding: const EdgeInsets.all(4.0),
​ child: ClipRRect(
​ borderRadius:
BorderRadius.circular(10),
​ child: Image.network(url),
​ ),
​ );
​ }).toList(),
​ ),
​ ),
​ ],
​ ),
​ ),
​ );
​ }
​ }

​ class FavoriteButton extends StatefulWidget {
​ const FavoriteButton({Key? key}) : super(key: key);

​ @override
​ _FavoriteButtonState createState() =>
_FavoriteButtonState();
​ }

​ class _FavoriteButtonState extends State<FavoriteButton> {
​ bool isFavorite = false;

​ @override
​ Widget build(BuildContext context) {
​ return IconButton(
​ icon: Icon(
​ isFavorite ? Icons.favorite : Icons.favorite_border,
​ color: Colors.red,
​ ),
​ onPressed: () {
​ setState(() {
​ isFavorite = !isFavorite;
​ });
​ },
​ );
​ }
​ }

​ tourism_place.dart
​ class TourismPlace {
​ String name;
​ String location;
​ String description;
​ String openDays;
​ String openTime;
​ String ticketPrice;
​ String imageAsset;
​ List<String> imageUrls;

​ TourismPlace({
​ required this.name,
​ required this.location,
​ required this.description,
​ required this.openDays,
​ required this.openTime,
​ required this.ticketPrice,
​ required this.imageAsset,
​ required this.imageUrls,
​ });
​ }

​ var tourismPlaceList = [
​ TourismPlace(
​ name: 'Farm House Lembang',
​ location: 'Lembang',
​ description:
​ 'Berada di jalur utama Bandung-Lembang, Farm House
menjadi objek wisata yang tidak pernah sepi pengunjung.
Selain karena letaknya strategis, kawasan ini juga
menghadirkan nuansa wisata khas Eropa. Semua itu diterapkan
dalam bentuk spot swafoto Instagramable.',
​ openDays: 'Open Everyday',
​ openTime: '09:00 - 20:00',
​ ticketPrice: 'Rp 25000',
​ imageAsset: 'images/farm-house.jpg',
​ imageUrls: [

'https://media-cdn.tripadvisor.com/media/photo-s/0d/7c/59/70/
farmhouse-lembang.jpg',

'https://media-cdn.tripadvisor.com/media/photo-w/13/f0/22/f6/
photo3jpg.jpg',

'https://media-cdn.tripadvisor.com/media/photo-m/1280/16/a9/3
3/43/liburan-di-farmhouse.jpg'
​ ],
​ ),
​ TourismPlace(
​ name: 'Observatorium Bosscha',
​ location: 'Lembang',
​ description:
​ 'Memiliki beberapa teleskop, antara lain, Refraktor
Ganda Zeiss, Schmidt Bimasakti, Refraktor Bamberg, Cassegrain
GOTO, dan Teleskop Surya. Refraktor Ganda Zeiss adalah jenis
teleskop terbesar untuk meneropong bintang. Benda ini
diletakkan pada atap kubah sehingga saat teropong digunakan,
atap tersebut harus dibuka. Observatorium Bosscha boleh
dikunjungi oleh siapa pun, tanpa tiket. Namun, bagi yang
ingin menggunakan teleskop Zeiss, wajib mendaftarkan diri.
Untuk instansi atau lembaga pendidikan, diberikan jadwal hari
Selasa sampai Jumat. Sementara itu, kunjungan individu dibuka
setiap hari Sabtu.',
​ openDays: 'Open Tuesday - Saturday',
​ openTime: '09:00 - 14:30',
​ ticketPrice: 'Rp 20000',
​ imageAsset: 'images/bosscha.jpg',
​ imageUrls: [

'https://media-cdn.tripadvisor.com/media/photo-o/12/6b/63/0b/
bosscha-observatory.jpg',

'https://media-cdn.tripadvisor.com/media/photo-p/0d/6a/88/9b/
photo3jpg.jpg',

'https://media-cdn.tripadvisor.com/media/photo-o/11/3f/04/39/
p-20171111-110220-largejpg.jpg',
​ ],
​ ),
​ TourismPlace(
​ name: 'Jalan Asia Afrika',
​ location: 'Kota Bandung',
​ description:
​ 'Jalan Asia Afrika di Bandung memiliki kaitan yang
sangat erat dengan pendirian kota Kembang ini. Karena pada
saat itu, Gubernur Jenderal Herman Willem Deaendels dari
Belanda menancapkan tongkatnya saat memerintahkan pendirian
kota ini, yang kemudian diabadikan menjadi tugu Bandung Nol
Kilometer.',
​ openDays: 'Open Everyday',
​ openTime: '24 hours',
​ ticketPrice: 'Free',
​ imageAsset: 'images/jalan-asia-afrika.jpg',
​ imageUrls: [

'https://media-cdn.tripadvisor.com/media/photo-o/0d/c2/e7/e6/
quotes-kota-bandung.jpg',

'https://media-cdn.tripadvisor.com/media/photo-w/17/f4/44/01/
jalan-asia-afrika.jpg',

'https://media-cdn.tripadvisor.com/media/photo-s/0a/ef/36/e2/
jalan-asia-afrika.jpg',
​ ],
​ ),
​ TourismPlace(
​ name: 'Stone Garden',
​ location: 'Padalarang',
​ description:
​ 'Stone Garden atau Taman Batu di Padalarang – Bandung
ini adalah nama secara harafiah untuk apa yang akan kita
lihat jika berada di sana. Hamparan batu yang artistik
membuat kita merasa tidak sedang berada di Bandung, apalagi
di Padalarang. Hamparan batu yang dimaksud bukan terhampar
begitu saja di atas tanah luas yang menjadi permukaannya.
Batu-batu besar yang ukuran pastinya bervariasi tersusun
seperti memiliki suatu formasi matematis.',
​ openDays: 'Open Everyday',
​ openTime: '06:00 - 17:00',
​ ticketPrice: 'Rp 3000',
​ imageAsset: 'images/stone-garden.jpg',
​ imageUrls: [

'https://media-cdn.tripadvisor.com/media/photo-o/15/01/d7/4b/
p-20180510-153310-01.jpg',

'https://media-cdn.tripadvisor.com/media/photo-w/15/68/00/32/
stone-garden-citatah.jpg',

'https://media-cdn.tripadvisor.com/media/photo-o/0d/a2/cb/05/
stone-garden-citatah.jpg',
​ ],
​ ),
​ TourismPlace(
​ name: 'Taman Film Pasopati',
​ location: 'Kota Bandung',
​ description:
​ 'Menjadi salah satu tempat wisata di Bandung yang
favorit, tentu Taman Film ini memiliki fasilitas cukup
memadai. Pemberian fasilitas ini memiliki harapan para
pengunjung akan merasa nyaman dan tak segan2 untuk kembali
berkunjung terus menerus kesini. Beberapa fasilitas taman
yang bisa kamu nikmati diantaranya seperti layar videotron
besar berukuran 4×8 untuk memutar berbagai macam pilihan film
seperti Film Indonesia, Bollywood, Korea, ataupun Indie
Bandung.',
​ openDays: 'Open Everyday',
​ openTime: '24 hours',
​ ticketPrice: 'Free',
​ imageAsset: 'images/taman-film.jpg',
​ imageUrls: [

'https://media-cdn.tripadvisor.com/media/photo-o/08/8b/87/50/
bandung-movie-park.jpg',

'https://media-cdn.tripadvisor.com/media/photo-o/17/67/d5/53/
img-20190505-114509-largejpg.jpg',

'https://media-cdn.tripadvisor.com/media/photo-w/09/73/33/05/
taman-film-pasopati.jpg',
​ ],
​ ),
​ TourismPlace(
​ name: 'Museum Geologi',
​ location: 'Kota Bandung',
​ description:
​ 'Museum Geologi didirikan pada tanggal 16 Mei 1929.
Museum ini telah direnovasi dengan dana bantuan dari JICA
(Japan International Cooperation Agency). Setelah mengalami
renovasi, Museum Geologi dibuka kembali dan diresmikan oleh
Wakil Presiden RI, Megawati Soekarnoputri pada tanggal 23
Agustus 2000. Sebagai salah satu monumen bersejarah, museum
berada di bawah perlindungan pemerintah dan merupakan
peninggalan nasional. Dalam Museum ini, tersimpan dan
dikelola materi-materi geologi yang berlimpah, seperti fosil,
batuan, mineral. Kesemuanya itu dikumpulkan selama kerja
lapangan di Indonesia sejak 1850.',
​ openDays: 'Open Saturday - Thursday',
​ openTime: '09:00 - 15:30',
​ ticketPrice: 'Rp 3000',
​ imageAsset: 'images/museum-geologi.jpg',
​ imageUrls: [

'https://media-cdn.tripadvisor.com/media/photo-w/19/1c/8e/f7/
geology-museum.jpg',

'https://media-cdn.tripadvisor.com/media/photo-o/11/a7/35/b7/
geology-museum.jpg',

'https://media-cdn.tripadvisor.com/media/photo-s/1a/55/e0/dc/
geology-museum.jpg',
​ ],
​ ),
​ TourismPlace(
​ name: 'Floating Market',
​ location: 'Lembang',
​ description:
​ 'Tempat wisata ini sepertinya memang ditujukan untuk
wisata keluarga di Bandung. Di sini kita bisa menikmati
suasana kawasan yang tertata rapi dan alami. Pada awalnya,
floating market Lembang tidak begitu luas. Tapi sekarang
sudah ekspansi dan memiliki banyak objek menarik baru. Nama
floating market ini sepertinya merujuk pada stand tempat
jualan makanan yang berada dalam perahu.',
​ openDays: 'Open Everyday',
​ openTime: '09:00 - 17:00',
​ ticketPrice: 'Rp 20000',
​ imageAsset: 'images/floating-market.png',
​ imageUrls: [

'https://media-cdn.tripadvisor.com/media/photo-o/17/f9/ff/f8/
floating-market-bandung.jpg',

'https://media-cdn.tripadvisor.com/media/photo-p/1a/86/d3/cd/
20200103-125059-largejpg.jpg',

'https://media-cdn.tripadvisor.com/media/photo-p/19/ce/b4/9b/
img20181224120857-largejpg.jpg',
​ ],
​ ),
​ TourismPlace(
​ name: 'Kawah Putih',
​ location: 'Ciwidey',
​ description:
​ 'Kawah Putih adalah tempat wisata di Bandung yang
paling terkenal. Berlokasi di Ciwidey, Jawa Barat, kurang
lebih sekitar 50 KM arah selatan kota Bandung, Kawah Putih
adalah sebuah danau yang terbentuk akibat dari letusan Gunung
Patuha. Sesuai dengan namanya, tanah yang ada di kawasan ini
berwarna putih akibat dari pencampuran unsur belerang.',
​ openDays: 'Open Everyday',
​ openTime: '07:00 - 17:00',
​ ticketPrice: 'Rp 15000',
​ imageAsset: 'images/kawah-putih.jpg',
​ imageUrls: [

'https://media-cdn.tripadvisor.com/media/photo-o/0b/6e/7c/ce/
rocks-sticking-out-of.jpg',

'https://media-cdn.tripadvisor.com/media/photo-p/0b/35/30/14/
white-crater.jpg',

'https://media-cdn.tripadvisor.com/media/photo-o/0a/8b/9a/79/
2945-t00572-www-initempatwisat.jpg',
​ ],
​ ),
​ TourismPlace(
​ name: 'Ranca Upas',
​ location: 'Ciwidey',
​ description:
​ 'Ranca Upas Ciwidey adalah kawasan bumi perkemahan di
bawah pengelolaan perhutani. Tempat ini berada di kawasan
wisata Bandung Selatan, satu lokasi dengan kawah putih, kolam
Cimanggu dan situ Patenggang. Banyak hal yang bisa dilakukan
di kawasan wisata ini, seperti berkemah, berinteraksi dengan
rusa, sampai bermain di water park dan mandi air panas.',
​ openDays: 'Open Everyday',
​ openTime: '24 hours',
​ ticketPrice: 'Rp 20000',
​ imageAsset: 'images/ranca-upas.jpg',
​ imageUrls: [

'https://media-cdn.tripadvisor.com/media/photo-o/1a/e0/7f/9c/
kampung-cai-ranca-upas.jpg',

'https://media-cdn.tripadvisor.com/media/photo-w/13/ee/2f/87/
ranca-upas.jpg',

'https://media-cdn.tripadvisor.com/media/photo-w/13/ee/27/0a/
ranca-upas.jpg',
​ ],
​ ),
​ ];

Anda dapat mengunduh seluruh kodenya pada tautan berikut:

● Codelab-3

Tambahan: Flutter dikenal dengan framework-nya yang sangat mudah


dalam menghadirkan tampilan yang menarik termasuk menambahkan
animasi. Salah satu yang paling mudah adalah Hero Animation. Sebagai
tantangan, bisakah Anda menambahkan Hero Animation pada aplikasi
Anda? Dicoding sudah pernah mengulasnya dalam blog berikut:
https://www.dicoding.com/blog/menerapkan-animasi-pada-project-flutter/

Codelab 4: Pengembangan untuk Web


Sejauh ini kita telah mengembangkan aplikasi Wisata Bandung untuk
platform mobile. Dengan Flutter mengumumkan Flutter Web yang sudah
masuk versi stable, artinya terdapat target platform tambahan yang perlu
kita kerjakan jika ingin menyasar pengguna yang lebih luas. Bukan hanya
web, pada codelab kali ini kita juga akan menerapkan layout yang
responsif untuk platform mobile untuk ukuran layar yang lebih besar seperti
tablet.

Hasil akhir dari codelab ini akan seperti berikut:


Mari kita mulai!

1. Silakan buka project aplikasi Wisata Bandung Anda.


2. Pastikan di dalam proyek terdapat folder web. Apabila tidak ada,
jalankan perintah berikut untuk mengaktifkan konfigurasi web.
​ flutter config --enable-web

Kemudian, untuk menambahkan target dukungan untuk web,


jalankan perintah di bawah pada terminal:

​ flutter create .

Folder web akan terbangkitkan secara otomatis. Folder tersebut


berisikan berkas pendukung web seperti index.html, manifest.json,
dan lainnya.

3. Sekarang jalankan aplikasi Anda. Aplikasi akan berjalan dengan baik


di browser. Akan tetapi kita akan menemui masalah dalam aspek
ukuran, seperti list yang terlalu besar seperti berikut:

4. Karena kita telah membuat aplikasi mobile lalu ingin membuat versi
webnya, maka secara tidak langsung kita telah menerapkan
mobile-first design. Sekarang kita perlu menentukan pada ukuran
berapa layout mobile ini sudah tidak sesuai dan perlu diubah
tampilannya. Untuk memudahkan, mari kita tampilkan lebar browser
atau layar ke dalam teks AppBar.
​ AppBar(
​ title:
​ Text('Wisata Bandung. Size:
${MediaQuery.of(context).size.width}'),
​ ),
5. Cobalah mengubah ukuran window browser dan cari breakpoint atau
titik di mana layout sudah tidak sesuai.

Sebagai contoh, pada ukuran lebar di atas 600 sudah banyak ruang
kosong yang lebih baik ditempati oleh widget lain. Maka, kita akan
gunakan ukuran lebar 600 sebagai batas breakpoint ukuran mobile.

6. Pada ukuran lebar di atas 600 kita akan buat tampilan yang berbeda
menggunakan grid. Sebelumnya, mari pindahkan tampilan ListView
menjadi widget tersendiri.
​ class MainScreen extends StatelessWidget {
​ const MainScreen({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ appBar: AppBar(
​ title:
​ Text('Wisata Bandung. Size:
${MediaQuery.of(context).size.width}'),
​ ),
​ body: TourismPlaceList(),
​ );
​ }
​ }

​ class TourismPlaceList extends StatelessWidget {
​ const TourismPlaceList({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return ListView.builder(...);
​ }
​ }
7. Kemudian buat widget baru untuk menampilkan GridView.
​ class TourismPlaceGrid extends StatelessWidget {
​ const TourismPlaceGrid({Key? key}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Padding(
​ padding: const EdgeInsets.all(24.0),
​ child: GridView.count(
​ crossAxisCount: 4,
​ children: [],
​ ),
​ );
​ }
​ }
8. Selanjutnya ubah body dari MainScreen untuk menampilkan
TourismPlaceList atau TourismPlaceGrid. Kita akan
memanfaatkan widget LayoutBuilderuntuk mendapatkan ukuran
layar.
​ class MainScreen extends StatelessWidget {
​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(
​ appBar: AppBar(
​ title:
​ Text('Wisata Bandung. Size:
${MediaQuery.of(context).size.width}'),
​ ),
​ body: LayoutBuilder(
​ builder: (BuildContext context, BoxConstraints
constraints) {
​ if (constraints.maxWidth <= 600) {
​ return TourismPlaceList();
​ } else {
​ return TourismPlaceGrid();
​ }
​ },
​ ),
​ );
​ }
​ }

Tampilan aplikasi di atas lebar 600 akan kosong karena kita belum
mendefinisikan item untuk ditampilkan.

9. Tampilan item grid yang akan kita buat akan seperti di bawah ini.
Cobalah untuk membuat tampilan Card seperti ini dulu sebelum
melihat kode pada langkah berikutnya.

10. Berikut ini adalah tampilan untuk Card di atas:


​ GridView.count(
​ crossAxisCount: 4,
​ children: tourismPlaceList.map((place) {
​ return InkWell(
​ onTap: () {
​ Navigator.push(context,
MaterialPageRoute(builder: (context) {
​ return DetailScreen(place: place);
​ }));
​ },
​ child: Card(
​ child: Column(
​ crossAxisAlignment:
CrossAxisAlignment.stretch,
​ children: [
​ Expanded(
​ child: Image.asset(
​ place.imageAsset,
​ fit: BoxFit.cover,
​ ),
​ ),
​ const SizedBox(height: 8),
​ Padding(
​ padding: const EdgeInsets.only(left: 8.0),
​ child: Text(
​ place.name,
​ style: const TextStyle(
​ fontSize: 16.0,
​ fontWeight: FontWeight.bold,
​ ),
​ ),
​ ),
​ Padding(
​ padding: const EdgeInsets.only(left: 8.0,
bottom: 8.0),
​ child: Text(
​ place.location,
​ ),
​ ),
​ ],
​ ),
​ ),
​ );
​ }).toList(),
​ ),
11.Jalankan aplikasi untuk melihat perubahan. Anda dapat melakukan
duplikasi data dummy agar tampilan data menjadi semakin banyak.
Namun, untuk ukuran resolusi layar yang lebih besar lagi tampilan
grid dengan empat kolom akan terlihat terlalu besar.

Geser kembali ukuran jendela browser untuk menemukan titik di


mana tampilan sudah harus berubah. Misalnya, kita ambil ukuran
lebar di atas 1200 akan menampilkan 6 kolom.

12. Tambahkan lagi kondisi pada LayoutBuilder. Kita akan


menentukan jumlah kolom yang digunakan melalui parameter
constructor.
​ LayoutBuilder(
​ builder: (BuildContext context, BoxConstraints
constraints) {
​ if (constraints.maxWidth <= 600) {
​ return TourismPlaceList();
​ } else if (constraints.maxWidth <= 1200) {
​ return TourismPlaceGrid(gridCount: 4);
​ } else {
​ return TourismPlaceGrid(gridCount: 6);
​ }
​ },
​ ),
13. Tambahkan constructor pada kelas TourismPlaceGrid.
​ class TourismPlaceGrid extends StatelessWidget {
​ final int gridCount;

​ const TourismPlaceGrid({Key? key, required
this.gridCount}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Padding(
​ padding: const EdgeInsets.all(16.0),
​ child: GridView.count(
​ crossAxisCount: gridCount,
​ children: tourismPlaceList.map((place) {
​ return InkWell(...);
​ }).toList(),
​ ),
​ );
​ }
​ }
14. Jalankan aplikasi dan lihat perubahan. Tampilan untuk lebar
resolusi 1920 sudah lebih baik dari sebelumnya.

15. Selanjutnya mari tambahkan jarak antara item supaya tidak terlalu
rapat.
​ GridView.count(
​ crossAxisCount: gridCount,
​ crossAxisSpacing: 16,
​ mainAxisSpacing: 16,
​ children: tourismPlaceList.map((place) {
​ return InkWell(...);
​ }).toList(),
​ ),
16. Jangan lupa untuk menghapus kembali teks ukuran pada AppBar.
​ AppBar(
​ title: Text('Wisata Bandung'),
​ ),
17. Selanjutnya kita akan mulai memodifikasi tampilan halaman detail.
Seperti yang Anda lihat ketika menjalankan aplikasi, halaman detail
tampak kurang estetik untuk ukuran resolusi yang lebar. Untuk
ukuran lebar di atas 800, kita akan membuat tampilannya seperti ini:

18. Kita gunakan LayoutBuilderuntuk memisahkan tampilan


berdasarkan breakpoint.
​ class DetailScreen extends StatelessWidget {
​ final TourismPlace place;

​ const DetailScreen({Key? key, required this.place}) :
super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return LayoutBuilder(
​ builder: (BuildContext context, BoxConstraints
constraints) {
​ if (constraints.maxWidth > 800) {
​ return DetailWebPage(place: place);
​ } else {
​ return DetailMobilePage(place: place);
​ }
​ },
​ );
​ }
​ }

Pindahkan widget yang sudah ada ke dalam kelas


DetailMobilePage.

​ class DetailMobilePage extends StatelessWidget {


​ final TourismPlace place;

​ const DetailMobilePage({Key? key, required
this.place}) : super(key: key);

​ @override
​ Widget build(BuildContext context) {
​ return Scaffold(...);
​ }
​ }

Buat juga kelas serupa untuk DetailWebPage.

19. Tidak ada perbedaan konten yang ditampilkan pada halaman


detail, tetapi hanya susunannya saja yang berbeda. Dari gambar
sebelumnya apakah Anda dapat menganalisis bagaimana layoutnya
disusun? Jika belum, maka kurang lebihnya akan seperti berikut:

20. Mari kita mulai dengan Column pertama. Tambahkan Text untuk
judul, lalu Row untuk struktur layout di bawahnya. Tambahkan jarak
menggunakan widget SizedBox.
​ Scaffold(
​ body: Column(
​ crossAxisAlignment: CrossAxisAlignment.start,
​ children: [
​ const Text(
​ 'Wisata Bandung',
​ style: TextStyle(
​ fontFamily: 'Staatliches',
​ fontSize: 32,
​ ),
​ ),
​ const SizedBox(height: 32),
​ Row(
​ children: [],
​ ),
​ ],
​ ),
​ );
21. Widget Row akan berisi children seperti berikut:
​ Row(
​ crossAxisAlignment: CrossAxisAlignment.start,
​ children: [
​ Expanded(
​ child: Column(
​ children: [],
​ ),
​ ),
​ const SizedBox(width: 32),
​ Expanded(
​ child: Card(...),
​ ),
​ ],
​ ),

Sementara Card untuk detail informasi tempat wisata memiliki widget


seperti berikut:

​ Card(
​ child: Container(
​ padding: const EdgeInsets.all(16),
​ child: Column(
​ mainAxisSize: MainAxisSize.max,
​ children: <Widget>[
​ Container(
​ child: Text(
​ place.name,
​ textAlign: TextAlign.center,
​ style: const TextStyle(
​ fontSize: 30.0,
​ fontFamily: 'Staatliches',
​ ),
​ ),
​ ),
​ Row(
​ mainAxisAlignment:
MainAxisAlignment.spaceBetween,
​ children: [
​ Row(
​ children: <Widget>[
​ const Icon(Icons.calendar_today),
​ const SizedBox(width: 8.0),
​ Text(
​ place.openDays,
​ style: informationTextStyle,
​ ),
​ ],
​ ),
​ const FavoriteButton(),
​ ],
​ ),
​ Row(
​ children: <Widget>[
​ const Icon(Icons.access_time),
​ const SizedBox(width: 8.0),
​ Text(
​ place.openTime,
​ style: informationTextStyle,
​ ),
​ ],
​ ),
​ const SizedBox(height: 8.0),
​ Row(
​ children: <Widget>[
​ const Icon(Icons.monetization_on),
​ const SizedBox(width: 8.0),
​ Text(
​ place.ticketPrice,
​ style: informationTextStyle,
​ ),
​ ],
​ ),
​ Container(
​ padding: const EdgeInsets.symmetric(vertical:
16.0),
​ child: Text(
​ place.description,
​ textAlign: TextAlign.justify,
​ style: const TextStyle(
​ fontSize: 16.0,
​ fontFamily: 'Oxygen',
​ ),
​ ),
​ ),
​ ],
​ ),
​ ),
​ ),
22. Widget Column terakhir kita gunakan untuk menampilkan gambar
dari aset dan daftar gambar dari internet.
​ Column(
​ children: [
​ ClipRRect(
​ child: Image.asset(place.imageAsset),
​ borderRadius: BorderRadius.circular(10),
​ ),
​ const SizedBox(height: 16),
​ Container(
​ height: 150,
​ padding: const EdgeInsets.only(bottom: 16),
​ child: ListView(
​ scrollDirection: Axis.horizontal,
​ children: place.imageUrls.map((url) {
​ return Padding(
​ padding: const EdgeInsets.all(4.0),
​ child: ClipRRect(
​ borderRadius: BorderRadius.circular(10),
​ child: Image.network(url),
​ ),
​ );
​ }).toList(),
​ ),
​ ),
​ ],
​ ),
Sekarang tampilan aplikasi akan seperti ini:

23. Mari tambahkan Padding untuk memberikan jarak antara konten


dengan pinggir halaman.
​ Scaffold(
​ body: Padding(
​ padding: const EdgeInsets.symmetric(
​ vertical: 16,
​ horizontal: 64,
​ ),
​ child: Column(...),
​ ),
​ );
24. Kita juga dapat menentukan ukuran lebar maksimum dari konten
yang tampil menggunakan SizedBox.
​ Padding(
​ padding: const EdgeInsets.symmetric(
​ vertical: 16,
​ horizontal: 64,
​ ),
​ child: Center(
​ child: SizedBox(
​ width: 1200,
​ child: Column(...),
​ ),
​ ),
​ ),

Lihat perubahan aplikasi, sekarang halaman detail akan memiliki


ukuran lebar maksimal 1200 pixel.

25. Satu hal yang penting untuk kita perhatikan adalah menghadirkan
pengalaman pengguna yang nyaman saat menggunakan platform
apa pun. Untuk itu, tambahkan Scrollbar pada daftar gambar. Selain
itu, tambahkan juga widget Container untuk memberikan ukuran
tinggi pada ListView.
​ Scrollbar(
​ child: Container(
​ height: 150,
​ padding: const EdgeInsets.only(bottom: 16),
​ child: ListView(
​ scrollDirection: Axis.horizontal,
​ children: widget.place.imageUrls.map((url) {
​ return Padding(...);
​ }).toList(),
​ ),
​ ),
​ ),
26. Jika Anda menggunakan touchpad pada laptop, gestur swipe
pada scrollbar akan berjalan dengan normal. Namun, ketika
melakukan drag menggunakan mouse, fitur scrollbar tidak berjalan.
Solusi yang bisa Anda lakukan adalah menggunakan
ScrollController. Inisialisasikan objek ScrollController di atas method
build().
​ final _scrollController = ScrollController();

Tambahkan _scrollController pada Scrollbar dan ListView.

​ Scrollbar(
​ controller: _scrollController,
​ child: Container(
​ height: 150,
​ padding: const EdgeInsets.only(bottom: 16),
​ child: ListView(
​ controller: _scrollController,
​ scrollDirection: Axis.horizontal,
​ children: widget.place.imageUrls.map((url) {
​ return Padding(...);
​ }).toList(),
​ ),
​ ),
​ ),
27. Seperti halnya TextEditingController, ScrollController juga harus
di-dispose ketika widget sudah tidak lagi digunakan. Ubahlah
DetailWebPage menjadi StatefulWidget supaya kita bisa memanggil
method dispose.

28. Tambahkan method dispose setelah method build() lalu panggil


_scrollController.dispose();
​ @override
​ void dispose() {
​ _scrollController.dispose();
​ super.dispose();
​ }
29. Selamat! Sekarang tampilan aplikasi Wisata Bandung sudah lebih
sesuai untuk ukuran resolusi layar yang lebih besar. Anda telah
belajar banyak menyusun halaman menggunakan widget, hingga
membuat halaman yang dapat responsif terhadap perbedaan ukuran
layar.

Kode selengkapnya untuk codelab ini dapat Anda temukan pada tautan:

● Codelab-4

Rangkuman Materi
Selamat! Anda telah belajar banyak di modul ini. Ayo kita ingat dan ulas
kembali apa saja yang sudah kita pelajari.

● Pada Flutter, semuanya adalah widget. Widget merupakan


komponen yang perlu saling disusun untuk membangun aplikasi
Flutter.
● Widget pada Flutter terbagi menjadi StatelessWidget dan
StatefulWidget. State sendiri merupakan data yang ada pada suatu
widget dan dapat berubah sesuai interaksi pengguna.
● Scaffold merupakan sebuah widget yang digunakan untuk membuat
tampilan dasar atau kerangka material design pada aplikasi Flutter.
Di dalam Scaffold, kita bisa menambahkan widget AppBar, Body, dan
FloatingActionButton.
● Container merupakan widget yang digunakan untuk menampung
widget lain. Di dalam Container kita bisa menambahkan style, shape
(bentuk), dan dekorasi lain pada child widget-nya.
● Widget Padding sesuai namanya merupakan widget untuk
menambahkan padding atau jarak antara konten dengan border-nya.
● Center digunakan untuk membuat child widget berada di posisi
tengah.
● Widget Row dan Column digunakan untuk menyusun beberapa
widget secara horizontal atau vertikal.
● Flutter menyediakan beberapa tampilan tombol (button), seperti
ElevatedButton, TextButton, OutlinedButton, IconButton, dan
DropdownButton.
● Terdapat beberapa widget yang dapat digunakan untuk menerima
input dari pengguna, di antaranya adalah TextField, Switch, Radio,
dan Checkbox.
● Widget Image digunakan untuk menampilkan gambar dari internet
atau aset proyek.
● Kita dapat menambahkan font sendiri sebagai aset lalu
menampilkannya menggunakan widget TextStyle.
● ListView digunakan untuk menampilkan beberapa item dalam bentuk
baris atau kolom yang bisa di-scroll.
● Flutter menyediakan widget Flexible dan Expanded yang dapat
memberikan ukuran secara fleksibel dan menempati ruang yang
tersedia.
● Kita dapat memanfaatkan MediaQuery atau widget LayoutBuilder
untuk mendapatkan ukuran layar aplikasi yang dijalankan. Dengan
ini, kita bisa menentukan breakpoint untuk membuat tampilan yang
responsif.
● Flutter dapat melakukan navigasi antar halaman dengan
memanfaatkan Navigator.
Deployment

Deployment
Setelah melalui tahapan pengembangan aplikasi, salah satu tahapan
terakhir yang perlu dilakukan adalah deployment. Tahapan ini dimulai dari
proses build yang membungkus project aplikasi kita menjadi berkas yang
bisa dieksekusi pada masing-masing platform, misalnya format APK
(Android Application Package) atau AAB (Android App Bundle) untuk
Android dan IPA (iOS App Store Package) untuk iOS. Berkas ini yang
nantinya akan didistribusikan ke Google Play Store atau pun App Store.

Jika Anda belum memahami berkas APK atau IPA, Anda dapat
menyamakannya dengan berkas EXE (Executable) pada sistem operasi
Windows.

Cara melakukan build ini terbilang mudah. Akan kami jelaskan pada
pembahasan selanjutnya.

Build APK
Project Flutter yang telah dibuat dapat kita build menjadi berkas .apk yang
dapat berjalan di Android. Build APK adalah suatu proses membungkus
aplikasi flutter menjadi format .apk yang nantinya untuk diinstal pada
perangkat Android. Berikut tahapan ketika build aplikasi Flutter ke APK.

AndroidManifest.xml
Sebelum mem-build APK, kita akan mengatur berkas
android/app/src/main/AndroidManifest.xml. AndroidManifest.xml
merupakan sebuah berkas yang berisikan informasi mengenai aplikasi
Android yang akan di-build. Informasi-informasi tersebut berupa nama
aplikasi, ikon, permission, screen orientation, dan lain-lain. Isi dari
AndroidManifest.xml seperti berikut:

​ <manifest
xmlns:android="http://schemas.android.com/apk/res/android"
​ package="id.eudeka.samples">
​ <application
​ android:name="io.flutter.app.FlutterApplication"
​ android:label="samples"
​ android:icon="@mipmap/ic_launcher">
​ <activity
​ android:name=".MainActivity"
​ android:launchMode="singleTop"
​ android:theme="@style/LaunchTheme"

android:configChanges="orientation|keyboardHidden|keyboard|sc
reenSize|smallestScreenSize|locale|layoutDirection|fontScale|
screenLayout|density|uiMode"
​ android:hardwareAccelerated="true"
​ android:windowSoftInputMode="adjustResize">
​ <meta-data

android:name="io.flutter.embedding.android.NormalTheme"
​ android:resource="@style/NormalTheme"
​ />
​ <meta-data

android:name="io.flutter.embedding.android.SplashScreenDrawab
le"
​ android:resource="@drawable/launch_background"
​ />
​ <intent-filter>
​ <action
android:name="android.intent.action.MAIN"/>
​ <category
android:name="android.intent.category.LAUNCHER"/>
​ </intent-filter>
​ </activity>
​ <meta-data
​ android:name="flutterEmbedding"
​ android:value="2" />
​ </application>
​ </manifest>

Setting Nama Aplikasi


Untuk mengatur nama aplikasi, kita cukup mengubah properti android:label
yang ada pada file AndroidManifest.xml seperti berikut:
​ <application
​ android:name="io.flutter.app.FlutterApplication"
​ android:label="common_widget"
​ android:icon="@mipmap/ic_launcher">
menjadi

​ <application
​ android:name="io.flutter.app.FlutterApplication"
​ android:label="Nama Aplikasi"
​ android:icon="@mipmap/ic_launcher">
Isikan android:label dengan nama aplikasi yang diinginkan. Atau Anda bisa
gunakan library berikut untuk menghasilkan nama aplikasi dari
pubspec.yaml.

Setting Ikon Aplikasi


Secara default ikon aplikasi Flutter kita adalah ikon Flutter. Untuk
mengubah icon aplikasi dengan mudah, kita akan mengganti gambar
ic_launcher.png yang berada pada folder android/app/src/main/res/ yang
terbagi menjadi berbagai mipmap (ukuran resolusi ikon).

Hal yang pertama kita lakukan adalah membuat ikon aplikasi dengan
Android Asset Studio.
Dengan Android Asset Studio, kita dapat membuat ikon aplikasi dengan
mudah dan nantinya akan terbuat dalam berbagai resolusi (mipmap).
Setelah membuat ikon sesuai dengan keinginan, tekan tombol download
yang ada di kanan atas.

Setelah mengunduh, unzip-lah berkas tersebut dan temukan folder res/ di


dalamnya. Lalu copy folder res/ ke android/app/src/main/res/ untuk
mengganti ic_launcher.png pada setiap mipmap dengan ikon aplikasi yang
baru. Atau Anda bisa gunakan library berikut untuk menghasilkan icon
launcher dari pubspec.yaml.

Setting Perizinan Aplikasi


Ketika aplikasi dalam mode debug atau profil, perizinan internet akan
secara otomatis ditambahkan. Namun ketika Anda ingin menjalankan atau
membuatnya dalam mode rilis, Anda perlu menambahkan semua perizinan
yang dibutuhkan pada AndroidManifest.

Untuk menambahkan perizinan pada aplikasi Android, Anda bisa


menambahkan tag uses-permission pada AndroidManifest, di dalam tag
manifest dan sejajar tag application. Contohnya seperti di bawah ini:

​ <uses-permission android:name="android.permission.INTERNET"/>
Melakukan Build APK
Setelah kita mengatur nama dan ikon aplikasi, langkah selanjutnya adalah
melakukan build aplikasi menjadi APK. Sebelumnya terdapat tiga (3) jenis
mode aplikasi yang perlu diketahui, yaitu debug, profile, dan release. APK
debug umumnya digunakan untuk pengujian dan penggunaan aplikasi
secara internal. Mode debug digunakan secara default ketika menjalankan
aplikasi menggunakan perintah flutter run. Sementara untuk bisa dirilis
melalui Google Play Store, Anda perlu membuat APK release. Sedangkan
mode profile sama hal nya dengan release hanya saja tetap dapat
di-debug menggunakan tools seperti DevTools dan tidak dapat dijalankan
di emulator atau simulator.

Pada kelas ini kita akan mempelajari bagaimana membuat APK debug.
Caranya ialah menggunakan terminal pada Android Studio. Tekan tombol
Terminal yang ada pada pojok kiri bawah.

Bila menggunakan Visual Studio Code pilih menu terminal yang ada pada
menu kiri atas. Lalu pilih new terminal.
Jika terminal telah muncul, tuliskan perintah berikut:

​ flutter build apk --debug


Tunggu hingga proses build berhasil. Setelah berhasil, hasil build yang
berupa berkas apk-debug.apk akan terletak di folder
build/app/outputs/apk/debug/ atau akan muncul direktori tempat
tersimpannya berkas ketika proses build selesai pada Terminal.

Untuk bisa mem-build apk release dan mengunggahnya melalui Google


Play Store, Anda memerlukan signing key. Signing key ini digunakan
sebagai tanda tangan supaya aplikasi Anda lebih aman. Secara default
Flutter menggunakan debug key sebagai signing key sehingga Anda
sebenarnya bisa membuat apk release dengan menjalankan perintah
berikut:

​ flutter build apk


Namun, tentunya akan lebih baik jika Anda menggunakan signing key milik
Anda sendiri. Cara untuk membuat signing key dan membuat apk release
dapat Anda baca pada tautan dokumentasi berikut:
https://flutter.dev/docs/deployment/android.

Build IPA
Catatan: Build .IPA hanya bisa dijalankan dengan mendaftar akun Apple
Developer Program. Silakan baca informasi tentang Apple Developer
Program di sini https://developer.apple.com/programs/.

Untuk lulus dari kelas ini, tidak diwajibkan untuk bisa build .IPA.

Pada materi ini kita akan mempelajari bagaimana melakukan build aplikasi
Flutter menjadi berkas .ipa yang dapat dijalankan pada perangkat iOS.
Sebelum melakukan build, ada beberapa hal yang perlu kita atur seperti
nama dan ikon aplikasi.

Setting Nama Aplikasi


Untuk mengatur nama aplikasi buka berkas Info.plist pada direktori
/ios/Runner/. Konfigurasi untuk nama aplikasi dapat Anda temukan dan
ubah pada key Bundle Name.

​ <key>CFBundleName</key>
​ <string>builder</string>
Atau Anda bisa gunakan library berikut untuk men-generate nama aplikasi
melalui pubspec.yaml.

Setting Ikon Aplikasi


Sama seperti perangkat Android, layar untuk perangkat iPhone juga terbagi
ke dalam berbagai ukuran. Sehingga diperlukan juga ukuran ikon yang
berbeda. Untuk membuat berbagai ukuran ikon untuk iOS, Anda dapat
memanfaatkan website seperti https://appicon.co/ atau yang lainnya.
Website ini juga bisa digunakan untuk membuat ikon aplikasi Android.
Perlu Anda perhatikan bahwa untuk icon pada iOS Anda tidak dapat
membuatnya transparant. Alih-alih, Anda harus membuat icon tersebut
penuh dengan warna
Ketika Anda klik Generate, browser akan mengunduh berkas yang Anda
butuhkan. Ikon untuk aplikasi iOS bisa Anda dapatkan pada folder
Assets.xcassets.
Selanjutnya Anda dapat mengganti folder Assets.xcassets yang ada pada
direktori /ios/Runner/ dengan hasil ikon yang sudah Anda generate. Atau
Anda bisa menggunakan library berikut untuk men-generate ikon aplikasi
melalui pubspec.yaml.

Melakukan build IPA


File IPA juga terbagi menjadi debug, profile, dan release. Namun untuk
melakukan build aplikasi Flutter menjadi IPA hanya bisa dilakukan pada
device macOS. Untuk melakukan build aplikasi menjadi .ipa Anda cukup
membuka terminal pada editor atau IDE Anda lalu menjalankan perintah
berikut:

​ flutter build ios


Secara default perintah di atas akan menghasilkan ipa release, sedangkan
jika Anda ingin membuat versi debug atau profile, gunakan perintah:

​ flutter build ios --debug


Namun, bagi Anda yang tidak mempunyai perangkat Apple jangan
khawatir, Anda tetap dapat men-deploy project Anda ke iOS menggunakan
CI/CD seperti Codemagic dan Bitrise.

Selamat Anda telah berhasil membuat dan build project Flutter di Android
atau iOS. Selanjutnya Anda diharuskan untuk mengirim submission project
Flutter. Silakan lanjut ke materi berikutnya untuk menuju halaman
submission.

Web Deployment
Pada materi ini kita akan mempelajari bagaimana melakukan build aplikasi
flutter untuk platform web. Sama seperti platform android dan ios, informasi
untuk pengaturan aplikasi berada pada folder web.

Setting Nama Aplikasi


Untuk mengatur nama aplikasi, kita bisa membuka berkas manifest.json.
Konfigurasi untuk nama aplikasi dapat Anda temukan dan ubah pada key
name dan short_name.

​ {
​ "name": "wisata_bandung",
​ "short_name": "wisata_bandung"
​ }

Setting Icon Aplikasi


Platform web juga membutuhkan icon dalam berbagai ukuran. Icon untuk
web dapat Anda taruh pada folder /web/icons. Kemudian, Anda perlu
mendaftarkannya pada berkas manifest.json.

​ {
​ ...
​ "icons": [
​ {
​ "src": "icons/Icon-192.png",
​ "sizes": "192x192",
​ "type": "image/png"
​ },
​ {
​ "src": "icons/Icon-512.png",
​ "sizes": "512x512",
​ "type": "image/png"
​ }
​ ]
​ }

Melakukan build web


Sama seperti build aplikasi android dan ios, untuk mem-build aplikasi
Flutter web kita menjalankan perintah flutter web. Perintah selengkapnya
adalah seperti ini:

​ flutter build web


Sama seperti ketika menjalankan flutter web, ketika melakukan build, kita
juga bisa menentukan renderer yang ingin digunakan. Untuk menentukan
renderer yang digunakan, tambahkan parameter --web-renderer pada
perintah flutter build. Jika tidak mendefinisikan parameter --web-renderer
maka mode auto yang akan digunakan.

Hasil build akan Anda temukan pada folder /build/web. Folder inilah yang
nantinya bisa Anda deploy ke sebuah web hosting atau web server.

Beberapa opsi hosting yang bisa Anda gunakan, antara lain:

● Firebase Hosting
● GitHub Pages
● Google Cloud Hosting
Rangkuman Materi
Kita telah mempelajari praktik deployment Flutter untuk beberapa platform.
Mari kita ulas kembali apa saja yang sudah kita pelajari.

● Informasi terkait aplikasi Android yang ingin di-build tersedia pada


berkas android/app/src/main/AndroidManifest.xml. Pada berkas
ini kita bisa mengatur nama, ikon, perizinan aplikasi, dll. Ada dua
format build Android yang bisa dibuat, yaitu .apk dan .aab. Untuk
melakukan build jalankan perintah berikut:
​ flutter build apk // Untuk format apk
​ flutter build appbundle // Untuk format aab
● Konfigurasi untuk aplikasi iOS tersedia pada berkas
ios/Runner/Info.plist. Pada berkas ini kita bisa mengatur nama
aplikasi, ikon, dan konfigurasi lainnya. Untuk melakukan build Flutter
menjadi file IPA, jalankan perintah berikut:
​ flutter build ios
● Konfigurasi untuk aplikasi web tersedia pada berkas
web/manifest.json. Pada berkas ini kita bisa mengatur nama aplikasi,
ikon, dan konfigurasi lainnya. Untuk melakukan build Flutter web,
jalankan perintah berikut:
​ flutter build web

Selamat! Anda telah menyelesaikan modul terakhir pada kelas ini. Anda
telah belajar banyak mulai dari proses instalasi hingga ke tahap
deployment aplikasi. Untuk bisa dinyatakan lulus dari kelas ini, Anda perlu
mengirimkan submission berupa aplikasi Flutter dengan tema bebas.
Manfaatkan materi yang sudah Anda pelajari dan kirimkan aplikasi terbaik
Anda ya! Good luck!

Anda mungkin juga menyukai