Flutter Consumer Widget 💫 🌌 ✨
Tags: flutter, dart, consumer, mobile
Published: 2022-03-20 19:28:19

**Consumer widget'ın iki ana amacı vardır:**
BuildContext'imiz olmadığında ve bu nedenle Provider.of'u kullanamadığımızda provider'dan bir değer alınmasına izin verir.

Bu senaryo genellikle, aşağıdaki örnekte olduğu gibi, provider'ı oluşturan widget öğesi aynı zamanda consumer'lardan biri olduğunda gerçekleşir:

```
@override
Widget build(BuildContext context) {
  return ChangeNotifierProvider(
    create: (_) => Foo(),
    child: Text(Provider.of<Foo>(context).value),
  );
}
```
Provider.of, provider'ının atası olan bir BuildContext ile çağrıldığından, bu örnek bir ProviderNotFoundException oluşturacaktır.

Bunun yerine, Provider.of'u kendi BuildContext'i ile çağıracak olan Consumer widget öğesini kullanabiliriz.

Consumer kullanarak önceki örneği şöyle yazabiliriz:

```
@override
Widget build(BuildContext context) {
  return ChangeNotifierProvider(
    create: (_) => Foo(),
    child: Consumer<Foo>(
      builder: (_, foo, __) => Text(foo.value),
    },
  );
}
```
Bu, ProviderNotFoundException oluşturmaz ve Text'i doğru şekilde oluşturur. Ayrıca, foo değeri değiştiğinde Text'i de günceller.

_Bu durum daha ayrıntılı yeniden oluşturmalar sağlayarak performans optimizasyonuna yardımcı olur._

Listen: false Provider.of'a geçirilmediği sürece, Provider.of'a geçirilen BuildContext ile ilişkili widget öğesi, elde edilen değer değiştiğinde yeniden oluşturulur. Bu beklenen davranıştır, ancak bazen gerekenden daha fazla widget'ı yeniden oluşturabilir.

Örneğin:

```
 @override
 Widget build(BuildContext context) {
   return FooWidget(
     child: BarWidget(
       bar: Provider.of<Bar>(context),
     ),
   );
 }
```

Yukarıdaki kodda, yalnızca BarWidget Provider.of tarafından döndürülen değere bağlıdır. Ancak Bar değiştiğinde, hem BarWidget hem de FooWidget yeniden oluşturulacaktır.

İdeal olarak, yalnızca BarWidget yeniden oluşturulmalıdır. Bunu başarmak için çözüm `Consumer` kullanmaktır.

Bunu yapmak için, yalnızca provider'a bağlı olan widget öğelerini bir Consumer'a sararız:

```
 @override
 Widget build(BuildContext context) {
   return FooWidget(
     child: Consumer<Bar>(
       builder: (_, bar, __) => BarWidget(bar: bar),
     ),
   );
 }
```
Bu durumda, Bar güncellenirse yalnızca BarWidget yeniden oluşturulur.

Ama ya bir provider'a bağlı olan FooWidget ise? Örneğin:

```
 @override
 Widget build(BuildContext context) {
   return FooWidget(
     foo: Provider.of<Foo>(context),
     child: BarWidget(),
   );
 }
```
Consumer ve isteğe bağlı `child` argümanını kullanarak bu tür bir senaryoyu ele alabiliriz:

```
 @override
 Widget build(BuildContext context) {
   return Consumer<Foo>(
     builder: (_, foo, child) => FooWidget(foo: foo, child: child),
     child: BarWidget(),
   );
 }
```

Bu örnekte, BarWidget builder'ın dışında oluşturulmuştur. Ardından, BarWidget örneği builder'a son parametre olarak iletilir.

Bu, Builder yeni değerlerle yeniden çağrıldığında, yeni bir BarWidget örneğinin oluşturulmayacağı anlamına gelir. Bu da, Flutter'ın BarWidget'ı yeniden oluşturması gerekmediğini bilmesini sağlar. Bu nedenle, böyle bir yapılandırmada, Foo değişirse yalnızca FooWidget yeniden oluşturulur.

**Not:**
Consumer widget'ı, MultiProvider içinde de kullanılabilir. Bunu yapmak için, oluşturucuya iletilen child'ı, oluşturduğu widget ağacında döndürmesi gerekir.

```
MultiProvider(
  providers: [
    Provider(create: (_) => Foo()),
    Consumer<Foo>(
      builder: (context, foo, child) =>
        Provider.value(value: foo.bar, child: child),
    )
  ],
);
```

Consumer widget'ının tek gerekli argümanı builder'dır. 
Builder, ChangeNotifier değiştiğinde çağrılan bir fonksiyondur.
Başka bir deyişle, modelinizde notifyListeners() öğesini çağırdığınızda, karşılık gelen tüm Consumer widget öğelerinin tüm builder methodları çağrılır.

Builder üç argümanla çağrılır. İlki, her derleme yönteminde de aldığınız context'dir.

Builder fonksiyonunun ikinci argümanı, ChangeNotifier örneğidir(instance).

Üçüncü argüman, optimizasyon için orada olan child'dır. Consumer'ınızın altında model değiştiğinde değişmeyen büyük bir widget alt ağacınız varsa, onu bir kez oluşturabilir ve builder'dan (oluşturucudan) geçirebilirsiniz.


```
return Consumer<CartModel>(
  builder: (context, cart, child) => Stack(
    children: [
    // SomeExpensiveWidget'ı her seferinde yeniden oluşturmadan burada kullanın.
      if (child != null) child,
      Text("Total price: ${cart.totalPrice}"),
    ],
  ),
// Pahalı widget'ı burada oluşturun.
  child: const SomeExpensiveWidget(),
);
```

Consumer widget'larınızı ağacın mümkün olduğunca derinlerine yerleştirmek en iyi uygulamadır. Bazı ayrıntılar değişti diye kullanıcı arayüzünün büyük bölümlerini yeniden oluşturmak istemezsiniz.


```
// BUNU YAPMA
return Consumer<CartModel>(
  builder: (context, cart, child) {
    return HumongousWidget(
      // ...
      child: AnotherMonstrousWidget(
        // ...
        child: Text('Total price: ${cart.totalPrice}'),
      ),
    );
  },
);
```

Bunun yerine:

```
// BUNU YAP
return HumongousWidget(
  // ...
  child: AnotherMonstrousWidget(
    // ...
    child: Consumer<CartModel>(
      builder: (context, cart, child) {
        return Text('Total price: ${cart.totalPrice}');
      },
    ),
  ),
);
```


References:
https://docs.flutter.dev/development/data-and-backend/state-mgmt/simple
https://pub.dev/documentation/provider/latest/provider/Consumer-class.html
https://flutterbyexample.com/lesson/finer-build-control-with-selector-1