Kemarin kita udah bikin app dengan gambar yang rapi. Tapi ada satu hal yang bikin app kamu masih keliatan “default banget”: warna biru bawaan dan font Roboto yang itu-itu aja. App yang profesional punya identitas visual — warna khas, font khas, gaya tombol yang konsisten. Dan di Flutter, semua itu diatur lewat ThemeData.
Bayangin kamu bikin 20 halaman, tiap tombol kamu tulis color: Colors.teal manual. Terus client minta ganti warna jadi oranye. Nyeri kan? Dengan ThemeData, kamu ganti satu tempat, semua tombol ikut berubah. Itulah kekuatan tema.
1. Kenapa Harus Pakai ThemeData
Tanpa tema, tiap widget harus dikasih style manual satu per satu. Akibatnya:
- Nggak konsisten — halaman A tombolnya teal, halaman B lupa jadi biru.
- Susah diubah — ganti warna = cari satu-satu di seluruh codebase.
- Duplikasi — warna yang sama ditulis berulang-ulang.
Solusinya: definisikan semua style sekali di MaterialApp, terus pakai di mana-mana.
MaterialApp(
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
useMaterial3: true,
),
home: const HomeScreen(),
)
Cuma segini, seluruh app kamu otomatis pakai warna teal. Tombol, switch, input, checkbox — semua ikut. Ini karena ColorScheme.fromSeed nge-generate palet warna lengkap (primer, sekunder, error, dll) dari satu “benih” warna.
2. Kustomisasi Lebih Dalam
ThemeData punya banyak komponen yang bisa diatur. Ini yang paling sering dipakai:
ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
useMaterial3: true,
// Font default seluruh app
fontFamily: 'Poppins',
// Style AppBar di semua halaman
appBarTheme: const AppBarTheme(
centerTitle: true,
elevation: 0,
),
// Style tombol elevated di semua tempat
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
textStyle: const TextStyle(fontWeight: FontWeight.w600),
),
),
// Style input field di semua form
inputDecorationTheme: InputDecorationTheme(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
filled: true,
fillColor: Colors.grey.shade100,
),
// Style card
cardTheme: CardTheme(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
),
)
Sekarang tiap AppBar, ElevatedButton, TextFormField, dan Card yang kamu buat otomatis ngikut style di atas. Nggak perlu set manual lagi.
3. Ambil Nilai Tema di Widget — Theme.of(context)
Kadang kamu butuh akses warna/teks dari tema di dalam widget. Gunakan Theme.of(context):
final theme = Theme.of(context);
// Warna primer
final primary = theme.colorScheme.primary;
// Style heading
theme.textTheme.titleLarge
// Warna error
theme.colorScheme.error
Contoh nyata — bikin teks yang warnanya ngikut tema:
Text(
'Selamat datang!',
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
color: Theme.of(context).colorScheme.primary,
fontWeight: FontWeight.bold,
),
)
⚠️ Jangan pakai
Colors.teallangsung di dalam widget kalau warnanya udah didefinisikan di tema. Selalu ambil dariTheme.of(context)biar konsisten dan ikut berubah saat tema berganti.
4. Font Custom
Font adalah bagian terbesar dari identitas visual. Ada dua cara populer:
4.1 Pakai google_fonts (paling gampang)
Tambah dependency di pubspec.yaml:
dependencies:
google_fonts: ^6.2.0
Terus pakai:
import 'package:google_fonts/google_fonts.dart';
Text(
'Halo Flutter!',
style: GoogleFonts.poppins(
fontSize: 24,
fontWeight: FontWeight.bold,
),
)
google_fonts otomatis nge-fetch font saat runtime, atau bisa dibundel offline. Cocok buat prototyping dan project yang nggak butuh kontrol penuh atas file font.
4.2 Manual dengan File .ttf (lebih kontrol)
Download file font (misal Poppins-Regular.ttf), taruh di folder assets/fonts/, terus daftarkan di pubspec.yaml:
flutter:
fonts:
- family: Poppins
fonts:
- asset: assets/fonts/Poppins-Regular.ttf
- asset: assets/fonts/Poppins-Bold.ttf
weight: 700
Setelah itu, tinggal set fontFamily: 'Poppins' di ThemeData (lihat bagian 2). Semua teks di app otomatis pakai Poppins. Cara ini lebih “bersih” karena font dibundel langsung ke app, tanpa fetch runtime.
5. Dark Mode — darkTheme & themeMode
Theme nggak cuma satu. App modern punya light & dark mode. Caranya:
MaterialApp(
theme: ThemeData.light(), // tema terang
darkTheme: ThemeData.dark(), // tema gelap
themeMode: ThemeMode.system, // ikut settingan HP user
home: const HomeScreen(),
)
ThemeMode.system— ikut mode HP user (paling recommended)ThemeMode.light— selalu terangThemeMode.dark— selalu gelap
Biasanya kamu definisikan dua ThemeData custom (light & dark) biar warnanya tetap konsisten di kedua mode.
6. Contoh Lengkap: App dengan Tema + Dark Mode Toggle
Gabungin semuanya — tema teal, font Poppins, dan tombol ganti mode:
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
void main() => runApp(const MyApp());
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool _darkMode = false;
ThemeData _buildTheme(Brightness brightness) {
final scheme = ColorScheme.fromSeed(
seedColor: Colors.teal,
brightness: brightness,
);
return ThemeData(
colorScheme: scheme,
useMaterial3: true,
textTheme: GoogleFonts.poppinsTextTheme(
ThemeData(brightness: brightness).textTheme,
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Tema App',
debugShowCheckedModeBanner: false,
theme: _buildTheme(Brightness.light),
darkTheme: _buildTheme(Brightness.dark),
themeMode: _darkMode ? ThemeMode.dark : ThemeMode.light,
home: HomeScreen(
darkMode: _darkMode,
onToggle: () => setState(() => _darkMode = !_darkMode),
),
);
}
}
class HomeScreen extends StatelessWidget {
final bool darkMode;
final VoidCallback onToggle;
const HomeScreen({
super.key,
required this.darkMode,
required this.onToggle,
});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Scaffold(
appBar: AppBar(title: const Text('🎨 Tema & Font')),
body: Padding(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'Identitas Visual',
style: theme.textTheme.headlineMedium?.copyWith(
color: theme.colorScheme.primary,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(
'Font, warna, dan gaya tombol diatur dari satu tempat.',
style: theme.textTheme.bodyLarge,
),
const SizedBox(height: 24),
ElevatedButton.icon(
onPressed: () {},
icon: const Icon(Icons.favorite),
label: const Text('Tombol Bertema'),
),
const SizedBox(height: 16),
OutlinedButton(
onPressed: () {},
child: const Text('Tombol Sekunder'),
),
const SizedBox(height: 24),
Card(
child: SwitchListTile(
title: const Text('Dark Mode'),
subtitle: const Text('Ganti tema terang/gelap'),
value: darkMode,
onChanged: (_) => onToggle(),
),
),
],
),
),
);
}
}
Jalankan dan coba: semua tombol, AppBar, dan teks otomatis mengikuti tema. Geser switch dark mode → seluruh app berubah warna secara instan, tanpa ubah satu baris pun di widget-nya. Itulah kekuatan ThemeData.
7. Best Practice
- Selalu pakai
ColorScheme.fromSeed— lebih modern dan menghasilkan palet harmonis daripada set warna manual satu-satu. - Ambil warna dari
Theme.of(context), jangan hardcodeColors.xdi widget. - Pisahkan
_buildTheme()jadi function terpisah biar kodemain.dartbersih. - Set
useMaterial3: true— Material 3 udah jadi default di Flutter terbaru dan tampilannya jauh lebih fresh. - Untuk font, bundle manual (
.ttf) kalau app butuh offline & konsisten lintas platform. Pakaigoogle_fontsbuat prototyping cepat. - Dark mode wajib — user zaman sekarang ekspektasinya app punya mode gelap.
Ringkasan
ThemeData= definisi warna, font, dan gaya widget di satu tempatColorScheme.fromSeed= generate palet warna dari satu warna benihTheme.of(context)= ambil nilai tema di widget manapun- Font custom = via
google_fonts(cepat) atau file.ttf(kontrol penuh) darkTheme+themeMode= dukung light & dark mode- Satu perubahan di tema = seluruh app ikut berubah
Dengan tema yang rapi, app kamu nggak cuma enak dilihat, tapi juga mudah di-maintain. Ini fondasi penting sebelum kita masuk ke state management nanti.
Besok kita bahas MediaQuery & Orientation Builder — bikin layout yang responsif di berbagai ukuran layar. Stay tuned! 📱
Coba sendiri! Bikin app dengan tema khas kamu (warna + font favorit) dan tombol toggle dark mode. Share hasilnya ke sosial media dan tag @ahsai001!