flutter
/

Flutter setState – A Complete Guide to Stateful Widgets

Last Sync: Today

On this page

8
0%
5 min read
Remaining
5 minleft

Click any section to jump — progress syncs automatically

flutter

Flutter setState – A Complete Guide to Stateful Widgets

What is setState?

setState is a method provided by StatefulWidget that tells Flutter that the state of the widget has changed and that the UI should be rebuilt. It is the simplest form of state management in Flutter and is perfect for managing local state that belongs to a single widget. When you call setState, you pass a callback that modifies the state variables. After the callback finishes, Flutter schedules a rebuild of the widget, updating the UI with the new values.

Basic Usage

To use setState, your widget must extend StatefulWidget and you must have a corresponding State class. Inside the State class, you call setState to update variables. The typical pattern is:

DARTRead-only
1
class Counter extends StatefulWidget {
  @override
  _CounterState createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  int _count = 0;

  void _increment() {
    setState(() {
      _count++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Count: $_count'),
        ElevatedButton(
          onPressed: _increment,
          child: Text('Increment'),
        ),
      ],
    );
  }
}

Notice that _count is a member variable of the _CounterState class. The setState callback modifies it, and then the build method is called again, reflecting the new value.

When to Use setState

    • Local state that only affects a single widget or a small subtree.
    • Simple interactions like toggles, counters, text fields, and animations.
    • When you don't need to share state across multiple widgets.
    • For prototyping or small apps where more complex state management would be overkill.

Performance Considerations

setState rebuilds the entire widget subtree of the State object. To keep your app fast:

    • Keep the widget tree small under the state that calls setState. If possible, split large widgets into smaller ones so that only the affected part rebuilds.
    • Avoid calling setState unnecessarily, e.g., inside loops or without actual changes.
    • Use const widgets wherever possible to avoid unnecessary rebuilds.
    • Don't call setState after the widget is disposed. Always check mounted before calling setState in asynchronous callbacks.

Common Mistakes

    • Calling setState without changing any state: This causes unnecessary rebuilds. Only call it when state actually changes.
    • Modifying state outside setState: If you modify a state variable without wrapping it in setState, the UI won't update, leading to bugs.
    • Calling setState in build: This creates an infinite loop because setState triggers build, which calls setState again.
    • Forgetting to check mounted in asynchronous callbacks: If you call setState after a Future completes but the widget has been disposed, you'll get an error. Always use if (mounted) before calling setState.
    • Creating large, monolithic State widgets: This makes rebuilds expensive. Break your UI into smaller widgets.

Advanced: Updating Multiple Variables

You can update multiple state variables in a single setState call. Flutter will batch the rebuild. For example:

DARTRead-only
1
void _updateBoth() {
  setState(() {
    _count++;
    _isToggled = !_isToggled;
  });
}

Using setState with TextEditingController

When you need to display the text of a TextField elsewhere, you can listen to changes with a TextEditingController and call setState to update the UI.

DARTRead-only
1
class _MyState extends State<MyWidget> {
  final TextEditingController _controller = TextEditingController();
  String _display = '';

  @override
  void initState() {
    super.initState();
    _controller.addListener(() {
      setState(() {
        _display = _controller.text;
      });
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        TextField(controller: _controller),
        Text('You typed: $_display'),
      ],
    );
  }
}

Key Takeaways

    • setState is used in StatefulWidget to update the UI.
    • Call it inside a function that modifies the state variables.
    • Only update variables that are actually used in the build method.
    • Keep the rebuild scope small by splitting widgets.
    • Always check mounted before calling setState in asynchronous code.
    • For shared state across widgets, consider using a state management solution like Provider, Bloc, or Riverpod.

Try it yourself

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'setState Demo',
      home: Scaffold(
        appBar: AppBar(title: Text('Counter')),
        body: Center(child: CounterWidget()),
      ),
    );
  }
}

class CounterWidget extends StatefulWidget {
  @override
  _CounterWidgetState createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State<CounterWidget> {
  int _count = 0;
  bool _isVisible = true;

  void _increment() {
    setState(() {
      _count++;
    });
  }

  void _toggleVisibility() {
    setState(() {
      _isVisible = !_isVisible;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        if (_isVisible)
          Text(
            'Count: $_count',
            style: TextStyle(fontSize: 24),
          ),
        SizedBox(height: 20),
        ElevatedButton(
          onPressed: _increment,
          child: Text('Increment'),
        ),
        SizedBox(height: 10),
        ElevatedButton(
          onPressed: _toggleVisibility,
          child: Text(_isVisible ? 'Hide Count' : 'Show Count'),
        ),
      ],
    );
  }
}

Test Your Knowledge

Q1
of 4

In which type of widget can you use setState?

A
StatelessWidget
B
StatefulWidget
C
Both
D
None
Q2
of 4

What does setState do?

A
It modifies the widget tree directly
B
It marks the widget as dirty and schedules a rebuild
C
It permanently changes the widget's properties
D
It updates the app's theme
Q3
of 4

How do you check if the widget is still mounted before calling setState in an async callback?

A
if (this.isAlive)
B
if (this.mounted)
C
if (mounted)
D
if (context.isMounted)
Q4
of 4

What happens if you call setState inside the build method?

A
The UI updates correctly
B
It causes an infinite loop and the app freezes
C
It throws an error immediately
D
It works but is inefficient

Frequently Asked Questions

Can I use setState in a StatelessWidget?

No, setState is only available in StatefulWidget because it requires the State object to hold the mutable state. If you need to update UI, you must use a StatefulWidget.

What happens if I call setState after the widget is disposed?

Flutter throws a FlutterError (in debug mode) or a setState() called after dispose() message. To avoid this, always check if (mounted) before calling setState, especially inside futures or callbacks.

Does setState rebuild the whole widget tree?

No, it rebuilds only the subtree of the State object that called setState. However, if your State widget contains a large subtree, all its children will be rebuilt. To optimize, split the widget tree into smaller widgets that rebuild independently.

How do I know when to use setState vs other state management solutions?

Use setState for local state that doesn't need to be shared. For state that is used across multiple widgets, or for complex app state, consider Provider, Riverpod, Bloc, or other solutions.

Can I call setState from outside the State class?

Not directly. You need to provide a callback that the State class can call. For example, you can pass a function from the parent to the child that calls setState in the parent.

Previous

flutter tabbar

Next

flutter provider

Related Content

Need help?

Explore our comprehensive docs or start a chat with our tech experts.