CodeBlanks Logo Icon CodeBlanks

Özet - setState

setState ve lifting pattern'ini birleştirerek sayaç ekranı yaparsın.

Özet - setState

Bu bölümde üç şey öğrendin: setState bir State'i dirty işaretler ve sonraki frame'de build'i yeniden çalıştırır; lifting state up ile state'i kardeşlerin ortak ebeveynine taşıyıp callbacklerle çocuklara iletirsin; mounted kontrolü ile async sonrası güvenli setState yaparsın.

if (mounted) {
  setState(() { _sayac++; });
}

State'i mümkün olduğunca küçük tut ve her async sonrası mounted'ı kontrol et.

Uygulama Sayaç ekranı: setState + lifting + mounted

SayacEkrani stateful widget'ında _sayac ve _durum tut. _artir ve _azalt metotları setState ile _sayac'ı değiştirsin. _durumYukle async metodu 100 ms bekleyip if (mounted) ile _durum'u 'hazır' yapsın. initState içinde print('başlangıç: $_sayac') çağır ve _durumYukle'yi başlat. Gosterici stateless widget'ı deger alsın.

Başlangıç kodu

main.dart
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: SayacEkrani());
  }
}

class SayacEkrani extends StatefulWidget {
  @override
  _SayacEkraniState createState() => _SayacEkraniState();
}

class _SayacEkraniState extends State<SayacEkrani> {
  int _sayac = 0;
  String _durum = 'bekliyor';

  // _artir ve _azalt: setState ile _sayac'ı değiştir
  // _durumYukle: async, mounted kontrolü, setState ile _durum='hazır'

  @override
  void initState() {
    super.initState();
    print('başlangıç: $_sayac');
    _durumYukle();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Sayaç - $_durum')),
      body: Center(child: Gosterici()),
      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          FloatingActionButton(
            onPressed: _azalt,
            child: Icon(Icons.remove),
          ),
          SizedBox(width: 8),
          FloatingActionButton(
            onPressed: _artir,
            child: Icon(Icons.add),
          ),
        ],
      ),
    );
  }
}

class Gosterici extends StatelessWidget {
  // deger field ve constructor ekle

  @override
  Widget build(BuildContext context) {
    return Text('$deger', style: TextStyle(fontSize: 48));
  }
}

Örnek çözüm

main.dart
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: SayacEkrani());
  }
}

class SayacEkrani extends StatefulWidget {
  @override
  _SayacEkraniState createState() => _SayacEkraniState();
}

class _SayacEkraniState extends State<SayacEkrani> {
  int _sayac = 0;
  String _durum = 'bekliyor';

  Future<void> _durumYukle() async {
    await Future.delayed(Duration(milliseconds: 100));
    if (mounted) {
      setState(() {
        _durum = 'hazır';
      });
    }
  }

  void _artir() {
    setState(() {
      _sayac++;
    });
  }

  void _azalt() {
    setState(() {
      _sayac--;
    });
  }

  @override
  void initState() {
    super.initState();
    print('başlangıç: $_sayac');
    _durumYukle();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Sayaç - $_durum')),
      body: Center(child: Gosterici(deger: _sayac)),
      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          FloatingActionButton(
            onPressed: _azalt,
            child: Icon(Icons.remove),
          ),
          SizedBox(width: 8),
          FloatingActionButton(
            onPressed: _artir,
            child: Icon(Icons.add),
          ),
        ],
      ),
    );
  }
}

class Gosterici extends StatelessWidget {
  final int deger;
  Gosterici({required this.deger});

  @override
  Widget build(BuildContext context) {
    return Text('$deger', style: TextStyle(fontSize: 48));
  }
}

Beklenen çıktı

başlangıç: 0

Soru ve cevaplar

Dersin sorularını cevaplarıyla birlikte inceleyebilirsin. Kendini denemek istersen dersi interaktif olarak çöz.

1 Async bir Future bekledikten sonra setState'i güvenli çağırmak için ne yapmalısın?

  • Önce if (mounted) kontrolü yap Doğru cevap
  • Önce dispose çağır
  • Önce initState'i tekrar çağır
  • Önce Future'i iptal et

Açıklama: Async sonrası widget dispose edilmiş olabilir; if (mounted) ile State'in hâlâ ağaçta olduğu doğrulanır, sonra setState çağrılır.

2 Çocuğun callback field'ını tanımla

Soru
class Dugme extends StatelessWidget {
  final VoidCallback ____;
  Dugme({required this.onTikla});
  @override
  Widget build(BuildContext context) => Text('Düğme');
}
Cevap
class Dugme extends StatelessWidget {
  final VoidCallback onTikla;
  Dugme({required this.onTikla});
  @override
  Widget build(BuildContext context) => Text('Düğme');
}

Açıklama: Lifting deseninde çocuk, callback'i constructor parametresi olarak alır ve bir field'da tutar; ebeveyn setState ile state'i günceller.

3 setState bölümünün parçaları

Kavram Anlamı
setState State değişikliğini framework'e bildirir
mounted Async sonrası setState için güvenlik kontrolü
Lifting state up Kardeşlerin ortak ebeveynine state taşır
Callback Çocuğun ebeveyne olay bildirme yolu

Orta seviye bölümündeki tüm dersler