- 16 Mar 2022
- 1,494
- 25
- 1,654
Uzun bir sürenin ardından merhaba. Yanda sitelerin hacklendiği, burada ise çiçek böceklerin uçuştuğu dart dilinde yazılan java ile bağı olan kotlinle arkadaş Flutter frameworkü ile berabersiniz. Bu konumda çok fazla detaya inip teknik bilgi vermek yerine neyi nerede ne için kullanmışım ve bu ne işe yarıyor formatında götürmeyi planlıyorum.
Bilindiği üzere Flutter framework'ü bizler için tek bir programlama dilinde birçok iş yapabilmemize olanak tanıyor. Ama bizde herkes gibi bu iş olarak tanımladığımız kısmın yalnızca mobil tarafını görücez. Yoksa msvc derleyicisini de yükleyip masaüstü uygulama mı yapalım? Kesinlikle hayır
Yapacağımız uygulama tek başına "uygulama" olarak sayılacak türden değil çünkü kendi haline hiçbir iş yapacak düzeyde değil. Bu halihazırda bir projeniz varsa eklemeyi düşüneceğiniz yani kullanıcı bazlı bir çeşit "bakın uygulamada böyle bir kişiselleştirme de var" dedirten bir uygulama olacak. (uygulama sayacı = 4)
Başlamadan önce flutter framework'üne ve gereksinimlerine sahip olmalısınız. Kurulumlarını internetten bakarak halledebilirsiniz.
Kod yazma platformu olarak Visual Studio Code text editor'ü tercih edicez çünkü herkes böyle yapıyor
Extension sekmesine flutter yazarak intellisense ve arayüzde yardımcı elemanlar olması adına indirip kurmanızda fayda var.
Buradan sonra anlatacaklarımı projenize göre uyarlayarak gidin. Yani tek bir ağızdan yazacağım siz kendi payınıza düşeni gerekirse alacak, almayacak ya da düzenleyeceksiniz.
Öncelikle text editörde kurmuş olduğumuz flutter eklentisi sayesinde bir proje oluşturucaz. (evet bu olmadan da konsol komutu ile de oluşturulabilir ama niye böyle oluşturmayalım ki )
Bizler için çeşitli klasörler ve dosyalar oluşturdu. İlgileneceğimiz tek klasör şuanlık lib klasörü ve pubspec.yaml dosyası. Çünkü içerisine dart dosyalarını ekleyeceğiz diğeriyle de bir tane harici kütüphane dahil edeceğiz.
başlangıç olarak pubspec.yaml dosyasına gidiyoruz.
satırını bulup "flutter:" hizasında
- Bu kütüphane olmazsa yazdıklarımız geçersiz kalır mı?
+ Hayır. Bu kütüphane bizim uygulamamızda yapacağımız işlemin kalıcı olmasını istediğimizden, kullanıcının her girişinde yeniden ayarlamasına gerek kalmasını istemediğimizden kullanıyoruz.
Şimdi
Burada yaptığım işlem üç adet istediğim renklerde tema oluşturup bunları seçime göre kaydedip daha sonra okuyup aşağıdaki main.dart dosyasında program her açıldığında(shared_preferences sayesinde) kayıtlı olan temayı alıp arayüzde bu renkleri bize sunması. Olabildiğince açıklayıcı yazmaya çalıştığım kodda anlaşılmayan yer varsa sorabilirsiniz.
Bu kodda farklı olarak görebileceğiniz method ve sınıflar
>
>
>
>
Burada yapılan işlem ise başta üç adet(tema adedimiz kadar) bool veri tipi tanımladık başlangıçları false çünkü başta temalar seçilmemiş olacak. initState methoduna baktığımızda da
Diğer konularda görüşmek üzere.
Bilindiği üzere Flutter framework'ü bizler için tek bir programlama dilinde birçok iş yapabilmemize olanak tanıyor. Ama bizde herkes gibi bu iş olarak tanımladığımız kısmın yalnızca mobil tarafını görücez. Yoksa msvc derleyicisini de yükleyip masaüstü uygulama mı yapalım? Kesinlikle hayır
Yapacağımız uygulama tek başına "uygulama" olarak sayılacak türden değil çünkü kendi haline hiçbir iş yapacak düzeyde değil. Bu halihazırda bir projeniz varsa eklemeyi düşüneceğiniz yani kullanıcı bazlı bir çeşit "bakın uygulamada böyle bir kişiselleştirme de var" dedirten bir uygulama olacak. (uygulama sayacı = 4)
Başlamadan önce flutter framework'üne ve gereksinimlerine sahip olmalısınız. Kurulumlarını internetten bakarak halledebilirsiniz.
Choose your development platform to get started
Install Flutter and get started. Downloads available for Windows, macOS, Linux, and ChromeOS operating systems.
docs.flutter.dev
Kod yazma platformu olarak Visual Studio Code text editor'ü tercih edicez çünkü herkes böyle yapıyor
Download Visual Studio Code - Mac, Linux, Windows
Visual Studio Code is free and available on your favorite platform - Linux, macOS, and Windows. Download Visual Studio Code to experience a redefined code editor, optimized for building and debugging modern web and cloud applications.
code.visualstudio.com
Buradan sonra anlatacaklarımı projenize göre uyarlayarak gidin. Yani tek bir ağızdan yazacağım siz kendi payınıza düşeni gerekirse alacak, almayacak ya da düzenleyeceksiniz.
Öncelikle text editörde kurmuş olduğumuz flutter eklentisi sayesinde bir proje oluşturucaz. (evet bu olmadan da konsol komutu ile de oluşturulabilir ama niye böyle oluşturmayalım ki )
Bizler için çeşitli klasörler ve dosyalar oluşturdu. İlgileneceğimiz tek klasör şuanlık lib klasörü ve pubspec.yaml dosyası. Çünkü içerisine dart dosyalarını ekleyeceğiz diğeriyle de bir tane harici kütüphane dahil edeceğiz.
başlangıç olarak pubspec.yaml dosyasına gidiyoruz.
YAML:
dependencies:
flutter:
sdk: flutter
shared_preferences: ^uygun_olan_surum
" uygun_olan_surum olarak belirttiğim noktaya ben ^2.2.1 numarasını yazdım. Güncel flutter sürümü kullanıyorsanız hata vermeyecektir. Eğer hata veriyorsa hata mesajına göre uygun sürümü yükleyin. Bu kütüphane bizim android/ios cihazlarımızda belirli bir dosya yoluna(bunu belirmemize gerek yok) belirlediğimiz anahtar ifadesi doğrultusunda kaydedebildiğimiz ve bu anahtarın değerini okuyup buna göre işlemler yapabildiğimiz bir kütüphane.- Bu kütüphane olmazsa yazdıklarımız geçersiz kalır mı?
+ Hayır. Bu kütüphane bizim uygulamamızda yapacağımız işlemin kalıcı olmasını istediğimizden, kullanıcının her girişinde yeniden ayarlamasına gerek kalmasını istemediğimizden kullanıyoruz.
Şimdi
tema.dart
adında bir dart dosyası oluşturalım ve aşağıdaki kodu yazalım.
Kod:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart';
class TemaSinifi {
static ValueNotifier<ThemeMode>? _themeNotifier;
static ThemeData? returnDarkThemeData;
static ThemeData get secilenTemayiAl => returnDarkThemeData!;
static ThemeData get acikTemayiAl => _acikTema;
static ValueNotifier<ThemeMode>? get themeNotifierAl => _themeNotifier;
static final ThemeData _acikTema = ThemeData(
brightness: Brightness.light,
colorScheme: const ColorScheme.light(
background: Colors.white,
secondary: Colors.black87,
primary: Colors.blue,
),
primaryIconTheme: const IconThemeData(
color: Colors.blueGrey,
),
appBarTheme: const AppBarTheme(
systemOverlayStyle: SystemUiOverlayStyle(
statusBarColor: Colors.blue,
statusBarIconBrightness: Brightness.light),
),
);
static final ThemeData _temaSec1 = ThemeData(
brightness: Brightness.dark,
primaryIconTheme: const IconThemeData(
color: Colors.amberAccent,
),
colorScheme: const ColorScheme.dark(
background: Color.fromARGB(255, 18, 18, 18),
secondary: Colors.deepOrangeAccent,
primary: Colors.amber,
),
appBarTheme: const AppBarTheme(
systemOverlayStyle: SystemUiOverlayStyle(
statusBarColor: Colors.amber,
statusBarIconBrightness: Brightness.dark),
),
);
static final ThemeData _temaSec2 = ThemeData(
brightness: Brightness.dark,
primaryIconTheme: const IconThemeData(
color: Colors.purpleAccent,
),
colorScheme: const ColorScheme.dark(
background: Color.fromARGB(255, 18, 18, 18),
secondary: Colors.purpleAccent,
primary: Colors.deepPurple,
),
appBarTheme: const AppBarTheme(
systemOverlayStyle: SystemUiOverlayStyle(
statusBarColor: Colors.deepPurple,
statusBarIconBrightness: Brightness.dark),
),
);
static final ThemeData _temaSec3 = ThemeData(
brightness: Brightness.dark,
primaryIconTheme: const IconThemeData(
color: Colors.blueAccent,
),
colorScheme: const ColorScheme.dark(
background: Color.fromARGB(255, 18, 18, 18),
secondary: Colors.lightBlueAccent,
primary: Colors.indigo,
),
appBarTheme: const AppBarTheme(
systemOverlayStyle: SystemUiOverlayStyle(
statusBarColor: Colors.indigo,
statusBarIconBrightness: Brightness.dark),
),
);
static Future<void> darkThemeState() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
if (preferences.getBool('temaSecildiMi') == null) {
_themeNotifier = ValueNotifier(ThemeMode.system);
preferences.setBool('temaSecildiMi', false);
}
if (!preferences.getBool('temaSecildiMi')!) {
_themeNotifier = ValueNotifier(ThemeMode.light);
} else {
_themeNotifier = ValueNotifier(ThemeMode.dark);
}
}
static Future<void> saveDarkTheme(bool state) async {
SharedPreferences preferences = await SharedPreferences.getInstance();
await preferences.setBool('temaSecildiMi', state);
await selectDarkTheme();
}
static Future<void> selectDarkTheme() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
final List<bool> getDarkThemeStates = [
preferences.getBool("_temaSec1") ?? false,
preferences.getBool("_temaSec2") ?? false,
preferences.getBool("_temaSec3") ?? false
];
if (getDarkThemeStates[0]) {
returnDarkThemeData = _temaSec1;
}
if (getDarkThemeStates[1]) {
returnDarkThemeData = _temaSec2;
}
if (getDarkThemeStates[2]) {
returnDarkThemeData = _temaSec3;
}
if (!getDarkThemeStates[0] &&
!getDarkThemeStates[1] &&
!getDarkThemeStates[2]) {
returnDarkThemeData = _temaSec3;
}
await preferences.setBool("_temaSec1", getDarkThemeStates[0]);
await preferences.setBool("_temaSec2", getDarkThemeStates[1]);
await preferences.setBool("_temaSec3", getDarkThemeStates[2]);
print("_temaSec1 = ${getDarkThemeStates[0]}");
print("_temaSec2 = ${getDarkThemeStates[1]}");
print("_temaSec3 = ${getDarkThemeStates[2]}");
}
}
main.dart
dosyasına geldikten sonra aşağıdaki kodu yazıyoruz.
Kod:
import 'package:flutter/material.dart';
import 'package:theme_changer/home.dart';
import 'package:theme_changer/tema.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await TemaSinifi.darkThemeState();
await TemaSinifi.selectDarkTheme();
runApp(const MainApp());
}
class MainApp extends StatelessWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context) {
return ValueListenableBuilder<ThemeMode>(
valueListenable: TemaSinifi.themeNotifierAl!,
builder: (BuildContext context, ThemeMode currentMode, Widget? widget) {
return MaterialApp(
home: const HomePage(),
darkTheme: TemaSinifi.secilenTemayiAl,
theme: TemaSinifi.acikTemayiAl,
themeMode: currentMode,
);
},
);
}
}
Bu kodda farklı olarak görebileceğiniz method ve sınıflar
WidgetsFlutterBinding.ensureInitialized
, ValueListenableBuilder
ve TemaSinifi
.>
WidgetsFlutterBinding.ensureInitialized()
methodu bizim widget ağacımızı (normal şartlarda bakıldığında sürekli olarak sınıflara argüman verdiğimiz iç içe yazılan widget sınıfları ile yazılmış yapıya deniyor) diğer sınıfların ve diğer sınıflardan miras alınan değişkenlerin başlatılması gerektiğini burada belirliyoruz.>
ValueListenableBuilder
sınıfı ise bizim belirlediğimiz türde değer tutan, değer değiştiğinde widget ağacı üzerinden işlem yapabilmemize olanak sağlayan bir sınıf. Bunu tema değişikliği için kullanıyoruz. Daha detaylı bilgi için türkçe kaynak bırakıyorum;>
TemaSinifi
sınıfı ise bizim ValueListenableBuilder
sınıfı ile haberleşmesine yarayacak ValueNotifier
sıfından örnekler barındıran sınıfımız.>
TemaSinifi.selectDarkTheme()
methodu neden en başta yazıldı diye sorarsanız bir başka method yazmak istemeyip aynı methodun içinde TemaSinifi
sınıfındaki returnDarkThemeData
değişkenine değer atamak için yazdım. Eğer bunu kaldırırsanız null value hatası verecektir. Dilerseniz düzenleyip ona göre bir mantık ile kullanabilirsiniz.main.dart
dosyasında görüldüğü üzere HomePage
adı verilen bir sınıf mevcut. Bu sınıfı implement edelim hemen. home.dart
adında dart dosyası oluşturun ve aşağıdaki gibi doldurun.
Kod:
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:theme_changer/tema.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
bool theme1 = false;
bool theme2 = false;
bool theme3 = false;
@override
void initState() {
super.initState();
readThemeState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).colorScheme.background,
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.primary,
title: Text("data"),
),
body: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: Text(
"TEMA SEÇ",
style: TextStyle(color: Theme.of(context).colorScheme.primary),
)),
const SizedBox(height: 10),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Stack(
alignment: Alignment.center,
children: [
Visibility(
visible: theme1,
child: Container(
height: 35,
width: 35,
decoration: BoxDecoration(
border: Border.all(
width: 5,
color: Theme.of(context).colorScheme.primary),
borderRadius: BorderRadius.circular(10)),
),
),
InkWell(
onTap: () async {
if (theme1) {
theme2 = false;
theme3 = false;
} else {
theme1 = true;
theme2 = false;
theme3 = false;
}
await setThemeState();
},
child: Container(
height: 30,
width: 30,
decoration: BoxDecoration(
border: Border.all(width: 1),
borderRadius: BorderRadius.circular(10),
gradient: const LinearGradient(
stops: [0.46, 0.56],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [Colors.amber, Colors.black87])),
),
),
],
),
Stack(
alignment: Alignment.center,
children: [
Visibility(
visible: theme2,
child: Container(
height: 35,
width: 35,
decoration: BoxDecoration(
border: Border.all(
width: 10,
color: Theme.of(context).colorScheme.primary),
borderRadius: BorderRadius.circular(10)),
),
),
InkWell(
onTap: () async {
if (theme2) {
theme1 = false;
theme3 = false;
} else {
theme1 = false;
theme2 = true;
theme3 = false;
}
await setThemeState();
},
child: Container(
height: 30,
width: 30,
decoration: BoxDecoration(
border: Border.all(width: 1),
borderRadius: BorderRadius.circular(10),
gradient: const LinearGradient(
stops: [0.46, 0.56],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [Colors.deepPurple, Colors.black87])),
),
),
],
),
Stack(
alignment: Alignment.center,
children: [
Visibility(
visible: theme3,
child: Container(
height: 35,
width: 35,
decoration: BoxDecoration(
border: Border.all(
width: 5,
color: Theme.of(context).colorScheme.primary),
borderRadius: BorderRadius.circular(10)),
),
),
InkWell(
onTap: () async {
if (theme3) {
theme1 = false;
theme2 = false;
} else {
theme1 = false;
theme2 = false;
theme3 = true;
}
await setThemeState();
},
child: Container(
height: 30,
width: 30,
decoration: BoxDecoration(
border: Border.all(width: 1),
borderRadius: BorderRadius.circular(10),
gradient: const LinearGradient(
stops: [0.46, 0.56],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [Colors.indigo, Colors.black87],
),
),
),
),
],
)
],
)
],
),
),
);
}
Future<void> setThemeState() async {
TemaSinifi.themeNotifierAl!.value = ThemeMode.light;
TemaSinifi.themeNotifierAl!.value = ThemeMode.dark;
SharedPreferences preferences = await SharedPreferences.getInstance();
preferences.setBool("_temaSec1", theme1);
preferences.setBool("_temaSec2", theme2);
preferences.setBool("_temaSec3", theme3);
await TemaSinifi.selectDarkTheme();
await TemaSinifi.saveDarkTheme(true);
setState(() {});
}
Future<void> readThemeState() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
theme1 = preferences.getBool("_temaSec1") ?? false;
theme2 = preferences.getBool("_temaSec2") ?? false;
theme3 = preferences.getBool("_temaSec3") ?? false;
setState(() {});
}
}
Theme.of(context).colorSheme.background
yazılı olarak gördüğünüz bu değişken ThemeData sınıfını kullanarak yaptığımız değişiklikler sonucunda değerleri almamıza yarıyor. ThemeData sınıfını da TemaSinifi
sınıfına giderseniz kullandığımızı ve neden colorSheme'den background'a ulaştığımızıda anlayabilirsiniz.Burada yapılan işlem ise başta üç adet(tema adedimiz kadar) bool veri tipi tanımladık başlangıçları false çünkü başta temalar seçilmemiş olacak. initState methoduna baktığımızda da
readThemeState
methodu bizi karşılıyor. Bilindiği üzere initState
, StatefulWidget
sınıfından miras alan widgetımızı güncelleyerek yeniden çizmemize olanak sağlıyor. Her çizim bizler için maliyet olduğundan bir defalığına çalışacak method olarak initState
methodunu geliştirmişler. Bunu override ederek super.initState
satırı var olan methodu kullanıyor ardından overide ettiğimizden altındaki satırları işliyor, bunu bir kez yapıyor. Tekrar yapması için uygulamanın ya da o sayfanın yeniden çağrılması gerekiyor. Bizim anasayfamız HomePage
sınıfı olduğundan başlangıçta temaların durumlarını okuyoruz.readThemeState
methodunda bool değişkenlerimizin değerlerini setThemeState
methodunda kaydettiklerimizi okuyoruz. Okuduğumuz değerleri SharedPreferences
sayesinde gerçekleştiriyoruz, getBool
methodu null safety bool döndüren bir method. Bu eğer null dönerse theme1 = preferences.getBool("_temaSec1") ?? false
yazarak direk false değerini atıyoruz. İşlemler sonrası setState
methodunu çağırarak widgetı yeniliyoruz.Diğer konularda görüşmek üzere.
Son düzenleme: