Bab ini membahas tentang konsep-konsep yang perlu dipahami ketika merancang tampilan layar
(user interface), seperti perbedaan antara kelas StatelessWidget dan StatefulWidgdet, pembuatan
tampilan dinamis menggunakan kelas StatefulWidget, pengaturan tata-letak komponen di layar,
dan pengaturan jarak antar-komponen.
Untuk memahami komponen dengan sifat stateless dan stateful, mula-mula kita harus mengetahui
tentang state. Dalam Flutter, state dinyatakan sebagai informasi yang dapat dibaca ketika suatu
komponen dibuat dan mungkin akan mengalami perubahan keadaan selama komponen tersebut
tampil di layar.
Komponen stateless adalah komponen yang tidak memiliki state dan digunakan untuk membuat
tampilan yang sifatnya statis (tidak berubah). Text, RaisedButton, dan Icon adalah contoh
komponen-komponen yang bersifat stateless.
Komponen stateful adalah komponen yang dilengkapi dengan state. Komponen ini digunakan
untuk membuat tampilan yang dapat menampung masukan dari pengguna aplikasi. Checkbox,
Radio, TextField, dan Slider adalah contoh komponen yang bcrsifat stateful. Sebagai contoh,
ketika pengguna aplikasi menekan komponen Checkbox yang ada di Jayar, maka keadaan (state
dari komponen terscbut akan berubah, misalnya dari kcadaan dipilih (checked, menjadi tidak
dipilih (unchecked) atau sebaliknya, Ketika terjadi perubahan keadaan, aplikasi akan mcnggambar
ulang komponen bersangkutan dengan tampilan yang baru.
Perbedaan-perbedaan dari kedua jenis komponen di atas dapat dilihat pada tabel di bawah ini.
Dalam kelas pertama kita perlu melakukan modifikasi (override) terhadap metode createState().
Metode ini bertugas untuk membuat objek dari kelas kedua dan mengembalikan objek tersebut
sebagai hasil metode.
atau
@override
HomeState createState() => HomeState();
}
pada contoh di atas, HomeState adalah nama kelas kedua. Dalam kelas kedua, kita perlu
melakukan override terhadap metode build ( ) untuk membangun tampilan layar menggunakan
komponen-komponen tertentu, seperti berikut:
@override
Widget build(BuildContext context) {
// . . .
}
}
Contoh di bawah ini akan menunjukkan konsep dasar pembuatan aplikasi yang memiliki layar
dinamis.
import 'package:flutter/material.dart';
void decrementCounter() {
setState(() {
counter--;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Demo StatefulWidget'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Nilai Counter : $counter',
style: TextStyle(fontSize: 30.0),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('<<'),
onPressed: decrementCounter,
),
Container(
width: 10.0,
),
RaisedButton(
child: Text('>>'),
onPressed: incrementCounter,
),
],
)
],
),
),
);
}
}
Pada contoh di atas kita menempatkan satu komponen Text dan dua komponen RaisedButton ke
dalam layar. Teks yang terdapat di dalam komponen Text akan berubah tergantung dari nilai
variabel counter. Untuk mengubah nilai variabel counter, kita mendefinisikan dua metode untuk
penanganan event onPressed dari objek RaisedButton pertama dan kedua.
void incrementCounter() {
setState(() {
counter++;
});
}
void decrementCounter() {
setState(() {
counter--;
});
}
Untuk menghubungkan kedua metode di atas dengan event onPressed pada Objek RaisedButton
pertama dan kedua, gunakan kode berikut:
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('<<'),
onPressed: decrementCounter,
),
Container(
width: 10.0,
),
RaisedButton(
child: Text('>>'),
onPressed: incrementCounter,
),
],
)
Untuk memasukkan komponen-komponen ke dalam kontainer dengan tipe Row, gunakan kode
berikut:
Row(
children: <Widget>[
//Komponen 1,
//Komponen 2,
//Komponen 3,
//. . .
],
);
import 'package:flutter/material.dart';
Pada contoh di atas kita menempatkan empat komponen RaisedButton ke dalam kontainer. Agar
keempat komponen tersebut tidak saling menempel kita dapat menyisipkan komponen Container
untuk menentukan jarak.
children: <Widget>[
RaisedButton(
child: Text('Button 1'),
onPressed: () {},
),
Container(
width: 10,
),
RaisedButton(
child: Text('Button 2'),
onPressed: () {},
),
Container(
width: 10,
),
RaisedButton(
child: Text('Button 3'),
onPressed: () {},
),
Container(
width: 10,
),
RaisedButton(
child: Text('Button 4'),
onPressed: () {},
),
],
Properti mainAxisAlignment pada kelas Row berfungsi untuk menentukan posisi komponen-
komponen pada saat ditampilkan ke layar. MainAxisAlignment.center akan menyebabkan
kontainer ditampilkan di posisi tengah (untuk arah horisontal).
mainAxisAlignment: MainAxisAlignment.center,
Komponen 1
Komponen 2 Kontainer
Komponen 3
Komponen 4
import 'package:flutter/material.dart';
Perilaku properti mainAxisA1ignment dan crossAxisAlignmen pada kelas column berbeda dengan
kelas Row. Pada kelas Column, mainAxisAlignment berfungsi untuk mengatur posisi kontainer
secar vertikal, sedangkan crossAxisA1ignment berfungsi untuk mengatu posisi kontainer secara
horisontal. Gambar di bawah ini menunjukkan cara kerja kedua properti tersebut ketika diterapkan
pada kontainer yang dibuat menggunakan kelas Column.
Memadukan Kelas Row dan Column
Pada kasus-kasus nyata dalam pembuatan aplikasi mobile, susunan komponen komponen yang
terdapat di dalam layar biasanya dibuat dengan memadukan penggunaan kelas Row dan Column.
Dalam satu kontainer Row bisa terdapat beberapa kontainer Column, seperti yang ditunjukkan
oleh contoh kode berikut :
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('Button 1'),
onPressed: () {},
),
RaisedButton(
child: Text('Button 2'),
onPressed: () {},
),
],
),
Container(
width: 10.0,
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('Button 3'),
onPressed: () {},
),
RaisedButton(
child: Text('Button 4'),
onPressed: () {},
),
],
),
Container(
width: 10.0,
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('Button 5'),
onPressed: () {},
),
RaisedButton(
child: Text('Button 6'),
onPressed: () {},
),
],
),
],
),
Satu kontainer Column juga dapat memiliki beberapa kontainer Row, seperti berikut:
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('Button 1'),
onPressed: () {},
),
Container(
width: 10.0,
),
RaisedButton(
child: Text('Button 2'),
onPressed: () {},
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('Button 3'),
onPressed: () {},
),
Container(
width: 10.0,
),
RaisedButton(
child: Text('Button 4'),
onPressed: () {},
),
],
),
Container(
width: 10.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('Button 5'),
onPressed: () {},
),
Container(
width: 10.0,
),
RaisedButton(
child: Text('Button 6'),
onPressed: () {},
),
],
),
],
),
import 'package:flutter/material.dart';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Objek Wisata'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Image.asset(
'images/menarapandang.jpg',
height: 250.0,
width: 412.0,
fit: BoxFit.fill,
),
Container(
width: 15.0,
),
Row(
children: <Widget>[
Container(
width: 15.0,
),
Column(
crossAxisAlignment: CrossAxisAlignment.star
t,
children: <Widget>[
Text(this.title,
style: TextStyle(
fontSize: 20.0, fontWeight: FontW
eight.bold)),
Text(
this.location,
style: TextStyle(
fontSize: 16.0,
color: Colors.grey,
),
),
],
),
Container(
width: 130.0,
),
Icon(
Icons.star,
size: 32.0,
color: Colors.red,
),
Text('5', style: TextStyle(fontSize: 14.0)),
],
),
Container(
padding: EdgeInsets.all(15.0),
child: Text(
this.description,
style: TextStyle(
fontSize: 18.0,
),
softWrap: true,
),
),
],
),
));
}
}
Pada contoh di atas baris pertama layar diisi dengan gambar menggunakan kode berikut:
Image.asset(
'images/menarapandang.jpg',
height: 250.0,
width: 412.0,
fit: BoxFit.fill,
),
Baris kedua diisi dengan kontainer Row. Dalam baris ini kolom pertama diisi kontainer Column,
kolom kedua diisi dengan icon bintang, dan kolom ketiga diisi dengan teks ‘5’.
Row(
children: <Widget>[
Container(
width: 15.0,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(this.title,
style: TextStyle(
fontSize: 20.0, fontWeight: FontW
eight.bold)),
Text(
this.location,
style: TextStyle(
fontSize: 16.0,
color: Colors.grey,
),
),
],
),
Container(
width: 130.0,
),
Icon(
Icons.star,
size: 32.0,
color: Colors.red,
),
Text('5', style: TextStyle(fontSize: 14.0)),
],
),
Baris terakhir diisi dengan deskripsi. Kodenya adalah sebagai berikut:
Container(
padding: EdgeInsets.all(15.0),
child: Text(
this.description,
style: TextStyle(
fontSize: 18.0,
),
softWrap: true,
),
),
Row(
children: <Widget>[
RaisedButton(
child: Text('Button 1'),
onPressed: null,
),
RaisedButton(
child: Text('Button 2'),
onPressed: null,
),
],
),
Kode di atas berfungsi untuk membuat dua komponen RaisedButton dan menampilkannya dengan
susunan horisontal. Ketika aplikasi dijalankam kedua komponen tersebut akan saling menempel.
Untuk membuat jarak antara Button 1 dan Button 2, kita dapat menyisipkan komponen Container
dengan lebar tertentu di antara kedua komponen tersebut, seperti berikut:
Row(
children: <Widget>[
RaisedButton(
child: Text('Button 1'),
onPressed: null,
),
Container(
width: 15.0,
),
RaisedButton(
child: Text('Button 2'),
onPressed: null,
),
],
),
Dalam contoh ini, kita membuat komponen Container dengan properti width bernilai 15.0. Untuk
kontainer yang bertipe Column, jarak antar komponen dapat dibuat dengan mengisi nilai ke dalam
properti height, misalnya seperti berikut:
Column(
children: <Widget>[
RaisedButton(
child: Text('Button 1'),
onPressed: null,
),
Container(
hight: 15.0,
),
RaisedButton(
child: Text('Button 2'),
onPressed: null,
),
],
),
Selain properti width dan height, dua properti yang sering digunakan pada saat menggunakan
kelas Container adalah margin dan padding. Properti margin berfungsi untuk mengatur jarak
antara kontainer yang dibuat dengan komponen lain yang berada di atas atau di sebelahnya
(tergantung dari jenis layout yang digunakan), sedangkan padding berfungsi untuk mengatur jarak
antara tepi kontainer dengan komponen yang berada di dalam kontainer. Pada saat membuat
kontainer, kita juga dapat mengatur tebal bingkai kontainer menggunakan properti border yang
terdapat di dalam kelas BoxDecoration. Gambar di bawah ini menunjukkan perbedaan antara
properti margin dan padding pada kelas Container serta properti border pada kelas
BoxDecoration.
Metode EdgeInsets.all( )
Metode EdgeInsets.all( ) digunakan untuk mengatur margin maupun Padding di seluruh Sisi
kontainer (atas, kanan, bawah, dan kiri). Contoh aplikasi di bawah ini menunjukkan penggunaan
metode EdgeInsets.all( ) yang diisikan untuk properti margin dan padding pada kelas Container.
import 'package:flutter/material.dart';
Pada kode di atas kita membuat tiga objek dari kelas Container yang disusun secara horisontal.
Properti margin dan padding dari kontainer pertama sama dengan kontainer ketiga. Pada kontainer
kedua kita mengisi nilai berbeda untuk properti margin dan padding. Selain itu, kita juga membuat
bingkai untuk kontainer kedua. Cara yang diperlukan untuk membuat bingkai adalah sebagai
berikut:
decoration: BoxDecoration(
border: Border.all(
width: 5.0,
color: Colors.red,
)),
Metode EdgeInsets.only( )
Berbeda dengan EdgeInsets.all ( ) , metode EdgeInsets.only( ) digunakân jika kita hanya ingin
mengatur margin atau padding pada sisi-sisi tertentu saja, bisa atas, kanan, bawah, atau kiri, contoh
penggunaannya adalah sebagai berikut :
Container(
margin: const EdgeInsets.only(5.0),
padding: const EdgeInsets.only(10.0),
decoration: BoxDecoration(
border: Border.all(
width: 5.0,
color: Colors.red,
)),
Kode di atas mengatur margin atas dengan nilai 10.0 dan padding sebelah kiri dengan nilai 5,0.
Pada saat menggunakan metode kita dapat menggunakan parameter top, right, bottom, dan left.
jika semua parameter tersebut digunakan dan nilainya sama, maka EdgeInsets.only( ) akan
sama dengan EdgeInsets.all ( ), misalnya seperti berikut
EdgeInsets.all(10.0)