Ana içeriğe atla

Flutter’ı Anlamak: Stateful ve Stateless Widget’lar ve Basit Bir Quiz Uygulaması

Flutter, Google’ın sunduğu bir UI (kullanıcı arayüzü) framework’üdür ve geliştiricilere mobil, web ve masaüstü platformları için tek bir kod tabanından derlenebilen uygulamalar oluşturma gücü sunar. Flutter’ın temelinde yatan en önemli konseptlerden biri widget’lardır. Widget’lar, herhangi bir Flutter uygulamasının temel taşlarıdır ve Flutter’da ustalaşmak için bu konsepti anlamak şarttır. Bugün, Flutter’ın iki önemli widget türünü ele alacağız: Stateful Widget ve Stateless Widget’lar. Aynı zamanda basit bir quiz uygulaması oluşturarak pratik yapacağız.


Widget’ların Temelleri

Flutter’da neredeyse her şey bir widget’tır. Butonlar, metinler, medya içerikleri ve hatta uygulamanın başlığının bulunduğu AppBar bile bir widget’tır. Bu widget’lar, Widget ağacı oluşturacak şekilde birbirlerinin içine yerleştirilir. Widget ağacı, uygulamanızın yapısını temsil eder. Örneğin, bir Scaffold widget'ının (ekranın ana yapısı) içinde bir AppBar widget'ı (en ütteki başlık çubuğu) ve bir Body widget'ı (ana içerik bölgesi) bulunabilir. Aslında tüm uygulama birbirinin içine geçmiş widget'lardan (nested widgets) oluşur. İşte bu hiyerarşik yapıya Widget ağacı denir.

A graphic, showing a Flutter Widget Tree. Flutter Widget ağacını gösteren bir grafik.

Bunu bir ev inşa etmeye de benzetebiliriz: Scaffold temeli oluşturur, AppBar çatıyı temsil eder ve body içine yerleştirilen widget'lar da odalar ve mobilyalar gibidir.

Stateless Widget Nedir?

Stateless widget, adından da anlaşıldığı gibi statiktir ve oluşturulduktan sonra değişmez. Bu tür widget’lar, güncellenmesi gerekmeyen arayüz bölümlerinde kullanılır. Örneğin, statik bir metin gösteren veya kullanıcı etkileşimi içermeyen sayfalar için Stateless widget kullanabilirsiniz.

Basit bir Stateless widget örneği:

class MyStatelessWidget extends StatelessWidget {
const MyStatelessWidget({super.key});

@override
Widget build(BuildContext context) {
return Text('Bu bir stateless widgettır.');
}
}

build metodu yalnızca bir kez çağrılır. Bu nedenle Stateless widget'lar statik içerik için oldukça verimlidir. Ancak dinamik davranışlar (kullanıcı girdisine göre metin güncelleme gibi) gerekiyorsa, Stateful widget kullanmanız gerekir.

Stateful Widget Nedir?

Stateful widget’lar, dinamik davranışlara olanak tanır. Bu widget’lar, state (durum) değişimlerine tepki olarak UI’ı güncelleyebilir. Kullanıcı etkileşimi, animasyonlar veya gerçek zamanlı güncellemeler için bu tür widget’lar kullanılır.

Bir Stateful widget, iki sınıftan oluşur:

  1. Ana widget sınıfı (“MyStatefulWidget”).
  2. State sınıfı (“_MyStatefulWidgetState”): mantığı ve dinamik veriyi tutar.

Basit bir Stateful Widget örneği:

class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({super.key});

@override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int counter = 0;
void incrementCounter() {
setState(() {
counter++;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Sayaç: $counter'),
ElevatedButton(
onPressed: incrementCounter,
child: Text('Artır'),
),
],
);
}
}

setState() Metodu

Yukarıda, incrementCounter() metodunda bir örneğini verdiğim setState() metodu; Flutter'a state'in (durum) güncellendiğini ve buna bağlı olarak kullanıcı arayüzünün yeniden oluşturulması gerektiğini bildirir. setState(), kendi içerisine yazdığımız değişkenlerin arayüzde kullanıldığı kısımları yeniden render eder. Bu sayede, bir widgetın içindeki dinamik değerlerin değişmesi durumunda (örnekteki sayacın artırılması, bir butonun renginin değişmesi vb.) UI'ın doğru bir şekilde güncellenmesini sağlar.

Basit Bir Quiz Uygulaması: Pratik Bir Örnek

Teoriyi pratikle birleştirerek basit bir quiz uygulaması oluşturalım. Uygulama, bir dizi soru gösterecek, kullanıcıların cevapları seçmesine izin verecek ve sonuçları gösterecek.

Adım 1: Ana Yapının Oluşturulması

Öncelikle Flutter uygulamasının ana giriş noktasını oluşturalım:

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
return MaterialApp(
home: QuizPage(),
);
}
}

Adım 2: Quiz Sayfasını Oluşturmak

Bu sayfa, quiz durumunu yönetmek için Stateful widget kullanacak, çünkü kullanıcı etkileşimi ve dinamik içerik güncellemeleri söz konusu olacak. Stateful widget sayesinde kullanıcının seçtiği cevaplar anında algılanacak ve ekranda gösterilecektir.

Quiz sayfası, aşağıdaki gibi bir widget ağacı yapısında organize edilecek:

  • Sorular bir Text widget'ı olarak gösterilecek.
  • Seçenekler, her biri birer OutlinedButton olarak sunulacak ve tıklama olaylarının işlenmesi için onPressed parametresine verilecek fonksiyon kullanılacak.
  • Kullanıcının cevapları, bir liste (state) kullanılarak tutulacak ve her bir butona tıklandığında bu liste güncellenecek.
  • Test bittikten sonra da “Gönder” butonuna tıklayıp, Navigator.push() metodu yardımıyla bir sonraki sayfaya (Result()) geçilecek. checkAnswers() metodu yardımıyla cevaplar kontrol ediliyor, döndürülen doğru sayısı da Result sayfasına parametre olarak gönderiliyor: Result(result: checkAnswers()).

setState() metoduyla, kullanıcı bir seçeneği tıkladığında state yenilenecek ve tıklanan seçenek için buton rengi değişecek. Bu sayede dinamik bir quiz deneyimi sağlanacak.

import 'package:flutter/material.dart';
import 'package:yt_tutorial_01/result.dart';

class Test extends StatefulWidget {
const Test({super.key});

@override
State<Test> createState() {
return _TestState();
}
}

class _TestState extends State<Test> {

var selected = [0, 0, 0, 0, 0];
var cheatSheet = [1, 1, 1, 1, 2];

void selectAnswer(int index, int value) {
setState(() {
selected[index] = value;
});
print('selected: $selected');
}

int checkAnswers() {
int result = 0;

for (var i = 0; i < cheatSheet.length; i++) {
if (selected[i] == cheatSheet[i]) {
result ++;
}
}

return result;
}

@override
Widget build(context) {
return Scaffold(
appBar: AppBar(title: Text('Sorular'),),
body: SingleChildScrollView(
child: Align(
alignment: Alignment.center,
child: Column(
children: [


// Soru 1
const Text('Türkiye nin başkenti neresi?'),
OutlinedButton(
style: OutlinedButton.styleFrom(
backgroundColor: selected[0] == 1 ? Colors.blue : null,
),
onPressed: () {
selectAnswer(0, 1);
},
child: const Text('Ankara'),),
OutlinedButton(
style: OutlinedButton.styleFrom(
backgroundColor: selected[0] == 2 ? Colors.blue : null,
),
onPressed: () {
selectAnswer(0, 2);
}, child: const Text('İstanbul'),),
const SizedBox(height: 20,),


// Soru 2
const Text('"const" anahtar kelimesi ne anlama gelir?'),
OutlinedButton(
style: OutlinedButton.styleFrom(
backgroundColor: selected[1] == 1 ? Colors.blue : null,
),
onPressed: () {
selectAnswer(1, 1);
}, child: const Text('O değişkenin değişmeyeceğini gösterir.'),),
OutlinedButton(
style: OutlinedButton.styleFrom(
backgroundColor: selected[1] == 2 ? Colors.blue : null,
),
onPressed: () {
selectAnswer(1, 2);
}, child: const Text('O değişkenin değişeceğini gösterir.'),),
const SizedBox(height: 20,),


// Soru 3
const Text('Column widget ı ne işe yarar?'),
OutlinedButton(
style: OutlinedButton.styleFrom(
backgroundColor: selected[2] == 1 ? Colors.blue : null,
),
onPressed: () {
selectAnswer(2, 1);
}, child: const Text('İçine eklenilenleri alt alta sıralar.'),),
OutlinedButton(
style: OutlinedButton.styleFrom(
backgroundColor: selected[2] == 2 ? Colors.blue : null,
),
onPressed: () {
selectAnswer(2, 2);
}, child: const Text('İçine eklenilenleri yan yana sıralar.'),),
const SizedBox(height: 20,),



// Soru 4
const Text('Stateful Widget nedir?'),
OutlinedButton(
style: OutlinedButton.styleFrom(
backgroundColor: selected[3] == 1 ? Colors.blue : null,
),
onPressed: () {
selectAnswer(3, 1);
}, child: const Text('State değişikliğine müsait olan Widget tır.'),),
OutlinedButton(
style: OutlinedButton.styleFrom(
backgroundColor: selected[3] == 2 ? Colors.blue : null,
),
onPressed: () {
selectAnswer(3, 2);
}, child: const Text('State değişikliğine müsait olmayan Widget tır.'),),
const SizedBox(height: 20,),



// Soru 5
const Text('Stateless Widget nedir?'),
OutlinedButton(
style: OutlinedButton.styleFrom(
backgroundColor: selected[4] == 1 ? Colors.blue : null,
),
onPressed: () {
selectAnswer(4, 1);
}, child: const Text('State değişikliğine müsait olan Widget tır.'),),
OutlinedButton(
style: OutlinedButton.styleFrom(
backgroundColor: selected[4] == 2 ? Colors.blue : null,
),
onPressed: () {
selectAnswer(4, 2);
}, child: const Text('State değişikliğine müsait olmayan Widget tır.'),),
const SizedBox(height: 20,),

ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.blue),
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => Result(result: checkAnswers()),));
}, child: const Text('Gönder', style: TextStyle(color: Colors.white),)),

],
),
),
),
);
}
}


Adım 3: Sonuçları Göstermek

Son olarak, doğru cevap sayısını gösteren bir sayfa oluşturalım. Bu sayfa, quiz uygulamasının sonucunu kullanıcıya sunmak için tasarlanacak ve herhangi bir state değişikliği gerektirmediği için Stateless Widget kullanılacak.

Sonuç sayfasını oluşturmak için aşağıdaki adımlar izlenecek:

  • ResultPage sınıfı, kendi constructor metodu aracılığıylacorrectAnswers adında bir parametre alır. Bu parametre, quiz sayfasından hesaplanan doğru cevap sayısını içerir.
  • Ekranda kullanıcıya sonuç bilgisi bir Text widget ile gösterilecek.
  • Sayfa basit bir yapıda olacak ve herhangi bir etkileşim içermeyecek.
import 'package:flutter/material.dart';

class Result extends StatelessWidget {
const Result({super.key, required this.result});

final int result;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Sonuç'),),
body: Center(child: Text('Doğru sayısı: $result'),),
);
}
}

Özet

  1. Stateless widget’lar, statik ve değişmez içerikler içindir.
  2. Stateful widget’lar, dinamik ve etkileşimli içerikler içindir.
  3. setState() metodu, değişen değişkenlere bağlı olarak UI'ı günceller.
  4. Flutter widget ağacı yapısı, esnek bir tasarım sağlar.

(Flutter’da var olan diğer widgetlara göz atmak isterseniz Flutter Widget Catalog’a göz atabilirsiniz!)

Bu derste Flutter’ın temel taşları olan Stateful ve Stateless widget’ların farkını, bunları ne zaman kullanmanız gerektiğini ve setState() metodunun dinamik içerik yönetimindeki önemini öğrendik. Ayrıca, bu bilgileri kullanarak basit bir quiz uygulaması oluşturduk ve widget ağacı yapısını uygulamalı bir şekilde deneyimledik. Flutter’ı daha iyi anlamak ve bu becerilerinizi geliştirmek için bol bol pratik yapmaya, öğrendiklerinizi yeni projelerde uygulamaya devam edin. Bir sonraki derslerde daha karmaşık Flutter konseptlerini hep birlikte keşfedeceğiz. Flutter’da ustalaşmak için pratik yapmaya devam edin ve bizi takipte kalın!

Yorumlar

Bu blogdaki popüler yayınlar

Flutter Dart Dersleri: Sıfırdan Flutter ile Mobil Uygulama Geliştirme

Flutter, mobil uygulama geliştirme dünyasında çığır açan bir teknolojidir. Google tarafından geliştirilen bu açık kaynaklı UI (kullanıcı arayüzü) framework’ü, geliştiricilere hem Android hem iOS hem de web için uygulamalar oluşturma kolaylığı sunar. Bu yazıda, Flutter’ın ne olduğunu, nasıl çalıştığını ve Flutter ile mobil uygulama geliştirmenin avantajlarını detaylıca inceleyeceğiz. Flutter Nedir? Flutter, Google’ın geliştirdiği bir çapraz platform (cross-platform) uygulama geliştirme aracıdır. Bu framework, tek bir kod tabanı kullanarak birden fazla platformda çalışan uygulamalar oluşturmanıza olanak tanır. Flutter’ın temel avantajları şu şekilde sıralanabilir: Tek Kod Tabanı : Hem Android hem iOS hem de web için ayrı projeler oluşturma ihtiyacını ortadan kaldırır. Performans : Dart programlama dili sayesinde Flutter uygulamaları hızlı ve akıcı çalışr. Esneklik : Flutter’ın önceden tanımlı widget’ları sayesinde zengin ve profesyonel arayüzler oluşturabilirsiniz. Flutter’ı Neden Kullan...