Sparround

Rebuild performansı və ölçmə

State management-in performans təsiri bir sualla ölçülür: bir dəyişiklik nə qədər widget-i oyadır? Rəsmi performans tövsiyələri bu istiqamətdə konkretdir:

  • Widget-lərdə mümkün olduqca `const` konstruktorlardan istifadə edin — bu, Flutter-in rebuild işinin böyük hissəsini qısa yolla keçməsinə imkan verir. flutter_lints paketi bunu avtomatik xatırladır.
  • `build()` metodlarında təkrarlanan və bahalı işdən çəkinin, çünki ata widget-lər yenidən qurulduqda `build()` tez-tez çağırıla bilər.
  • Böyük `build()` funksiyası olan həddindən artıq iri widget-lərdən çəkinin; onları həm enkapsulyasiyaya, həm də necə dəyişdiklərinə görə bölün. Sənəd əlavə edir: setState() çağırıldıqda bütün alt ağac yenidən qurulur, ona görə çağırışı dəyişən hissəyə yaxın saxlamaq lazımdır.
  • Təkrar istifadə olunan UI hissələri üçün funksiya yerinə `StatelessWidget` seçin.
  • Böyük siyahı və grid-lərdə lazy builder metodlarından istifadə edin — yalnız ekranda görünən hissə qurulur.
LibraryRebuild sahəsini daraltmaq üçün alət
Flutter (library-siz)`setState`-i aşağı çəkmək, `ValueListenableBuilder`, `const`, `ListenableBuilder`
Provider`Consumer` (`child` parametri ilə), `Selector`, `context.select`
Riverpod`Consumer` widget-i, `provider.select(...)`, ayrı-ayrı kiçik provider-lər
BLoC`BlocSelector`, `buildWhen`, `BlocBuilder`-i dərinə yerləşdirmək

Ölçmə: təxminlə optimizasiya etməyin. DevTools-un Performance görünüşü rebuild-ləri tapmaq üçün konkret seçimlər verir:

  • Track Widget Buildsbuild() metod hadisələrini timeline-da göstərir; hadisənin adında widget-in adı olur.
  • Enhance tracing — daha detallı izləmə seçimləri; sənəd xəbərdarlıq edir ki, bu seçimlər aktiv olduqda frame vaxtları mənfi təsirlənə bilər.
  • Track LayoutsTrack Paints — render obyektlərinin layout və paint hadisələri.

Müsahibədə ardıcıllığı belə ifadə etmək güclüdür: əvvəlcə ölç, sonra rebuild sahəsini darald, sonra yenidən ölç. Optimizasiya iddiası rəqəmlə müşayiət olunmursa, o, təxmindir.

dart
// ❌ Bütün ekran hər dəyişiklikdə qurulur:
// - watch build-in yuxarısındadır
// - ağır widget-lər eyni metodun içindədir
// - const yoxdur
class DashboardBad extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final model = context.watch<DashboardModel>();
    return Column(
      children: [
        HeavyChart(data: model.chartData),
        ExpensiveMap(),
        Text('Bildiriş: ${model.unreadCount}'),
      ],
    );
  }
}

// ✅ Yalnız dəyişən hissə qurulur:
// - ağır widget-lər const və ya kənarda
// - abunəlik yalnız lazım olan sahəyə
class DashboardGood extends StatelessWidget {
  const DashboardGood({super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // Yalnız chartData dəyişdikdə qurulur.
        Selector<DashboardModel, List<Point>>(
          selector: (_, m) => m.chartData,
          builder: (_, data, __) => HeavyChart(data: data),
        ),
        const ExpensiveMap(), // heç vaxt rebuild olunmur
        Selector<DashboardModel, int>(
          selector: (_, m) => m.unreadCount,
          builder: (_, count, __) => Text('Bildiriş: $count'),
        ),
      ],
    );
  }
}

Eyni ekran, iki rebuild profili.

Ən çox rastlanan performans səhvi state management-də deyil, abunəliyin çox yuxarıda olmasındadır: build-in birinci sətrində bütün modeli watch etmək ekranın hər hissəsini bir dəyişikliyə bağlayır. Bu, üç library-də də eyni səhvdir və həlli də eynidir — abunəliyi ağacda aşağı çəkmək (Consumer, Selector, BlocSelector, Riverpod-da kiçik Consumer widget-ləri).

📚 Mənbələr və sənədlər