Flutter Cubit-Bloc Yaklaşımları Fonksiyon ve Event Yaklaşım

Gauloran

Global Moderatör
7 Tem 2013
8,124
610
local
eFwAM4WXhu.png


Selamlar bu konuda state management yöntemlerinden Bloc mimarisini kullanarak mobil uygulama geliştireceğiz ve Bloc yapısı ile ilgili sağlam bir temel atmış olacağız. Konuya başlamadan önce bilinmesi gerekenler:
Kod:
- Temel seviye Dart bilgisi
- Temel seviye Flutter bilgisi (Provider'ın mantığını bilmek ve diğer temel şeyler)

İnceleyebileceğiniz diğer konularım:

0'dan İleri Seviyeye Mobil Uygulama Geliştirme Eğitimi Veriyorum #1
0'dan İleri Seviyeye Mobil Uygulama Geliştirme Eğitimi Veriyorum #2
0'dan İleri Seviyeye Mobil Uygulama Geliştirme Eğitimi Veriyorum #3
0'dan İleri Seviyeye Mobil Uygulama Geliştirme Eğitimi Veriyorum #4
0'dan İleri Seviyeye Mobil Uygulama Geliştirme Eğitimi Veriyorum #5
0'dan İleri Seviyeye Mobil Uygulama Geliştirme Eğitimi Veriyorum #6
0'dan İleri Seviyeye Mobil Uygulama Geliştirme Eğitimi Veriyorum #7
0'dan İleri Seviyeye Mobil Uygulama Geliştirme Eğitimi Veriyorum #8
0'dan İleri Seviyeye Mobil Uygulama Geliştirme Eğitimi Veriyorum #9
0'dan İleri Seviyeye Mobil Uygulama Geliştirme Eğitimi Veriyorum #10
0'dan İleri Seviyeye Mobil Uygulama Geliştirme Eğitimi Veriyorum #11
0'dan İleri Seviyeye Mobil Uygulama Geliştirme Eğitimi Veriyorum #12
0'dan İleri Seviyeye Mobil Uygulama Geliştirme Eğitimi Veriyorum #13
0'dan İleri Seviyeye Mobil Uygulama Geliştirme Eğitimi Veriyorum #14
0'dan İleri Seviyeye Mobil Uygulama Geliştirme Eğitimi Veriyorum #15
Flutter & Dart Fundamentals | Örnek Quiz Uygulaması #16
Flutter uygulamalarında debugging mantığı #17
Flutter element tree render tree mantığı

Devam edelim. Öncelikle yeni bir Flutter projesi oluşturuyoruz. Ben kendi snippetlarımı kullanıyorum eğer snippetınız yoksa default olarak gelen main.dart dosyanızın içeriğini Vscode üzerinde (Android Studio kullanıyorsanız da olur //.* ile yorum satırlarını silebilirsiniz. Android studio da hepsini seçin silin tekrar sildiğinizde boşluklar gider vscode üzerinde replace edip ctrl+alt+f çekerek boşlukları temizleyebilirsiniz Android Studio üzerinde de yaparsınız fark etmiyor ben Vscode kullanıyorum.

Projeyi oluşturduktan ve yorum satırlarını sildikten sonra main.dart içerisinde default olarak gelen MyHomePage sınıfının kodlarını başka bir dosya oluşturup home_page.dart şeklinde yine lib'in içerisine atalım. Özetle şu anda iki dosyamız var main.dart ve home_page.dart ve ikisinin de içeriği şu şekilde:

sBrC5IHO.png


home_page.dart

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

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

main.dart

Kod:
import 'package:flutter/material.dart';
import 'package:weather/home_page.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

Şimdi fark ettiyseniz hiçbir kod yazmadık henüz default olarak gelen counter uygulamasının kodları bunlar yeni flutter app projesi oluşturduğunuzda. Şimdi state management yöntemimiz olan Bloc package'ını yükleyelim projeye entegre edelim. Bunu halledene kadar kodlarımızda düzenleme yapmayacağız.

YLFQ2h7qN.png



Bloc diye arattığımızda 2 sonuç alıyoruz birisi bloc diğeri flutter_bloc. Bloc package Bloc sınıfı ile ilgili logic komponentlerini içerir flutter_bloc ise UI katmanında implemente edebileceğimiz şeyleri içerir. Özetle flutter_bloc bloc package'ını içerir zaten. Onu yükleyelim.

Kod:
flutter pub add flutter_bloc

terminale bunu yazarak ekleyebilirsiniz.

Şimdi state'i yönetebiliriz aslında gereken yükleme bu kadardı. Birkaç yaklaşım var yine Bloc kullanımı da çeşitlendirilebilir. Aradaki farkları da dahil ayrı ayrı bahsedeceğim.

YAKLAŞIM: CUBIT KULLANIMI

lib'in hemen altında cubit diye bir klasör oluşturuyoruz ve counter_cubit.dart diye bir dosya oluşturuyoruz. _cubit olarak sonunu o şekilde yapmamızın nedeni bunun bir cubit dosyası olduğunu anlayalım diye bu şekilde klasörledik. Counter olayı da uygulamamıza yönelik olduğu için.

Ve hata aldık...

dkhIoHvrQ.png


Ne oluyor daha sınıfın ismini yazmadan hata veriyor?! Sakin oluyoruz. Tansiyonumuz düşmesin diye LOTR evrenine birkaç dakikalığına gerçek hayattan ve cubitlerden soyutlanıp uğrayıp geliyoruz hemen.


Evet hoş geldin Tr simülasyonuna tekrar geri geldin ben de elf olmak isterdim ama yapacak bir şey yok neyse Fluttera devam edelim.

Aldığımız hata sen bir Cubit oluşturdun ama constructor oluşturmadın daha doğrusu default değeri vermen gerekiyor diyor bize bu hata. Eğer cubitlerin kaynak koduna gidecek olursak yani bloc package dan geliyor zaten. Constructorı olduğunu fark edeceksiniz initialState yazar hatta. Child sınıftan parametre geçilmesi gerekiyor bu nedenle default değer olarak size bahsettiğim initialState'i geçmeniz gerekiyor.

initialState ne nasıl geçeceğiz neyi geçeceğiz ne oluyor?
Bizim yöneteceğimiz ne bu uygulamada? Bu bir counter app değil mi default olarak sağ alttaki floatingActionButton'a defalarca tıkladığımda uygulamanın ortasındaki sayı 1,2,3 diye artıyor. Bunu bloc ile yönetebilmek istiyorum. Bu state'i yöneteceğim. initialState'im ne olacak peki yani başlangıç durumum ne olacak? 0 olacak 0'dan başlasın istiyorum. Bu değeri nasıl geçeceğim?

Şöyle:

Kod:
import 'package:flutter_bloc/flutter_bloc.dart';

class CounterCubit extends Cubit {
  CounterCubit() : super(0); //Turkhackteam.org
}

4X2vaA.png


Bu logic'i buradan siliyoruz sildiğinizde hata veren yerler olacak örneğin Text widgetı o logici kullanıyor olacak hemen Text'in içini '' boş falan bırakın şimdilik. Ne hatası ne oluyor nasıl çözeceğim dur anlamıyorum ne diyor bu diyenler için düzeltilmiş hali:

home_page.dart'ın içeriği:

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

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {},
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

Bunları epey değiştireceğiz. Şimdi cubit'imizi tanımlayalım instance yani.

N4Abn.png


Nereden geliyor bunlar biz böyle bir şey yazmadık ki ? Bunlar cubitten geliyor zaten yazmamıza gerek yok önceden yazılmış. Önceden yazılmış her şeyi yazmamıza gerek yok mantığında söylemedim dilerseniz kendi compilerınızı yazabilirsiniz. Hem kendi compilerınızı yazmayıp kendinize yazılımcı mı diyorsunuz?

6WPKQ8XuyehI.png


Artık direkt counter'ı Text widgetımıza geçirebiliriz Text('$counter') şeklinde

Şimdi yaptık ama şöyle bir sıkıntı var counter'a gidip bakın dynamic olarak gözüküyor ama dynamic kullanmak çok sıkıntılı yatırım tavsiyesi değildir. Dolayısıyla türünü belirtmek için oluşturduğumuz Cubitimize gidip <int> olarak yönettiğimiz değerin türünü belirtiyoruz.

Kod:
import 'package:flutter_bloc/flutter_bloc.dart';

class CounterCubit extends Cubit<int> {
  CounterCubit() : super(0); //Turkhackteam.org
}

Bu arada int olarak belirttikten sonra initialState olarak artık String falan girerseniz hata alırsınız. Şimdi iyi hoş ama tıklıyoruz 0 artmıyor ?

RAKZV8.png


Evet zaten henüz logic yazmadık hatta bir logic vardı direkt setState çakılarak yapılan onu da silmiştik dolayısıyla 0 olarak initialState geliyor fakat artıracak bir logic yazmadık. Şimdi onu halletmeliyiz ve bunu da tahmin edeceğiniz gibi cubit'imizde yapacağız.

Zsh0toN.png


Cubitimizde logic yazacağız ama state'i böyle artıramıyoruz. Bunun için emit fonksiyonu oluşturmuşlar önceden yazmışlar emit(state) diyerek state'i güncelleyebiliyoruz. Dolayısıyla emit(state + 1); yaparsak state'i state+1 olarak değiştirecektir ve dinleyenleri haberdar edecektir.

home_page.dart dosyamızdaki

Kod:
floatingActionButton: FloatingActionButton(
        onPressed: () => CounterCubit().increment(),
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),

FloatingActionButton kısmındaki onPressed'i bu şekilde düzenlediğimizde artık logicimiz de var butona tıklandığında 1,2,3 diye artacak herhalde diye bekleyişe giriyoruz fakat yine olmuyor ama neden?

FkHOBMrWIAMElka.jpg


Aslında basit. Fark ettiyseniz 2 tane instance oluşturuyoruz CounterCubit'ten birincisini yukarıda oluşturmuştuk hatta onun değerini Text widgetında gösteriyoruz. E burada yine oluşturuyoruz CounterCubit(). diyerek yeni oluşturduğumuz instance'ın increment metodunu tetikliyoruz aslında sorun bu. O nedenle yukarıda

final counterCubit = CounterCubit(); diye 1 kere tanımlarsak cubitimizi kullanırken aynı instance'ı kullanmak için counterCubit. diyerek kullanmamız sorunu çözer.


Kod:
import 'package:flutter/material.dart';
import 'package:weather/cubit/counter_cubit.dart';

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final counterCubit = CounterCubit();
  @override
  Widget build(BuildContext context) {
    final counter = counterCubit.state;
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$counter',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => counterCubit.increment(),
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

olarak değiştiriyoruz ve tekrar deniyoruz. Butona tıklıyoruz.. E yine olmadı?!


3e812aac8564981eaebccc5231d8c4bd813bfe53.gif


Şu an böyle olduğunuzun farkındayım. Ama olay şu ki aslında koda bakacak olursanız Cubit kendi içerisinde evet artırıyor increment metodu kullanıldığında state+1 oluyor ama home_page.dart'daki kodumuza bakarsanız mesela uygulama başladı 0 olarak default değerimiz var daha sonra butona tıklandı cubit kendi içerisinde state'i güncelledi fakat güncelledi de cubit bunu kendi içinde yaptı şu anda home_page.dart'ın bundan haberi var mı ? yok ki build metodu tetiklenmiyor ki tekrardan ilgili yer nasıl güncellenecek.

Akıllara gelen o ben incrementi tetiklettikten sonra setState'i çakar çözerim diyorsanız o zaman biz niye cubit kullanıyoruz ilk baştaki logic'i niye sildik başka dosyaya alırdım onu ordan çeker kullanır setState atar geçerdim hayır hayır bunu çözmemiz lazım ama setState ile değil. Emit fonksiyonu zaten dinleyicilerini haberdar eder demiştim size. Zaten o çalışıyor onda sıkıntı yok ama sorun bizim dinlemiyor olmamız.

eFwAM4WXhu.png


Dinlemek için ne yapacağız?
Bizim Bloc tarafından yazılmış bir widgetı kullanarak bu cubitleri dinleyebiliyoruz. BlocBuilder diyorlar buna.

Kaynak koduna gidip bakalım B olarak bloc bekliyor bizden adamlar yazmış bloc: counterCubit, diye verebiliriz ama nasıl?
Şöyle aslında baktığımız zaman bizim counterCubit'imiz sonuç olarak bloc'tan gelen bir Cubit değil mi? EVET
Bloc'tan gelen Cubit'in kaynak koduna baktığımız zaman BlocBase den miras almış bir sınıf olduğunu ve BlocBase sınıfının da StateStreamable dan implemente
olduğunu fark ediyoruz. Özetle StateStreamable beklenen bir yere cubit basıp geçebiliyoruz.


Özetle counterCubit'i kullanabiliriz ve BlocBuilder'ın yaptığı şey kendisine verdiğimiz bloc'u inceleyip bize bir builder yapısı sağlıyor.
Bu builder yapısında context, counter olarak yazdım context bildiğimiz context. counter da bizim counter değeri. Tabi bunları yaparken
<CounterCubit, int> şeklinde BlocBuilder'a bildirmemiz gerekiyor neyi kullandığımızı blocumuzu ve state türünün değerinin int olduğunu belirtmemiz gerekiyor bizim uygulamamız özelinde yani.

Kodları inceleyelim:

Kod:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:weather/cubit/counter_cubit.dart';

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final counterCubit = CounterCubit();
  @override
  Widget build(BuildContext context) {
    return BlocBuilder<CounterCubit, int>(
      bloc: counterCubit,
      builder: (context, counter) {
        return Scaffold(
          appBar: AppBar(
            backgroundColor: Theme.of(context).colorScheme.inversePrimary,
            title: Text(widget.title),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                const Text(
                  'You have pushed the button this many times:',
                ),
                Text(
                  '$counter',
                  style: Theme.of(context).textTheme.headlineMedium,
                ),
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () => counterCubit.increment(),
            tooltip: 'Increment',
            child: const Icon(Icons.add),
          ),
        );
      },
    );
  }
}

rpmseD.png


Evet artık çalışıyor. setState kullanmadan yapmış olduk. Ama bu yaptığımız bütün Scaffold'u içeriyor BlocBuilder bunu böyle bırakmayıp optimize edelim sadece Text widgetını ilgilendirsin.

Kod:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:weather/cubit/counter_cubit.dart';

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final counterCubit = CounterCubit();
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            BlocBuilder<CounterCubit, int>(
              bloc: counterCubit,
              builder: (context, counter) {
                return Text(
                  '$counter',
                  style: Theme.of(context).textTheme.headlineMedium,
                );
              },
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => counterCubit.increment(),
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

Bu şekilde düzenledik ve tekrar kontrol ediyoruz sıkıntı yok.

Şimdi bir de değeri azaltalım. Bunu da siz yapın aynı mantık her şey kurulu gidip logic yazacaksınız yazacağınız şey de tek satır resmen hemen yapın.

4JgxsdS.png


Kod:
import 'package:flutter_bloc/flutter_bloc.dart';

class CounterCubit extends Cubit<int> {
  CounterCubit() : super(0); //Turkhackteam.org

  void increment() {
    emit(state + 1);
  }

  void decrement() {
    emit(state - 1);
  }
}

- spamladığınızda 0'ın altına inmesin ama + spamlandığında sonsuza kadar gidebilsin diye bir logic yapmak istesek nasıl yapardık?

VvfnX.png


burada aslında mantık karmaşık gözükebilen logiclerin cubitlerin içerisinde olması ve UI yazdığımız yerlere fonksiyonları ve logicleri karmaşık bir halde olmamasını temiz kod yazmayı sağlıyor aslında. emit'i de cubit dışarısında kullanamadığımız için bir nevi koruma sağlıyor.

Ufak bir mola:


Biraz daha düzeltmek için home_page.dart dosyasındaki floatingActionButton kısmını komple kesip lib içerisinde inc_dec_page.dart diyerek başka bir dart dosyası oluşturup stles bir widget oluşturup return Scaffold deyip ardından yapıştırıyoruz.

Kod:
floatingActionButton: Column(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          FloatingActionButton(
            onPressed: () => counterCubit.increment(),
            tooltip: 'Increment',
            child: const Icon(Icons.add),
          ),
          FloatingActionButton(
            onPressed: () => counterCubit.decrement(),
            tooltip: 'Decrement',
            child: const Icon(Icons.minimize),
          ),
        ],
      ),

yani şunu yapmış olduk:

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: Column(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          FloatingActionButton(
            onPressed: () => counterCubit.increment(),
            tooltip: 'Increment',
            child: const Icon(Icons.add),
          ),
          FloatingActionButton(
            onPressed: () => counterCubit.decrement(),
            tooltip: 'Decrement',
            child: const Icon(Icons.minimize),
          ),
        ],
      ),
    );
  }
}

şöyle yapmış olduk yani parçalama mantığı. ama counterCubit kısımları hata veriyor onlara nasıl erişeceğiz. Yeniden CounterCubit().increment diyerek kullanırsak çalışmaz çünkü tekrar instance oluşturmuş olacağız bir mantığı olmayacak. Bu sorunu da çözmemiz lazım. Bunu çözmek için Provider kullanabiliriz.

Bloc zaten kendi içinde Provider'ı kullanır yani bize getirir. Provider kullanarak CounterCubit'imizin bir instance ını oluşturup bu instance'a her yerden erişebiliriz.

main.dartımızdaki MaterialApp'i BlocProivder ile sarmalarsak ve bloc'larımızı verirsek onların instancelarına erişebiliriz.


Kod:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:weather/cubit/counter_cubit.dart';
import 'package:weather/home_page.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_)=> CounterCubit(),
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
          useMaterial3: true,
        ),
        home: const MyHomePage(title: 'Flutter Demo Home Page'),
      ),
    );
  }
}

burada BlocProvider ile sarmaladık ve bloc'umuzu verdik. _ placeholder görevi görüyor. Özetle bir fonksiyon var ve bu CounterCubit()'i veriyor gibi düşünelim.

BlocProvider'ın Provider'dan farkı ne ki ben anlamadım diyorsanız BlocProvider'da bloc verebiliyorsunuz onların da StateStreamable dan geliyor olması gerek. Provider'da ise sınıf verebiliyoruz böyle bir şart olmadan.

Şimdi home_page.dart'a geri dönüyoruz ve orada counterCubit olarak final diye tanımlamıştık onu siliyoruz ve Providerımızdan erişeceğiz artık oradaki de sonuçta aynı cubitimizin instance ı.

Kod:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:weather/cubit/counter_cubit.dart';
import 'package:weather/inc_dec_page.dart';

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  Widget build(BuildContext context) {
    final counterCubit = BlocProvider.of<CounterCubit>(context);
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            BlocBuilder<CounterCubit, int>(
              bloc: counterCubit,
              builder: (context, counter) {
                return Text(
                  '$counter',
                  style: Theme.of(context).textTheme.headlineMedium,
                );
              },
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
          onPressed: () {
            Navigator.of(context).push(MaterialPageRoute(
              builder: (context) => const IncDecPage(),
            ));
          },
          child: const Icon(Icons.navigate_next)),
    );
  }
}

Bir de floatingActionButton olarak IncDecPage() sayfasına geçiş yaptırtıyoruz deneyeceğiz yani
IncDecPage yani inc_dec_page.dart dosyamızda ise

Kod:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:weather/cubit/counter_cubit.dart';

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

  @override
  Widget build(BuildContext context) {
    final counterCubit = BlocProvider.of<CounterCubit>(context);
    return Scaffold(
      floatingActionButton: Column(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          FloatingActionButton(
            onPressed: () => counterCubit.increment(),
            tooltip: 'Increment',
            child: const Icon(Icons.add),
          ),
          FloatingActionButton(
            onPressed: () => counterCubit.decrement(),
            tooltip: 'Decrement',
            child: const Icon(Icons.minimize),
          ),
        ],
      ),
    );
  }
}

Provider kullanırken hatırlıyoruz build metodu içerisinden kullanıyorduk burada da aynı mantık final counterCubit diyerek cubitimizin tek instance ına erişebiliyoruz her yerdn providerı kullanarak. Burada yaptığımız şey şu BlocProvider.of diyerek yapıyoruz aynı kullanım sadece BlocProvider yazmamız yeterli. <> içerisinde belirtiyoruz sınıfımız CounterCubit zaten. Böylece artık bu dosyada counterCubit diyerek aynı instance'a erişebileceğiz. Ve cubitimizdeki logicleri çalıştırdığımızda bu cubiti dinleyenler aynı instance'ı dinleyecekleri için değişiklikler farklı sayfada olan bir değişiklik başka bir sayfada bulunan text widgdetındaki counter'ı değiştirebilecek.

EpKf5IOdjH.png
SnigW0b7.png



eFwAM4WXhu.png


DİĞER YAKLAŞIMA BAKALIM: BLOC YAKLAŞIMI
lib klasörü içerisinde bloc diye bir dosya oluşturalım. Cubit değilde direkt olarak Bloc kullanmak. bloc dosyası içerisinde counter_bloc.dart diye bir dosya oluşturuyoruz. Cubit kodlarımız dosyamız kalsın bu bir diğer yaklaşım yani üstteki kısım ayrı bu kısım ayrı gibi düşünün. İki farklı yöntem gibi hangisini tercih ederseniz kullanın. Cubit kullanacağım diyorsanız yukarıya gidin hayır benim sınıflarım Bloc tan extends olacak bu yaklaşımı tercih ediyorum diyorsanız buradan devam.

Kod:
import 'package:flutter_bloc/flutter_bloc.dart';

class CounterBloc extends Bloc {
  CounterBloc() : super(0);
}

Bloc tan extends ettiğinizde 2 şey ister <> içerisinde. Biz normalde cubit kullandığımızda yöneteceğimiz veri türünü verip geçiyorduk. Bloc'tan extends ettiğimizde ise yani bu yaklaşımda Event, State bekler.

Event nedir?
Mesela bir butona basılacaksa bu bir aksiyondur ve aksiyon event demektir gibi düşünebiliriz. Blocta eventi oluşturup ve bu eventlere yani aksiyonlara göre stateleri halledeceğimiz eventHandler'lar oluşturarak stateleri update edebiliyoruz. Bu kısım biraz karışık gelmiş olabilir uygularken anlaşılma ihtimali daha yüksek çünkü ben de okuduğumda epey karışık duruyor.

lotr3_movie_screencaps.com_20354.jpg


Ne oluyor!? ne eventi ne handlerı ne güzel cubitlerle yapıyorduk derken başımıza bunlar çıktı. Devam edelim

bloc_flow.png


Tam okunmuyor fakat mühim değil maksat şema.

Kod:
import 'package:flutter_bloc/flutter_bloc.dart';

class CounterIncremented{
  //bu bir event/action
 
}

class CounterBloc extends Bloc<CounterIncremented, int> {
  CounterBloc() : super(0);
}

bu şekilde kodladık counter_bloc.dart dosyamızı şimdi main.dart dosyamıza gidelim cubitimizi falan kaldırmak istemiyoruz şimdilik o yüzden MaterialApp'i yine bir BlocProvider ile sarmalayalım ve CounterBloc() verelim bu sefer. Şimdi böyle oldu

Kod:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:weather/bloc/counter_bloc.dart';
import 'package:weather/cubit/counter_cubit.dart';
import 'package:weather/home_page.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_)=> CounterCubit(),
      child: BlocProvider(
        create: (_)=> CounterBloc(),
        child: MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
            useMaterial3: true,
          ),
          home: const MyHomePage(title: 'Flutter Demo Home Page'),
        ),
      ),
    );
  }
}

ama sorun şu ki böyle iç içe çok fazla olsa ne olacak karmaşık bir yapı işimize gelmez. Bu nedenle MultiBlocProvider kullanabiliriz.

Kod:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:weather/bloc/counter_bloc.dart';
import 'package:weather/cubit/counter_cubit.dart';
import 'package:weather/home_page.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MultiBlocProvider(
      providers: [
        BlocProvider(create: (_)=>CounterCubit()),
        BlocProvider(create: (_)=>CounterBloc()),
      ],
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
          useMaterial3: true,
        ),
        home: const MyHomePage(title: 'Flutter Demo Home Page'),
      ),
    );
  }
}

Diyerek tek seferde birden fazla provider verebiliyoruz bu şekilde. Şimdi kullanımı yaparken yine aynı hatta daha kolay

home_page.dart ' a bakalım

Kod:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:weather/bloc/counter_bloc.dart';
import 'package:weather/cubit/counter_cubit.dart';
import 'package:weather/inc_dec_page.dart';

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  Widget build(BuildContext context) {
    final counterCubit = BlocProvider.of<CounterCubit>(context);
    final counterBloc = BlocProvider.of<CounterBloc>(context);
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            BlocBuilder<CounterBloc, int>(
              bloc: counterBloc,
              builder: (context, counter) {
                return Text(
                  '$counter',
                  style: Theme.of(context).textTheme.headlineMedium,
                );
              },
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
          onPressed: () {
            Navigator.of(context).push(MaterialPageRoute(
              builder: (context) => const IncDecPage(),
            ));
          },
          child: const Icon(Icons.navigate_next)),
    );
  }
}

Burada kullanırken bloc: diyerek counterBloc diye belirtmesek bile MultiBlocProvider'a otomatik olarak gider ve CounterBloc kullananı bulur kendisi yani yine çalışır. Bulunca Instance ını alır kullanır. Aynı şekilde cubit kullanımında da belirtmeseniz de olur. Durum böyle olunca başta final counterBloc ve counterCubit diye tanımladığımız şeylere bile ihtiyacımız kalmıyor.

Kod:
import 'package:flutter_bloc/flutter_bloc.dart';

class CounterIncremented {
  //bu bir event/action
}

class CounterBloc extends Bloc<CounterIncremented, int> {
  CounterBloc() : super(0){ //default deger 0 olacak
    //constructor icerisinde
    on<CounterIncremented>((event, emit){ //bu bizim event handlerımız yani aksiyon handlerımız
      //emiti bu handlerların disinda kullanamayiz blocta.
      emit(state+1);
    });
  }
}

Şimdi aksiyonumuzu oluşturduk ve CounterBloc'u yazdık. Default değerini verdik ve oluşturduğumuz aksiyonun yani CounterIncremented'ın handlerını yazdık. Bunu Constructora default değerini verdikten sonra scope içerisinde on diyerek yapabiliyoruz aksiyonu belirtiyoruz yani o aksiyonun logicini yazacağız gibi düşünün. logici bu şekilde yazarak emit edebiliyoruz. state+1 diye emit ettik.

Kullanacağımız zaman da

Kod:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:weather/bloc/counter_bloc.dart';
import 'package:weather/cubit/counter_cubit.dart';

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

  @override
  Widget build(BuildContext context) {
    //final counterCubit = BlocProvider.of<CounterCubit>(context);
    final counterBloc = BlocProvider.of<CounterBloc>(context);
    return Scaffold(
      floatingActionButton: Column(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          FloatingActionButton(
            onPressed: () => counterBloc.add(CounterIncremented()),
            tooltip: 'Increment',
            child: const Icon(Icons.add),
          ),
          FloatingActionButton(
            onPressed: () {}, //counterCubit.decrement(),
            tooltip: 'Decrement',
            child: const Icon(Icons.minimize),
          ),
        ],
      ),
    );
  }
}

bu şekilde kullanabiliyoruz. add diyerek aksiyonu belirtiyoruz.

Şimdi bir diğer aksiyon olan azaltma işlemini yazmayı deneyin kendiniz. Devam edelim

fSMofx.png


bizim başka aksiyonlarımız da olabilir bu nedenle böyle bir senaryoda CounterBloc extends <> diyerek buraya tek bir şey vermek yerine

abstract bir class oluşturuyoruz veya sealed (eğer sealed class oluşturursanız örneğin switch case aşağıdaki gibi bir kullanım yaptığınızda bütün ihtimalleri
girmeniz gerekir yani bütün aksiyonları ele almanız gerekir öyle bir zorunluluk oluşturmak istemiyorsanız abstract class da kullanabilirsiniz ama iyidir sealed.

yani artık CounterIncremented kullanmak yerine CounterEvent yazacağız böylece birden fazla aksiyonu handle edebileceğiz. Aksiyon classlarımızın başına final atabiliriz. Bunların dışında extended olmaz bu da işimize gelebilir.

Kod:
import 'package:flutter_bloc/flutter_bloc.dart';

sealed class CounterEvent {}

final class CounterIncremented extends CounterEvent {
  //bu bir event/action
}

final class CounterDecremented extends CounterEvent {
  //bu da bir event/action
}

class CounterBloc extends Bloc<CounterEvent, int> {
  CounterBloc() : super(0) {
    //default deger 0 olacak
    //constructor icerisinde
    on<CounterIncremented>((event, emit) {
      //bu bizim event handlerımız yani aksiyon handlerımız
      //emiti bu handlerların disinda kullanamayiz blocta.
      emit(state + 1);
    });

    on<CounterDecremented>((event, emit) {
      emit(state - 1);
    });
  }
}
Bu şekilde düzenledik. 20-30 aksiyon olabilir öyle düşünün. Bu arada performans kalemi ve proje klasörleme yapısı açısından counter_event.dart diye bir dosya oluşturup bu aksiyon sınıflarını oraya atmak daha mantıklı olur.

Kod:
sealed class CounterEvent {}

final class CounterIncremented extends CounterEvent {
  //bu bir event/action
}

final class CounterDecremented extends CounterEvent {
  //bu da bir event/action
}

böyle verdik buraya ve counter_bloc.dart'tan kaldırdık ve sadece importu etmemiz yeterli artık.
Yalnız yine import etmek yerine tek bir seymis gibi kullanabilmek icin part part of kullanımı yapmak hoş olur.

counter_actions.dart dosyasına ki bu dosyaya counter_event.dart da diyebilirsiniz ben sonradan actions diye değiştirdim aynı şey. part of 'counter_bloc.dart' ekleyerek
benzer şekilde counter_actions.dart dosyasına da
part 'counter_actions.dart'; yazabilirsiniz. single unit yani aynı kutuphaneymis gibi is gorecek artik.

kullanalım ve test edelim:

onPressed: () => counterBloc.add(CounterDecremented()),

eklemesini yapmayı unutmayın yoksa azaldığını göremezsiniz. Bu arada emit fonksiyonu Bloc kullanımında eventHandlerlar dışında erişilemezken Cubitte bütün fonksiyonlar erişebiliyor.

Bu konuda Bloc kullanımının temelindeki iki farklı yaklaşımdan örneklerle bahsetmeye çalıştım. Yardımcı olabildiysem ne mutlu.


<3 Gauloran

eFwAM4WXhu.png
 
Üst

Turkhackteam.org internet sitesi 5651 sayılı kanun’un 2. maddesinin 1. fıkrasının m) bendi ile aynı kanunun 5. maddesi kapsamında "Yer Sağlayıcı" konumundadır. İçerikler ön onay olmaksızın tamamen kullanıcılar tarafından oluşturulmaktadır. Turkhackteam.org; Yer sağlayıcı olarak, kullanıcılar tarafından oluşturulan içeriği ya da hukuka aykırı paylaşımı kontrol etmekle ya da araştırmakla yükümlü değildir. Türkhackteam saldırı timleri Türk sitelerine hiçbir zararlı faaliyette bulunmaz. Türkhackteam üyelerinin yaptığı bireysel hack faaliyetlerinden Türkhackteam sorumlu değildir. Sitelerinize Türkhackteam ismi kullanılarak hack faaliyetinde bulunulursa, site-sunucu erişim loglarından bu faaliyeti gerçekleştiren ip adresini tespit edip diğer kanıtlarla birlikte savcılığa suç duyurusunda bulununuz.