Theming adalah praktik menetapkan identitas visual aplikasi pada satu tempat, sehingga warna dan gaya teks tidak ditulis ulang di setiap layar. Topik ini membahas ThemeData pada MaterialApp, pembentukan skema warna dengan ColorScheme.fromSeed, peran-peran warna Material 3, skala tipografi TextTheme, pembacaan tema melalui Theme.of(context), serta penimpaan tema pada bagian tertentu dari pohon widget. Setelah topik ini dibaca, perubahan warna pada seluruh aplikasi cukup dilakukan dengan menyunting beberapa baris tema.
ThemeData pada MaterialApp#
ThemeData adalah kelas yang menampung konfigurasi visual aplikasi, mulai dari skema warna, skala tipografi, bentuk komponen, hingga gaya tombol. Properti theme pada MaterialApp menerimanya, dan seluruh widget di bawah MaterialApp membaca konfigurasi yang sama. Perubahan satu nilai pada ThemeData, misalnya warna utama, otomatis berlaku pada AppBar, tombol, kartu, dan komponen Material lainnya tanpa menyunting tampilan satu per satu.
Sejak Flutter 3.16, Material 3 menjadi bawaan, sehingga ThemeData yang disusun dengan cara standar sudah menghasilkan tampilan Material 3 tanpa penanda tambahan. Cara yang paling umum untuk menyusun skema warnanya adalah ColorScheme.fromSeed, yaitu pembangkit skema lengkap dari satu warna asal yang disebut seed color.
// tidak-dijalankan
MaterialApp(
title: 'Catatan Harian',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
),
home: LayarCatatan(),
)Argumen seedColor: Colors.teal menjadi asal pembangkitan. ColorScheme.fromSeed menghitung deretan warna turunan dari warna asal tersebut, seperti versi lembut untuk latar kartu dan versi gelap untuk teks, sehingga kontras antara teks dan latarnya dijaga secara otomatis. Mengganti identitas warna aplikasi berarti mengganti satu argumen tersebut, lalu seluruh komponen mengikuti pada build berikutnya.
Peran warna pada skema Material 3#
ColorScheme bukan kumpulan warna acak, melainkan sekumpulan peran (role) dengan fungsi yang tetap. Setiap peran menjawab pertanyaan warna apa yang digunakan di suatu permukaan, sehingga tampilan yang konsisten muncul dari kebiasaan memilih peran yang tepat, bukan dari menulis kode warna satu per satu. Peran yang paling sering dibutuhkan antara lain primary, surface, dan error.
Tabel berikut merangkum peran tersebut beserta contoh permukaannya pada aplikasi catatan.
| Peran | Fungsi | Contoh permukaan |
|---|---|---|
primary | Warna utama identitas aplikasi | Tombol utama dan elemen bertanda |
onPrimary | Teks dan ikon di atas primary | Label pada tombol utama |
surface | Latar kartu, panel, dan lembaran | Latar kartu catatan |
onSurface | Teks dan ikon di atas surface | Isi teks kartu catatan |
secondaryContainer | Aksen pendamping yang lebih lembut | Kartu catatan yang sedang dipilih |
error | Status gagal dan pembatalan | Pesan validasi pada form |
onError | Teks dan ikon di atas error | Teks pesan validasi yang bertumpu pada warna error |
Setiap peran berawalan on berpasangan dengan peran latarnya dan telah dihitung agar kontrasnya memadai. Berkat pasangan inilah dukungan kontras dan keterbacaan menjadi bagian bawaan Material 3, bukan pekerjaan tambahan pengembang. Peran-peran lain pada ColorScheme mengikuti pola yang sama, dan daftar lengkapnya tersedia pada dokumentasi kelas tersebut.
Skala tipografi dengan TextTheme#
TextTheme adalah kumpulan gaya teks yang tersusun sebagai skala, yaitu daftar peran teks dari yang terbesar hingga terkecil. Nama perannya mengikuti pola kelompok plus ukuran, misalnya displayLarge untuk teks berukuran sangat besar yang mencolok, headlineMedium untuk judul layar, titleLarge untuk judul komponen, bodyMedium untuk isi teks, dan labelSmall untuk teks pendek seperti label tombol. Skala ini berasal dari peran tipografi Material 3, sehingga jumlah dan namanya stabil antarproyek. Setiap peran menetapkan ukuran, ketebalan, dan jarak huruf sekaligus, sehingga teks yang sejenis tampil seragam di seluruh aplikasi.
// tidak-dijalankan
Text(
'Statistik nilai ujian',
style: Theme.of(context).textTheme.titleLarge,
)TextTheme bawaan juga berpasangan dengan ColorScheme, misalnya warna teks isi mengikuti peran onSurface, sehingga teks tetap terbaca ketika skema berubah. Penyesuaian skala dilakukan pada tema aplikasi, bukan pada setiap widget teks, dengan cara menimpa ThemeData sebagaimana dijelaskan pada bagian akhir topik ini. Apabila satu gaya perlu disesuaikan, misalnya ukuran titleLarge yang diperbesar sedikit, perubahan dilakukan sekali pada tema dan seluruh pengguna peran tersebut ikut berubah. Kebiasaan tersebut menjaga konsistensi tampilan ketika jumlah layar bertambah.
Membaca tema lewat Theme.of(context)#
Theme.of(context) mengambil tema terdekat yang melingkupi widget pemanggilnya, tanpa perlu meneruskan ThemeData sebagai parameter dari satu widget ke widget lain. MaterialApp sendiri memasang widget Theme di puncak pohon, sehingga Theme.of(context) yang dipanggil dari metode build widget mana pun di bawahnya selalu menemukan hasil. Pencarian itu berlangsung pada setiap build, sehingga nilai yang dibaca selalu nilai terbaru, termasuk setelah tema diganti.
// tidak-dijalankan
final skema = Theme.of(context).colorScheme;
return Card(
color: skema.secondaryContainer,
child: Padding(
padding: const EdgeInsets.all(16),
child: Text(
'Catatan dipilih',
style: Theme.of(context).textTheme.labelLarge,
),
),
);Membaca peran dari tema lebih menguntungkan daripada menulis warna tetap. Tampilan mengikuti tema yang aktif, misalnya ketika aplikasi juga menyediakan mode gelap, dan perubahan identitas warna tidak menuntut penyuntingan widget satu per satu. Satu pembacaan di awal metode build juga dapat digunakan oleh beberapa widget sekaligus, seperti pola pada kode di atas yang menampung colorScheme pada satu variabel. Kebiasaan ini menjadi prasyarat ketika aplikasi menyediakan mode gelap, karena warna yang ditulis tetap tidak ikut berganti bersama temanya.
Menimpa tema pada bagian tertentu#
Tema aplikasi tidak selalu cocok untuk setiap bagian layar, misalnya panel statistik yang memerlukan warna aksen berbeda. Widget Theme memasang ThemeData baru pada subpohon, dan seluruh widget di bawahnya membaca tema tersebut melalui Theme.of(context). Untuk mengubah sebagian kecil saja, Theme.of(context).copyWith menyalin tema yang sedang berlaku lalu menimpa peran yang dimaksud, sehingga peran lain tetap tidak berubah.
// tidak-dijalankan
Theme(
data: Theme.of(context).copyWith(
colorScheme: Theme.of(context).colorScheme.copyWith(
primary: Colors.deepPurple,
),
),
child: PanelStatistik(),
)PanelStatistik membaca primary melalui Theme.of(context) dan mendapatkan warna deepPurple, sedangkan bagian lain layar tetap membaca teal dari tema aplikasi. Penimpaan seperti ini bersifat lokal karena hanya berlaku pada subpohon di dalam child milik Theme. Apabila tema perlu dibedakan untuk satu layar penuh, pola yang sama dapat diterapkan di atas Scaffold layar tersebut. Jika subpohon yang dibungkus cukup besar, penimpaan satu titik seperti ini lebih disiplin daripada menimpa warna satu per satu di setiap widget anak.
Kesalahan umum#
Tiga kesalahan berikut paling sering muncul saat tema mulai diterapkan pada proyek kecil. Masing-masing memengaruhi konsistensi tampilan, tetapi perbaikannya pendek dan cukup dilakukan sekali.
- Menulis warna tetap di setiap widget. Tampilan sulit diubah dan mudah tidak konsisten karena warna tersebar di banyak berkas. Perbaikannya, pilih peran dari ColorScheme melalui Theme.of(context), dan simpan warna eksplisit untuk kasus yang benar-benar khusus.
- Memanggil Theme.of(context) dengan konteks di atas MaterialApp. Konteks semacam itu tidak menemukan widget Theme sehingga aplikasi melempar error. Perbaikannya, panggil Theme.of(context) dari metode build widget yang berada di bawah MaterialApp.
- Mengganti tema dengan ThemeData yang disusun dari awal. Properti yang tidak ditetapkan kembali ke nilai bawaannya, sehingga bagian tampilan lain ikut berubah tanpa disengaja. Perbaikannya, mulai dari Theme.of(context) lalu gunakan copyWith untuk menimpa yang diperlukan saja.
Ringkasan#
- ThemeData dipasang pada properti
thememilik MaterialApp dan menampung skema warna beserta skala tipografi seluruh aplikasi. - ColorScheme.fromSeed membangun skema Material 3 lengkap dari satu seed color dengan kontras yang telah dihitung.
- Warna dan teks dipilih berdasarkan perannya, misalnya
primary,surface,error, dan peran-peran pada TextTheme. - Theme.of(context) membaca tema terdekat, sedangkan widget Theme dengan copyWith menimpa tema hanya pada subpohonnya.
Referensi#
- Use themes to share colors and font styles — Flutter — Resep resmi menetapkan dan membaca theme pada aplikasi Flutter.
- ThemeData class — Flutter API — Dokumentasi kelas penampung konfigurasi tema beserta nilai bawaannya.
- ColorScheme class — Flutter API — Daftar peran warna pada skema Material 3 beserta arti masing-masing.
- Color system — Material Design 3 — Spesifikasi sistem warna Material 3 dari penerbitnya, termasuk peran warna dan pasangan kontrasnya.
- TextTheme class — Flutter API — Dokumentasi skala peran teks dari display hingga label.