Flutter'da Radio ve RadioListTile Kullanımı
Flutter'da formlar ve kullanıcı seçimleri oluştururken Radio butonları en temel yapı taşlarından biridir.
Kullanıcıya "birçok seçenek arasından sadece birini seçtirmek" istediğinizde bu widget'ları kullanırsınız. İki temel widget mevcuttur: Radio (sadece yuvarlak buton) ve RadioListTile (metin ve tıklanabilir alan içeren liste elemanı).
Flutter’da kullanıcıdan tek bir seçenek seçmesini istediğimiz durumlarda en doğru bileşenlerden biri Radio ve RadioListTile’dır. Özellikle anketler, ayar ekranları, cinsiyet/seviye/tema seçimleri gibi senaryolarda sıkça kullanılır.
Radio butonlarının çalışma mantığı oldukça basittir ancak State Management (Durum Yönetimi) ile doğru kurgulanması gerekir. Mantık şudur:
Value (Değer): O butonun temsil ettiği kimlik (Örn: "Elma").
GroupValue (Grup Değeri): O an seçili olan değer (Örn: Şu an "Armut" seçili).
Mantık: Eğer
value == groupValueise buton "seçili" (dolu) görünür.
1. Radio Widget'ı
Radio, en yalın halidir. Yanında bir metin barındırmaz, sadece yuvarlak seçim kutusunu oluşturur. Metin eklemek için genellikle bir Row içine alınır.
Temel Özellikleri:
value: Bu butonun taşıdığı değer.
groupValue: Şu an grupta seçili olan değer (State'den gelir).
onChanged: Tıklandığında çalışacak fonksiyon.
Örnek Kullanım:
int? _secilenDeger = 1; // Başlangıçta 1 numaralı seçenek seçili
// ... Widget ağacı içinde:
Row(
children: [
Radio<int>(
value: 1, // Bu butonun değeri 1
groupValue: _secilenDeger, // Eğer _secilenDeger 1 ise işaretli olur
onChanged: (int? value) {
setState(() {
_secilenDeger = value;
});
},
),
Text("Seçenek 1"),
],
)
Dikkat:
Radiokullanırken kullanıcı sadece yuvarlak butona tıkladığında seçim gerçekleşir. Yanındaki "Seçenek 1" yazısına tıklarsa seçim değişmez. Bu durum kullanıcı deneyimi (UX) açısından her zaman ideal değildir.
2. RadioListTile Widget'ı (Önerilen)
RadioListTile, Material Design standartlarına uygun, satır bazlı bir yapı sunar. Hem butonu hem de metni içerir. En büyük avantajı, kullanıcı metne tıkladığında da seçimin gerçekleşmesidir.
Temel Özellikleri:
title: Ana metin.
subtitle: Alt açıklama metni.
secondary: Genellikle sola veya sağa eklenen ikon.
contentPadding: Kenar boşlukları.
activeColor: Seçildiğinde alacağı renk.
Örnek Kullanım:
RadioListTile<String>(
title: const Text("Kredi Kartı"),
subtitle: const Text("Taksit seçenekleri mevcut"),
value: "kredi_karti",
groupValue: _odemeYontemi,
onChanged: (String? value) {
setState(() {
_odemeYontemi = value;
});
},
secondary: const Icon(Icons.credit_card),
)
3. Tam Kapsamlı Örnek Proje
Aşağıdaki kodu kopyalayıp boş bir main.dart dosyasına yapıştırarak çalıştırabilirsiniz. Bu örnekte "Cinsiyet Seçimi" (Enum yapısı ile) ve "Tema Rengi Seçimi" senaryolarını göreceksiniz.
Best Practice olarak seçenekleri yönetmek için Enum kullanacağız.
import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(home: RadioOrnekSayfasi()));
}
// Seçenekleri tip güvenliği (Type Safety) için Enum ile tanımlamak en iyisidir.
enum Cinsiyet { erkek, kadin, belirtmekIstemiyorum }
class RadioOrnekSayfasi extends StatefulWidget {
const RadioOrnekSayfasi({super.key});
@override
State<RadioOrnekSayfasi> createState() => _RadioOrnekSayfasiState();
}
class _RadioOrnekSayfasiState extends State<RadioOrnekSayfasi> {
// Başlangıç değeri
Cinsiyet? _secilenCinsiyet = Cinsiyet.erkek;
// Dinamik liste için örnek
String _favoriMeyve = "Elma";
final List<String> _meyveler = ["Elma", "Armut", "Muz", "Çilek"];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Radio & RadioListTile Rehberi'),
backgroundColor: Colors.deepPurple,
foregroundColor: Colors.white,
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"1. Temel Radio Kullanımı (Enum ile):",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
// Yöntem 1: Basit Radio ve Row
Row(
children: [
Radio<Cinsiyet>(
value: Cinsiyet.erkek,
groupValue: _secilenCinsiyet,
activeColor: Colors.blue, // Seçili renk
onChanged: (Cinsiyet? value) {
setState(() {
_secilenCinsiyet = value;
});
},
),
const Text("Erkek"),
Radio<Cinsiyet>(
value: Cinsiyet.kadin,
groupValue: _secilenCinsiyet,
activeColor: Colors.pink,
onChanged: (Cinsiyet? value) {
setState(() {
_secilenCinsiyet = value;
});
},
),
const Text("Kadın"),
],
),
const Divider(height: 40),
const Text(
"2. RadioListTile Kullanımı (Dinamik Liste):",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const Text("Hangi meyveyi seversin?", style: TextStyle(color: Colors.grey)),
// Yöntem 2: RadioListTile
// Liste elemanlarını döngü ile oluşturuyoruz
..._meyveler.map((meyve) {
return RadioListTile<String>(
title: Text(meyve),
value: meyve,
groupValue: _favoriMeyve,
activeColor: Colors.deepPurple,
contentPadding: EdgeInsets.zero, // Boşlukları sıfırlama
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: BorderSide(color: Colors.grey.shade200)
),
onChanged: (String? value) {
setState(() {
_favoriMeyve = value!;
});
},
);
}),
const SizedBox(height: 20),
Container(
padding: const EdgeInsets.all(12),
color: Colors.yellow.shade100,
child: Text("Seçilen Cinsiyet: ${_secilenCinsiyet?.name}\nSeçilen Meyve: $_favoriMeyve"),
)
],
),
),
);
}
}
Radio ve RadioListTile Karşılaştırması
Hangi durumda hangisini kullanmalısınız?
| Özellik | Radio | RadioListTile |
| Görünüm | Sadece yuvarlak buton. | Buton + Başlık + Alt Başlık + İkon. |
| Tıklama Alanı | Sadece butonun kendisi (küçük). | Satırın tamamı (metin dahil). |
| Yerleşim | Row veya özel tasarım içinde esnek. | Standart liste görünümü. |
| Kullanım Yeri | Yan yana (horizontal) seçenekler için. | Alt alta (vertical) uzun listeler için. |
| Özelleştirme | Tasarımı tamamen size bırakır. | Material Design kurallarına bağlıdır. |
İpuçları ve Püf Noktaları
Tip Güvenliği (Generics): Widget'ları kullanırken her zaman tipi belirtin. Örn:
Radio<int>veyaRadioListTile<String>. Bu, yanlış türde veri atamanızı engeller.Renk Değiştirme:
activeColorparametresi ile seçili olduğunda alacağı rengi değiştirebilirsiniz.fillColorile daha detaylı (seçili değilkenki renk vb.) ayarlar yapılabilir.TileColor:
RadioListTileiçindetileColorveselectedTileColorkullanarak tüm satırın arka plan rengini değiştirebilirsiniz.
Bu yapıyı kullanarak uygulamanızda ayarlar menüleri, filtreleme seçenekleri veya anket formları oluşturabilirsiniz.
1️⃣ Radio Nedir?
Radio, birden fazla seçenek içinden yalnızca bir tanesinin seçilebildiği input widget’ıdır.
📌 Checkbox’tan farkı:
Checkbox → Çoklu seçim
Radio → Tekli seçim
2️⃣ Radio Temel Kullanımı
Radio çalışabilmesi için 3 temel parametre gerekir:
Radio<T>(
value: T,
groupValue: T?,
onChanged: (T?) {},
)
🔎 Parametre Açıklaması
| Parametre | Açıklama |
|---|---|
| value | Bu radio’nun değeri |
| groupValue | Seçili olan değer |
| onChanged | Seçim değiştiğinde çalışır |
🧠 En Önemli Mantık: groupValue
Radio’ların bağlı olduğu ortak değişkendir.
Eğer:
value == groupValue
ise o radio seçili görünür.
✅ Basit Örnek
class GenderSelector extends StatefulWidget {
const GenderSelector({super.key});
@override
State<GenderSelector> createState() => _GenderSelectorState();
}
class _GenderSelectorState extends State<GenderSelector> {
String? selectedGender;
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
children: [
Radio<String>(
value: "Erkek",
groupValue: selectedGender,
onChanged: (value) {
setState(() {
selectedGender = value;
});
},
),
const Text("Erkek"),
],
),
Row(
children: [
Radio<String>(
value: "Kadın",
groupValue: selectedGender,
onChanged: (value) {
setState(() {
selectedGender = value;
});
},
),
const Text("Kadın"),
],
),
],
);
}
}
3️⃣ RadioListTile Nedir?
RadioListTile, Radio + Text + Padding + InkWell birleşimidir.
Yani tıklanabilir alanı büyütür ve daha profesyonel görünüm sağlar.
✅ RadioListTile Kullanımı
class LevelSelector extends StatefulWidget {
const LevelSelector({super.key});
@override
State<LevelSelector> createState() => _LevelSelectorState();
}
class _LevelSelectorState extends State<LevelSelector> {
String? selectedLevel;
@override
Widget build(BuildContext context) {
return Column(
children: [
RadioListTile<String>(
title: const Text("Başlangıç"),
value: "Beginner",
groupValue: selectedLevel,
onChanged: (value) {
setState(() {
selectedLevel = value;
});
},
),
RadioListTile<String>(
title: const Text("Orta"),
value: "Intermediate",
groupValue: selectedLevel,
onChanged: (value) {
setState(() {
selectedLevel = value;
});
},
),
RadioListTile<String>(
title: const Text("İleri"),
value: "Advanced",
groupValue: selectedLevel,
onChanged: (value) {
setState(() {
selectedLevel = value;
});
},
),
],
);
}
}
4️⃣ Radio vs RadioListTile Karşılaştırma
| Özellik | Radio | RadioListTile |
|---|---|---|
| Sadece buton | ✅ | ❌ |
| Metin otomatik | ❌ | ✅ |
| Tıklama alanı geniş | ❌ | ✅ |
| Profesyonel görünüm | Orta | Yüksek |
| Form ekranları | Manuel düzen | Hazır yapı |
👉 Gerçek projelerde çoğunlukla RadioListTile tercih edilir.
5️⃣ Enum ile Profesyonel Kullanım (Clean Code)
String yerine enum kullanmak daha güvenlidir.
enum ThemeModeOption { light, dark, system }
Enum ile RadioListTile
ThemeModeOption? selectedTheme;
Column(
children: [
RadioListTile<ThemeModeOption>(
title: const Text("Light Mode"),
value: ThemeModeOption.light,
groupValue: selectedTheme,
onChanged: (value) {
setState(() {
selectedTheme = value;
});
},
),
RadioListTile<ThemeModeOption>(
title: const Text("Dark Mode"),
value: ThemeModeOption.dark,
groupValue: selectedTheme,
onChanged: (value) {
setState(() {
selectedTheme = value;
});
},
),
],
)
✅ Tip güvenliği
✅ Daha temiz kod
✅ Büyük projelerde hata riskini azaltır
6️⃣ Material 3 Uyumlu Tasarım
Material 3 aktif etmek için:
MaterialApp(
theme: ThemeData(
useMaterial3: true,
),
)
🎨 Stil Özelleştirme
RadioListTile<String>(
activeColor: Colors.deepPurple,
tileColor: Colors.grey.shade100,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
title: const Text("Seçenek"),
value: "A",
groupValue: selectedValue,
onChanged: (value) {},
)
7️⃣ Form İçinde Radio Kullanımı
Radio’yu Form ile kullanırken validation manuel yapılır.
if (selectedValue == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Lütfen bir seçim yapın")),
);
}
8️⃣ Mini Proje: Ayar Ekranı (Material 3)
Özellikler:
Tema seçimi (RadioListTile)
Bildirim tercihi
Kaydet butonu
Snackbar feedback
class SettingsScreen extends StatefulWidget {
const SettingsScreen({super.key});
@override
State<SettingsScreen> createState() => _SettingsScreenState();
}
enum ThemeOption { light, dark }
class _SettingsScreenState extends State<SettingsScreen> {
ThemeOption? selectedTheme = ThemeOption.light;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Ayarlar")),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
const Text(
"Tema Seçimi",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
RadioListTile<ThemeOption>(
title: const Text("Açık Tema"),
value: ThemeOption.light,
groupValue: selectedTheme,
onChanged: (value) {
setState(() {
selectedTheme = value;
});
},
),
RadioListTile<ThemeOption>(
title: const Text("Koyu Tema"),
value: ThemeOption.dark,
groupValue: selectedTheme,
onChanged: (value) {
setState(() {
selectedTheme = value;
});
},
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Ayarlar kaydedildi")),
);
},
child: const Text("Kaydet"),
)
],
),
),
);
}
}
9️⃣ Sık Yapılan Hatalar
❌ groupValue null bırakmak
❌ setState çağırmamak
❌ String yerine enum kullanmamak
❌ Çok sayıda Radio’yu Column içinde scroll olmadan kullanmak
🔟 Performans İpuçları
Büyük listelerde
ListView.builderkullanınEnum tercih edin
Sabit metinlerde
constkullanınGereksiz rebuild’lerden kaçının
📌 Özet
✔ Radio → Tekli seçim
✔ groupValue → Kontrol mekanizması
✔ RadioListTile → Daha profesyonel kullanım
✔ Enum → Temiz ve güvenli kod
✔ Material 3 → Modern tasarım
Normalde TextFormField'in kendi içinde bir validator özelliği vardır. Ancak Radio veya RadioListTile yalın haldeyken Form yapısından habersizdir. Onları bir forma dahil etmek ve "Burası boş geçilemez!" dedirtmek için FormField widget'ı ile sarmalamamız gerekir.
Aşağıda, bir kullanıcının eğitim durumunu seçmesini isteyen ve seçim yapmadan "Kaydet"e basarsa kırmızı hata mesajı gösteren tam çalışan kod yapısı bulunmaktadır.
Önemli Nokta: FormField Mantığı
Bu kodda göreceğiniz sihirli değnek FormField widget'ıdır. Bu widget, Radio grubunu sarmalar ve şunları sağlar:
Validator: Seçim yapılıp yapılmadığını kontrol eder.
Builder: Hata mesajını (kırmızı yazı) ekrana basmamızı sağlar.
state.didChange: Seçim yapıldığında Form'a "Hey, değer değişti!" haberini verir.
Kod Örneği
import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(home: RadioFormPage()));
}
class RadioFormPage extends StatefulWidget {
const RadioFormPage({super.key});
@override
State<RadioFormPage> createState() => _RadioFormPageState();
}
class _RadioFormPageState extends State<RadioFormPage> {
// Formun durumunu kontrol etmek için anahtar
final _formKey = GlobalKey<FormState>();
// Seçilen değeri tutacak değişken
String? _egitimDurumu;
// Seçeneklerimiz
final List<String> _secenekler = [
"İlköğretim",
"Lise",
"Üniversite",
"Yüksek Lisans"
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Radio Form Validation")),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey, // Form anahtarını buraya veriyoruz
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"Eğitim Durumunuz:",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
// --- İŞİN PÜF NOKTASI BURASI ---
// Radio grubunu FormField içine alıyoruz
FormField<String>(
// 1. Kural (Validator): Eğer değer null ise hata döndür
validator: (value) {
if (value == null) {
return "Lütfen bir eğitim durumu seçiniz!";
}
return null;
},
builder: (FormFieldState<String> state) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Seçenekleri listeliyoruz
..._secenekler.map((e) => RadioListTile<String>(
title: Text(e),
value: e,
// Hem yerel değişkeni hem de form state'ini kontrol edebiliriz
groupValue: _egitimDurumu,
onChanged: (val) {
setState(() {
_egitimDurumu = val;
});
// BURASI ÇOK ÖNEMLİ:
// FormField'e değerin değiştiğini bildiriyoruz
// Böylece hata mesajı varsa silinir.
state.didChange(val);
},
)),
// 2. Hata Mesajı Gösterimi
// Eğer formda hata varsa (state.hasError), kırmızı metni göster
if (state.hasError)
Padding(
padding: const EdgeInsets.only(left: 16.0, top: 5.0),
child: Text(
state.errorText!,
style: TextStyle(
color: Theme.of(context).colorScheme.error,
fontSize: 12,
),
),
),
],
);
},
),
// --------------------------------
const SizedBox(height: 30),
SizedBox(
width: double.infinity,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
),
onPressed: () {
// Butona basıldığında doğrulama yap
if (_formKey.currentState!.validate()) {
// Hata yoksa burası çalışır
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Kaydedildi: $_egitimDurumu")),
);
} else {
// Hata varsa validator çalışır ve kırmızı yazılar çıkar
print("Form geçerli değil");
}
},
child: const Text("KAYDET"),
),
),
],
),
),
),
);
}
}
Kodun Çalışma Mantığı
FormField Sarıcı:
RadioListTilewidget'larını doğrudanColumniçine koymak yerineFormField'inbuilderparametresi içine koyduk. Bu sayedeRadiogrubu bir bütün olarak "tek bir form elemanı" gibi davranır.state.didChange(val): Kullanıcı bir şeye tıkladığında sadece
setStateyapıp ekranı güncellemek yetmez.state.didChange(val)kodunu da ekledik. Bu kod, FormField'e "Kullanıcı bir şey seçti, artık değer null değil, varsa hata mesajını kaldır" der.state.hasError:
builderiçinde en alta birifbloğu ekledik. Eğer kullanıcı "Kaydet"e bastığında bir seçim yapmamışsa,validatordevreye girer,state.hasErrortrue olur ve kırmızı hata metni (state.errorText) görünür hale gelir.
Bu yapı sayesinde, karmaşık formlarda bile Radio butonlarını TextFormField kadar kolay bir şekilde yönetebilir ve doğrulayabilirsiniz.
🌙 Flutter Dark Mode + Light Mode Varyasyonu
RadioListTile ile Dinamik Tema Değişimi (Material 3)
Bu örnekte:
✅ Light / Dark / System tema seçimi
✅ RadioListTile ile seçim
✅ Anında uygulanan tema değişimi
✅ Clean mimari yapıya uygun çözüm
✅ Material 3 tasarım
yapacağız.
🏗 Mimari Yapı
lib/
│
├── domain/
│ └── theme_option.dart
│
├── presentation/
│ ├── theme_notifier.dart
│ ├── widgets/
│ │ └── custom_radio_tile.dart
│ └── screens/
│ └── settings_screen.dart
│
└── main.dart
1️⃣ Domain Katmanı
📁 theme_option.dart
enum AppThemeOption {
light,
dark,
system,
}
2️⃣ Theme State Yönetimi
Burada basit ve profesyonel bir çözüm olarak ChangeNotifier kullanıyoruz.
📁 theme_notifier.dart
import 'package:flutter/material.dart';
import '../domain/theme_option.dart';
class ThemeNotifier extends ChangeNotifier {
ThemeMode _themeMode = ThemeMode.system;
ThemeMode get themeMode => _themeMode;
void updateTheme(AppThemeOption option) {
switch (option) {
case AppThemeOption.light:
_themeMode = ThemeMode.light;
break;
case AppThemeOption.dark:
_themeMode = ThemeMode.dark;
break;
case AppThemeOption.system:
_themeMode = ThemeMode.system;
break;
}
notifyListeners();
}
}
3️⃣ Reusable Radio Widget
📁 custom_radio_tile.dart
import 'package:flutter/material.dart';
class CustomRadioTile<T> extends StatelessWidget {
final String title;
final T value;
final T? groupValue;
final ValueChanged<T?> onChanged;
const CustomRadioTile({
super.key,
required this.title,
required this.value,
required this.groupValue,
required this.onChanged,
});
@override
Widget build(BuildContext context) {
return RadioListTile<T>(
title: Text(title),
value: value,
groupValue: groupValue,
onChanged: onChanged,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
);
}
}
4️⃣ Settings Screen
📁 settings_screen.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../../domain/theme_option.dart';
import '../theme_notifier.dart';
import '../widgets/custom_radio_tile.dart';
class SettingsScreen extends StatelessWidget {
const SettingsScreen({super.key});
@override
Widget build(BuildContext context) {
final themeNotifier = context.watch<ThemeNotifier>();
AppThemeOption selectedOption;
switch (themeNotifier.themeMode) {
case ThemeMode.light:
selectedOption = AppThemeOption.light;
break;
case ThemeMode.dark:
selectedOption = AppThemeOption.dark;
break;
default:
selectedOption = AppThemeOption.system;
}
return Scaffold(
appBar: AppBar(title: const Text("Tema Ayarları")),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
const Text(
"Tema Seçimi",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
CustomRadioTile<AppThemeOption>(
title: "Açık Tema",
value: AppThemeOption.light,
groupValue: selectedOption,
onChanged: (value) {
if (value != null) {
themeNotifier.updateTheme(value);
}
},
),
CustomRadioTile<AppThemeOption>(
title: "Koyu Tema",
value: AppThemeOption.dark,
groupValue: selectedOption,
onChanged: (value) {
if (value != null) {
themeNotifier.updateTheme(value);
}
},
),
CustomRadioTile<AppThemeOption>(
title: "Sistem Varsayılanı",
value: AppThemeOption.system,
groupValue: selectedOption,
onChanged: (value) {
if (value != null) {
themeNotifier.updateTheme(value);
}
},
),
],
),
),
);
}
}
5️⃣ main.dart (Material 3 + Dynamic Theme)
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'presentation/theme_notifier.dart';
import 'presentation/screens/settings_screen.dart';
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => ThemeNotifier(),
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
final themeNotifier = context.watch<ThemeNotifier>();
return MaterialApp(
debugShowCheckedModeBanner: false,
themeMode: themeNotifier.themeMode,
theme: ThemeData(
useMaterial3: true,
colorSchemeSeed: Colors.deepPurple,
brightness: Brightness.light,
),
darkTheme: ThemeData(
useMaterial3: true,
colorSchemeSeed: Colors.deepPurple,
brightness: Brightness.dark,
),
home: const SettingsScreen(),
);
}
}
🧠 Tema Akış Diyagramı
Kullanıcı Radio Seçer
│
▼
ThemeNotifier.updateTheme()
│
▼
ThemeMode değişir
│
▼
notifyListeners()
│
▼
MaterialApp rebuild olur
│
▼
UI Light / Dark olarak güncellenir
🎨 Light vs Dark Görsel Davranış
| Özellik | Light | Dark |
|---|---|---|
| Arka Plan | Beyaz | Koyu gri |
| Text | Siyah | Beyaz |
| Surface | Açık ton | Koyu ton |
| Shadow | Hafif | Daha belirgin |
Material 3 otomatik uyum sağlar.
🚀 Production Seviyesine Taşımak İçin
Bir üst seviyede:
SharedPreferences ile kalıcı tema kaydı
Splash screen’de otomatik yükleme
Riverpod ile state yönetimi
Animated theme geçişi
System theme değişimini dinleme
eklenebilir.
Yorumlar
Yorum Gönder