Flutter

InheritedModel, sisi lain dari InheritedWidget

Hallo teman”, jumpa lagi sama mimin. Kali ini kita bakalan bahas terkait Flutter lagi, yaitu tentang InheritedModel. Sebelum membahas lebih lanjut, teman”...

Ikhwan Written by Ikhwan · 2 min read >

Hallo teman”, jumpa lagi sama mimin. Kali ini kita bakalan bahas terkait Flutter lagi, yaitu tentang InheritedModel. Sebelum membahas lebih lanjut, teman” disarankan udah paham tentang InheritedWidget, karena pembahasannya cukup berkaitan. Jika belum, silahkan baca di sini ya.

Kenapa harus menggunakan InheritedModel?

Bagaimana jika kita menggunakan InheritedWidget, lalu kita ingin merubah data? atau melakukan setState( ) ?”

Di pembahasan sebelumnya kita menggunakan schema seperti berikut:

Kondisi: mendapatkan value di Widget A dari Widget C dan Widget D.

Jika kita menggunakan InheritedWidget dan melakukan setState( ) atau memperbarui data, maka Widget B akan ikut di build ulang. Jika misalkan schemanya lebih besar, otomatis ia akan melakukan build ulang untuk semua child widgetnya. Tentu hal ini kurang bagus untuk performa bukan? InheritedModel adalah solusi untuk hal tersebut.

Dengan InheritedModel widget yang melakukan build ulang hanyalah widget yang menggunakan data yang ada di InheritedModel. Jadi kalau berdasarkan schema di atas, maka Widget B tidak akan ikut di build ulang.

Kita akan membuat widget sesuai dengan schema sebelumnya. Berikut codenya:

import 'package:flutter/material.dart';

String textOne = "Text1";
String textTwo = "Text2";

class TextColor extends InheritedModel<String> {
  const TextColor({
    @required this.colorPrimary,
    @required this.colorSecondary,
    @required Widget child,
  })  : assert(colorPrimary != null),
        assert(colorSecondary != null),
        assert(child != null),
        super(child: child);

  final Color colorPrimary;
  final Color colorSecondary;

  static TextColor of(BuildContext context, String aspect) {
    return InheritedModel.inheritFrom<TextColor>(context, aspect: aspect);
  }

  @override
  bool updateShouldNotify(TextColor old) =>
      colorPrimary != old.colorPrimary || colorSecondary != old.colorSecondary;

  @override
  bool updateShouldNotifyDependent(
      TextColor oldWidget, Set<String> dependencies) {
    return (colorPrimary != oldWidget.colorPrimary &&
            dependencies.contains(textOne)) ||
        (colorSecondary != oldWidget.colorSecondary &&
            dependencies.contains(textTwo));
  }
}

class InheritedModelActivity extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => InheritedModelActivityState();
}

class InheritedModelActivityState extends State<InheritedModelActivity> {
  var colorsPrimary = Colors.purple;
  var colorsSecondary = Colors.blue;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Inherited Model"),
      ),
      body: Container(
        margin: EdgeInsets.only(top: 136),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            TextColor(
                colorPrimary: colorsPrimary,
                colorSecondary: colorsSecondary,
                child: const WidgetB()),
            GestureDetector(
              onTap: () {
                setState(() {
                  print("YukNgoding change colorPrimary");
                  colorsPrimary = Colors.red;
                });
              },
              child: Container(
                alignment: Alignment.center,
                margin: EdgeInsets.only(top: 72, bottom: 12),
                padding: EdgeInsets.all(12),
                child: Text("Refresh Color Primary"),
              ),
            ),
            GestureDetector(
              onTap: () {
                setState(() {
                  print("YukNgoding change colorSecondary");
                  colorsSecondary = Colors.green;
                });
              },
              child: Container(
                alignment: Alignment.center,
                padding: EdgeInsets.all(12),
                child: Text("Refresh Color Secondary"),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class WidgetB extends StatelessWidget {
  const WidgetB();

  @override
  Widget build(BuildContext context) {
    print("YukNgoding build WidgetB");
    final textColor = TextColor.of(context, textOne);

    return Column(
      children: [
        Text(
          "Widget B",
          style: TextStyle(color: textColor.colorPrimary),
        ),
        WidgetC()
      ],
    );
  }
}

class WidgetC extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print("YukNgoding build WidgetC");
    final textColor = TextColor.of(context, textTwo);

    return Column(
      children: [
        Text(
          "Widget C",
          style: TextStyle(color: textColor.colorSecondary),
        ),
        WidgetD()
      ],
    );
  }
}

class WidgetD extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print("YukNgoding build WidgetD");
    final textColor = TextColor.of(context, textTwo);

    return Column(
      children: [
        Text(
          "Widget D",
          style: TextStyle(color: textColor.colorSecondary),
        ),
      ],
    );
  }
}

Case 1

Jika kita melakukan click pada Widget “Refresh Color Secondary” kita akan mendapat log dari hasil code print seperti berikut:

Hasil click pertama

Seperti yang tertera pada gambar, Widget B tidak di build ulang. Hal tersebut terjadi karena Widget B tidak menggunakan colorsSecondary. Yay, kita mendapatkan manfaat dari penggunaan InheritedModel, yaitu “Widget akan dibuild ulang jika widget tersebut menggunakan nilai yang berasal dari InheritedModel”

Bagaimana kalau kita coba click Widget “Refresh Color Secondary” sekali lagi?

Hasil click kedua

Oke ketika melihat log, kita mendapatkan kesimpulan kedua yaitu “Jika nilai yang digunakan sama, maka widget tidak akan dibuild ulang”. Hal ini bisa terjadi karena logic yang kita gunakan pada method updateShouldNotifyDependent menghasilkan false. Kita tentu bisa saja menggunakan logic lain, dan memaksa widget dibuild ulang meskipun nilainya tetap sama. Gunakanlah sesuai kebutuhan 😀

Case 2

Kali ini kita akan melakukan click pada widget “Refresh Color Primary“, lalu mencek hasil print. Berikut hasilnya:

Hasil click pertama

Kenapa Widget C dan Widget D ikut dibuild ulang, padahal yang diubah cuma colorsPrimary?

Hal ini wajar terjadi. Kenapa? Karena hal ini sesuai dengan konsep dasar dari Flutter. Pada schema kita sebelumnya WidgetB merupakan parent dari WidgetC, dan Widget C adalah parent dari Widget D. Kalau parent widget dibuild ulang, maka childnya akan dibuild ulang juga. Oleh karena itu Widget B, C, dan D dibuild ulang.

Lalu kalau widget “Refresh Color Primary” diclick sekali lagi, apa yang terjadi?

Hasil click kedua

Yang terjadi sama dengan case pertama, yaitu tidak ada terjadi perubahan karena nilainya tidak berubah.

Sekian dulu tulisan kali ini, jika ada penjelasan yang salah atau kurang tepat, silahkan berikan komentar. Dan jangan lupa untuk tepuk tangannya ya. Terimakasih telah meluangkan waktunya untuk membaca.

Oh ya, blog ini juga punya tulisan menarik lainnya loh, baik itu tentang flutter maupun hal lainnya. Terimakasih 😀

Menggunakan Google Map di Flutter

Ikhwan in Flutter
  ·   2 min read

Cara untuk Migrasi ke Flutter 2

Ikhwan in Flutter
  ·   2 min read

Tinggalkan Balasan

Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai *