What is "dirty" in Flutter & what is causing this "dirty" state?

Issue

I am trying to learn both state management & dependancy injection with this demo project. I’m trying to demo injecting some methods all over the place like I may need to in my program. I’m using GetX because I like being able to do this without context in non-widget classes.

So my problem here is the last method, summationReturns(), in the last class below. Attempts to take methods with return statements and add them together. I call this in two places. In the floating button, this works fine but in my text widget I get a dirty state error.

Why is this not working when everything else works? And I assume this will be a corollary from the last question, what is a dirty state? Seems like two questions but I would imagine that they are one in the same.

///
///
/// DEMO PROJECT WORKING OUT GETX
/// WORKOUT DEPENDANCY INJECTION AND STATE MANAGEMENT

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:get/get_state_manager/get_state_manager.dart';

void main() {
  runApp(GetMaterialApp(
    home: Home(),
    debugShowCheckedModeBanner: false,
  ));
}

class Home extends StatelessWidget {
  // Injection of dependancy
  final Controller controller = Get.put(Controller());
  final Observable observable = Get.put(Observable());
  final SimpleMath simpleMath = Get.put(SimpleMath());

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('GetX Demo'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('Get builders:'),
            GetBuilder<Controller>(builder: (controller) {
              return Text(controller.count.toString());
            }),
            GetBuilder<Controller>(builder: (controller) {
              return Text(controller.countList.toString());
            }),
            GetBuilder<Controller>(builder: (controller) {
              return Text(controller.returnCount().toString());
            }),
            GetBuilder<Controller>(builder: (controller) {
              return Text(controller.returnList().toString());
            }),
            SizedBox(height: 20.0),
            Text('Get observables:'),
            Obx(() => Text(observable.count.value.toString())),
            Obx(() => Text(observable.countList.value.toString())),
            Obx(() => Text(observable.returnCount().toString())),
            Obx(() => Text(observable.returnList().toString())),
            SizedBox(height: 20.0),
            Text('Get from other class:'),
            GetBuilder<SimpleMath>(builder: (simpleMath) {
              return Text('Variable summation: ' + simpleMath.summationVariables().toString());
            }),
            GetBuilder<SimpleMath>(builder: (simpleMath) {
              return Text(simpleMath.summationReturns().toString());
            }),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          controller.crunch();
          observable.crunch();
          simpleMath.summationVariables();
          simpleMath.summationReturns();
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

class Controller extends GetxController {
  int count = 0;
  List<int> countList = [];

  void crunch() {
    count += 1;
    countList.add(count);
    update();
  }

  int returnCount() {
    return count;
  }

  List<int> returnList() {
    return countList;
  }
}

class Observable extends GetxController {
  RxInt count = 0.obs;
  Rx<RxList> countList = RxList().obs;

  void crunch() {
    count.value += 1;
    countList.value.add(count.value);
  }

  int returnCount() {
    return count.value;
  }

  List<dynamic> returnList() {
    return countList.value.toList();
  }
}

class SimpleMath extends GetxController {
  final Controller controller = Get.find<Controller>();
  final Observable observable = Get.find<Observable>();

  int summationVariables() {
    int sum = controller.count + observable.count.value;
    update();
    return sum;
  }

  int summationReturns() {
    int sum = controller.returnCount() + observable.returnCount();
    print('Summation of return values: ' + sum.toString());
    update();
    return sum;
  }
}

Error:

══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following assertion was thrown building GetBuilder<SimpleMath>(dirty, state:
GetBuilderState<SimpleMath>#4d62d):
setState() or markNeedsBuild() called during build.
This GetBuilder<SimpleMath> widget cannot be marked as needing to build because the framework is
already in the process of building widgets.  A widget can be marked as needing to be built during
the build phase only if one of its ancestors is currently building. This exception is allowed
because the framework builds parent widgets before children, which means a dirty descendant will
always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was:
  GetBuilder<SimpleMath>
The widget which was currently being built when the offending call was made was:
  GetBuilder<SimpleMath>

The relevant error-causing widget was:
  GetBuilder<SimpleMath>
  file:///Users/robertobuttazzoni/Documents/Flutter%20Tutorials/Flutter%20Learning/getx_basics/getx_basics/lib/main.dart:57:13

Solution

Calling update while build is ongoing is an example of dirty scenario. To fix your issue, do not call update inside the GetBuilder.

Sample…

In Home

GetBuilder<SimpleMath>(
    builder: (simpleMath) => Text('Variable summation: ' +
        simpleMath
            .summationVariables(shouldUpdate: false)
            .toString())),
GetBuilder<SimpleMath>(
    builder: (simpleMath) => Text(simpleMath
        .summationReturns(shouldUpdate: false)
        .toString())),

In SimpleMath

int summationVariables({bool shouldUpdate = true}) {
  int sum = controller.count + observable.count.value;
  if (shouldUpdate) update();
  return sum;
}

int summationReturns({bool shouldUpdate = true}) {
  int sum = controller.returnCount() + observable.returnCount();
  print('Summation of return values: ' + sum.toString());
  if (shouldUpdate) update();
  return sum;
}

Answered By – rickimaru

Answer Checked By – Mildred Charles (FlutterFixes Admin)

Leave a Reply

Your email address will not be published.