CodeBlanks Logo Icon CodeBlanks

setState in depth

Learn how setState works and when it rebuilds.

setState in depth

setState marks a State object as 'dirty' and tells the framework that this widget needs to be rebuilt. The rebuild does not happen immediately; the framework defers this work until the next frame.

void increment() {
  setState(() {
    count++;
  });
}

This way multiple setState calls in a row can be merged into a single rebuild.

scheduleBuild and dirty

Internally setState does two things: first it runs the callback you gave it (e.g. count++), then it sets the _dirty flag on the State object to true and calls markNeedsBuild. That call adds the element to the scheduler's list.

setState(() { count++; });
// _dirty = true
// markNeedsBuild() scheduled

When the next frame begins, that element's build method is called again.

Practice StatefulWidget that increments a counter

Build a StatefulWidget. The _sayac variable starts at 0. In initState, call print('başlangıç: $_sayac'). When the button is pressed, increment _sayac with setState.

Starter code

main.dart
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyCounter(),
    );
  }
}

class MyCounter extends StatefulWidget {
  @override
  _MyCounterState createState() => _MyCounterState();
}

class _MyCounterState extends State<MyCounter> {
  int _sayac = 0;

  @override
  void initState() {
    super.initState();
    print('başlangıç: $_sayac');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Sayaç')),
      body: Center(
        child: Text('$_sayac'),
      ),
      floatingActionButton: FloatingActionButton(
        // setState ile _sayac'ı artır
        onPressed: () {},
        child: Icon(Icons.add),
      ),
    );
  }
}

Sample solution

main.dart
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyCounter(),
    );
  }
}

class MyCounter extends StatefulWidget {
  @override
  _MyCounterState createState() => _MyCounterState();
}

class _MyCounterState extends State<MyCounter> {
  int _sayac = 0;

  @override
  void initState() {
    super.initState();
    print('başlangıç: $_sayac');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Sayaç')),
      body: Center(
        child: Text('$_sayac'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            _sayac++;
          });
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

Expected output

başlangıç: 0

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 What happens when setState is called?

  • the State is marked dirty and a rebuild is scheduled for the next frame Correct answer
  • the app restarts and main runs again
  • It is only valid inside initState, otherwise it throws
  • the build method runs immediately in the same call stack

Explanation: setState marks the State dirty and schedules a rebuild on the next frame via markNeedsBuild; build is not called synchronously right away.

2 Complete the setState call

Question
void artir() {
  ____(() {
    sayac++;
  });
}
Answer
void artir() {
  setState(() {
    sayac++;
  });
}

Explanation: setState runs the given callback, then marks the State dirty and schedules a build.

3 State lifecycle parts

Term Meaning
setState Marks State dirty and schedules build
initState Called once when State is created
build The method that returns the widget tree
dispose Called when State is destroyed

All lessons in Intermediate