Flutter & Dart widget tree, element tree, render tree #18

Gauloran

Global Moderatör
7 Tem 2013
8,130
621
Seriye devam ediyoruz. Önceki konulara en alttan ulaşabilirsiniz. En son debugging mantığından bahsetmiştik. Bu konuda ise bazı önemli kavramlara değinmek istiyorum. Widget tree'den de bahsetmiştik ancak yine bir üstünden geçmekte fayda var. Widget tree projemizde kullandığımız widgetların kombinasyonu dediğimiz bir widgetlar bütünü. Flutter'da her widget tree için bir element tree oluşturulur. Bu element tree'ye widget tree'nin hafızada temsil edilen hali diyebiliriz. Elementler eğer uygunsa tekrar kullanılır çünkü UI güncellemesi gerekli olduğu zaman kullanılırlar. Bu zamana kadar seriye devam eden arkadaşlar tahmin edecektir ki build metodu çok sık çağrılabilir.

Widget, widget , widget = Widget Tree

Widget Tree ------> Element Tree (element tree oluşturulur)
widget -> element
widget -> element


ZsduM7.png


Bir de Render Tree işin içine girer o da görünür UI blokları diyebiliriz. Render Tree'yi oluşturup update etmek büyük iş olduğu için performansa çok etki eder bu nedenle Flutter Render Tree'ye çok dokunmayı tercih etmez. UI update process'i şu şekilde ilerler:

build() metodu kullanıldı

(flutter element tree'yi kontrol ediyor ve yeniden kullanıyor zaten olanları. daha sonra yeni element tree ile eski element tree'yi karşılaştırıyor.)

new expected UI <----> Actual UI

eğer herhangi bir fark varsa bu farklılıklar Render Tree'ye uygulanıyor ve UI update ediliyor. tabii flutter bütün ekranı ufak bir widgetta olan güncelleme için yeniden renderlamıyor. partially re-rendered denilir buna. yani update edilmesi gereken kısımları update ediyor. bizim developer olarak render tree'yi ya da element tree'yi dert etmemize gerek yok flutter bizim için yapıyor zaten ancak mantığını bilmek ve konsepti anlamak önde gelir.

şimdi yeni bir flutter projesi oluşturalım main.dart içeriğini silin ve aşağıdaki kodları ekleyin:

Kod:
import 'package:flutter/material.dart';[/FONT][/CENTER][/FONT][/CENTER][/FONT][/CENTER]
[FONT=verdana][CENTER][FONT=verdana][CENTER][FONT=verdana][CENTER]import 'package:flutter_application_2/ui_updated_demo.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(useMaterial3: true),
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Flutter Internals'),
        ),
        body: const UIUpdatesDemo(),
      ),
    );
  }
}




daha sonra ui_updated_demo.dart dosyası oluşturun lib klasörü içerisinde main.dart'ın yanına. ve içeriğine aşağıdaki kodları ekleyin:

Kod:
import 'package:flutter/material.dart';[/FONT][/CENTER][/FONT][/CENTER][/FONT][/CENTER]
[FONT=verdana][CENTER][FONT=verdana][CENTER][FONT=verdana][CENTER]
class UIUpdatesDemo extends StatefulWidget {
  const UIUpdatesDemo({super.key});

  @override
  StatefulElement createElement() {
    print('UIUpdatesDemo CREATEELEMENT called');
    return super.createElement();
  }

  @override
  State<UIUpdatesDemo> createState() {
    return _UIUpdatesDemo();
  }
}

class _UIUpdatesDemo extends State<UIUpdatesDemo> {
  var _isUnderstood = false;

  @override
  Widget build(BuildContext context) {
    print('UIUpdatesDemo BUILD called');
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            const Text(
              'Every Flutter developer should have a basic understanding of Flutter\'s internals!',
              textAlign: TextAlign.center,
              style: TextStyle(fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 16),
            const Text(
              'Do you understand how Flutter updates UIs?',
              textAlign: TextAlign.center,
            ),
            const SizedBox(height: 24),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                TextButton(
                  onPressed: () {
                    setState(() {
                      _isUnderstood = false;
                    });
                  },
                  child: const Text('No'),
                ),
                TextButton(
                  onPressed: () {
                    setState(() {
                      _isUnderstood = true;
                    });
                  },
                  child: const Text('Yes'),
                ),
              ],
            ),
            if (_isUnderstood) const Text('Awesome!'),
          ],
        ),
      ),
    );
  }
}




ve projeyi emulatörünüzde başlatın. başlattığınızda basit bir ekran karşınıza çıkacak DEBUG CONSOLE'u açıp kontrol edin iki şey dikkatinizi çekecek.

Kod:
UIUpdatesDemo CREATEELEMENT called[/FONT][/CENTER][/FONT][/CENTER][/FONT][/CENTER]
[FONT=verdana][CENTER][FONT=verdana][CENTER][FONT=verdana][CENTER]UIUpdatesDemo BUILD called




Bu logların gelme sebebi ui_updates_demo.dart dosyası içerisinde build metodunun içine hemen altına bir print atmışız BUILD called oradan geliyor. ELEMENT called kısmı ise UIUpdatesDemo extends StatefulWidget kısmındaki createElement() metodunun içerisine print bıraktığımız için oradan geliyor. Konunun başında bahsettiğim element widget falan diye işte onlar bunlar aslında. Şu anda biraz aydınlanmış olmanız gerekli aydınlanmadıysanız takmadan devam edin. Log kısmında görüldüğü üzere CREATEELEMENT buildden önce çağrılıyor daha sonra build içerisinde biz widgetları veriyoruz vs. yani element objectinin güncellenmesi gerek daha sonra update edildiğini anlıyoruz. yani uygulama başlatıldığında ui renderlanıyor ilk kez işte bu bahsettiğim element update olurken render tree yaratılıyor yani. Daha sonrasını yukarıda anlatmıştım. Bu projede emulatörünüz açıksa hala No butonuna tıklayın ve UI da hiçbir değişikliğin olmadığını göreceksiniz. Ama DEBUG CONSOLE'dan loglara bakarsanız Build metodunun No butonuna tıklayınca tekrar çalıştığını göreceksiniz. ama nasıl? no butonuna gittiğinizde onPressed kısmında setState metodunun kullanıldığını görüyoruz. olan şeyler:

No butonuna tıklandı
setState kullanıldığı için Build metodu tekrar çağrıldı
Flutter hiçbir elementin değişmediğini fark etti yeni element tree ile eski element tree arasında
sonuç: UI da bir update olmadı

Yes butonuna tıklandı
kodumuzda bir if yapısı var o kod çalışacağından Awesome text widgetı gösterilecek yani
build metodu çalıştı setState kullanıldığı için
Flutter karşılaştırma yapıyor eski widget tree ile yeni widget tree arasında
createElement çalışmadı çünkü zaten vardı içten içe text widgetı için bir element oluşturacak onu da loglamıyoruz bu projede
Flutter Render Tree ye gidiyor ve sadece Text Widgetının ilgili olduğu yeri update ediyor. Bütün her şeyi re-rendered etmez. Awesome texti bu şekilde gösteriliyor.

ama yine de yukarıda sizlere vermiş olduğum kodlarda optimize edilebilir birçok şey mevcut. sonuç olarak flutter build metodu zırt pırt çalıştığı için flutter her seferinde kontrol ediyor update edilmesi gereken ne var falan diye. bütün widgetları kontrol etmeli yani. ondan dolayı basit bir app'te sıkıntı olmaz ama karmaşık bir app geliştirirken sıkıntı çıkar. kodu şöyle biraz daha geliştirebiliriz:

ZsdP5n.png


demo_buttons.dart diye bir dosya oluşturuyoruz dart dosyamıza, amacımız şu ui_updated_demo.dart dosyasındaki if ile başlayan Awesome text widgetını ve onun üstündeki butonları içeren Row widgetını o dosyadan kırpıp demo_buttons.dart dosyamızda onları ayrı bir widget haline getireceğiz.

demo_buttons.dart içerisine yazmaya başlıyoruz:

Kod:
import 'package:flutter/material.dart';[/FONT][/CENTER][/FONT][/CENTER][/FONT][/CENTER]
[FONT=verdana][CENTER][FONT=verdana][CENTER][FONT=verdana][CENTER]
class DemoButtons extends StatefulWidget {
  const DemoButtons({super.key});

  @override
  State<DemoButtons> createState() {
    return _DemoButtonsState();
  }
}

class _DemoButtonsState extends State<DemoButtons> {
  var _isUnderstood = false;

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            TextButton(
              onPressed: () {
                setState(() {
                  _isUnderstood = false;
                });
              },
              child: const Text('No'),
            ),
            TextButton(
              onPressed: () {
                setState(() {
                  _isUnderstood = true;
                });
              },
              child: const Text('Yes'),
            ),
          ],
        ),
        if (_isUnderstood) const Text('Awesome!'),
      ],
    );
  }
}




şimdi ui_updated_demo.dart'a gidip onu düzenleyebiliriz:

Kod:
import 'package:flutter/material.dart';[/FONT][/CENTER][/FONT][/CENTER][/FONT][/CENTER]
[FONT=verdana][CENTER][FONT=verdana][CENTER][FONT=verdana][CENTER]import 'package:flutter_application_2/demo_buttons.dart';

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

  @override
  StatefulElement createElement() {
    print('UIUpdatesDemo CREATEELEMENT called');
    return super.createElement();
  }

  @override
  State<UIUpdatesDemo> createState() {
    return _UIUpdatesDemo();
  }
}

class _UIUpdatesDemo extends State<UIUpdatesDemo> {
  var _isUnderstood = false;

  @override
  Widget build(BuildContext context) {
    print('UIUpdatesDemo BUILD called');
    return const Padding(
      padding: EdgeInsets.all(8.0),
      child: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            Text(
              'Every Flutter developer should have a basic understanding of Flutter\'s internals!',
              textAlign: TextAlign.center,
              style: TextStyle(fontWeight: FontWeight.bold),
            ),
            SizedBox(height: 16),
            Text(
              'Do you understand how Flutter updates UIs?',
              textAlign: TextAlign.center,
            ),
            SizedBox(height: 24),
            DemoButtons(),
          ],
        ),
      ),
    );
  }
}




bu şekilde düzenledik. şimdi ui_updates_demo.dart dosyasında artık statefulwidget kullanmamıza gerek yok statelesswidget a çevirelim onu:

Kod:
import 'package:flutter/material.dart';[/FONT][/CENTER][/FONT][/CENTER][/FONT][/CENTER]
[FONT=verdana][CENTER][FONT=verdana][CENTER][FONT=verdana][CENTER]import 'package:flutter_application_2/demo_buttons.dart';

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

  @override
  Widget build(BuildContext context) {
    print('UIUpdatesDemo BUILD called');
    return const Padding(
      padding: EdgeInsets.all(8.0),
      child: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            Text(
              'Every Flutter developer should have a basic understanding of Flutter\'s internals!',
              textAlign: TextAlign.center,
              style: TextStyle(fontWeight: FontWeight.bold),
            ),
            SizedBox(height: 16),
            Text(
              'Do you understand how Flutter updates UIs?',
              textAlign: TextAlign.center,
            ),
            SizedBox(height: 24),
            DemoButtons(),
          ],
        ),
      ),
    );
  }
}




build metodu tetiklendiğinde bir print atalım demo_buttons.dart dosyasına ilgili kısmına gidin ve atın:

Kod:
Widget build(BuildContext context) {[/FONT][/CENTER][/FONT][/CENTER][/FONT][/CENTER]
[FONT=verdana][CENTER][FONT=verdana][CENTER][FONT=verdana][CENTER]    print('DemoButtons BUILD called');




şimdi projeyi bir yeniden başlatın emülatörde. DEBUG CONSOLE açıp kontrol edin.

Kod:
I/flutter ( 4762): UIUpdatesDemo BUILD called[/FONT][/CENTER][/FONT][/CENTER][/FONT][/CENTER]
[FONT=verdana][CENTER][FONT=verdana][CENTER][FONT=verdana][CENTER]I/flutter ( 4762): DemoButtons BUILD called




uygulama başlatıldığında bunları aldık gayet normal beklediğimiz gibi. şimdi yes butonuna bastığınızda

Kod:
I/flutter ( 4762): DemoButtons BUILD called

aldığınızı göreceksiniz. fark ettiyseniz artık ui_updated_demo.dart ın BUILD metodu tetiklenmedi ve böylece ui_updated_demo.dart dosyası içerisindeki widgetları flutterın acaba bu yes butonuna tıklandığında neler değişti diye kontrol etmesine artık gerek YOK. şimdi bunu büyük bir projede düşünün onlarca gereksiz widgetı flutterın kontrol etmesi gerekeceğinden performans sıkıntıları çıkardı. burada anlatmaya çalıştığım konsept ile bu sıkıntılardan kurtulmuş oluyorsunuz.

Okuduğunuz için teşekkürler bir sonraki konuda görüşmek üzere
Gauloran <3


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
 

Gauloran

Global Moderatör
7 Tem 2013
8,130
621
yorumlar icin tesekkurler. bir sonraki konuda farkli onemli kavramlara deginecegim.
 
Ü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.