DEV Community

Cover image for Flutter Consumer Widget 💫 🌌 ✨
Gülsen Keskin
Gülsen Keskin

Posted on

Flutter Consumer Widget 💫 🌌 ✨

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),
  );
}
Enter fullscreen mode Exit fullscreen mode

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),
    },
  );
}
Enter fullscreen mode Exit fullscreen mode

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),
     ),
   );
 }
Enter fullscreen mode Exit fullscreen mode

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),
     ),
   );
 }
Enter fullscreen mode Exit fullscreen mode

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(),
   );
 }
Enter fullscreen mode Exit fullscreen mode

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(),
   );
 }
Enter fullscreen mode Exit fullscreen mode

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),
    )
  ],
);
Enter fullscreen mode Exit fullscreen mode

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(),
);
Enter fullscreen mode Exit fullscreen mode

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}'),
      ),
    );
  },
);
Enter fullscreen mode Exit fullscreen mode

Bunun yerine:

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

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

Top comments (0)