Set State in Flutter, build method and Internal State

What does the set state method mean in Flutter? In this section we’ll try to understand the topic from a beginner’s point of view. 

If you have enough experience in Flutter, you may skip this section. Or, applying your experience if you find any inconsistency in this explanation, please express your view in the comment box.

Before we’re going to explain the set state method in Flutter, let’s see a simple example.

When we create any Flutter app, it comes with a counter app. Right? 

Just refactor the code by breaking it up in several widgets.

Firstly, the set state method in Flutter notifies the Framework that the internal State of this object has changed.

Secondly, whenever we change the internal State of a State object, we make the change in a function. 

However, that sounds quite natural.

Why?

Because the role of a function is to change the state of the field of any object. That is the crux of object oriented programming.

As a result, we pass setState to that function where we make the change.

Let’s see a simple Stateful widget where we have used a counter to press the button and change the internal state of the object.

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    print('print My App');
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    print('print MyHomePageState');
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: CenterWidget(counter: _counter),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

class CenterWidget extends StatelessWidget {
  const CenterWidget({
    Key? key,
    required int counter,
  })  : _counter = counter,
        super(key: key);

  final int _counter;

  @override
  Widget build(BuildContext context) {
    print('print Center Widget');
    return Center(
      child: ColumnWidget(counter: _counter),
    );
  }
}

class ColumnWidget extends StatelessWidget {
  const ColumnWidget({
    Key? key,
    required int counter,
  })  : _counter = counter,
        super(key: key);

  final int _counter;

  @override
  Widget build(BuildContext context) {
    print('print Column Widget');
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        const Text(
          'You have pushed the button this many times:',
        ),
        Text(
          '$_counter',
          style: Theme.of(context).textTheme.headline4,
        ),
      ],
    );
  }
}

Once we run the above code what do we see in the console?

Let’s see the output first.

print My App
print MyHomePageState
print Center Widget
print Column Widget

But it has no relation with the Stateful widget. If we change the above code to Stateless widget, we will get the same output.

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    print('print My App');
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key? key}) : super(key: key);

  final String title = 'Stateless';

  @override
  Widget build(BuildContext context) {
    print('print MyHomePageState');
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: const CenterWidget(),
    );
  }
}

class CenterWidget extends StatelessWidget {
  const CenterWidget({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    print('print Center Widget');
    return const Center(
      child: ColumnWidget(),
    );
  }
}

class ColumnWidget extends StatelessWidget {
  const ColumnWidget({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    print('print Column Widget');
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: const <Widget>[
        Text(
          'You have pushed the button this many times:',
        ),
      ],
    );
  }
}

Set State and build method in Flutter

When we run any Flutter app, the framework calls every child widget with the build method and that impacts the user interface.

It has nothing to do with the Stateful widget.

But we can only use the setState with Stateful widget. 

For example we have pressed the floating action button four times.

Set State in Flutter and a simple Counter app
Set State in Flutter and a simple Counter app

With each press, we actually call the set state in flutter and the setState notifies the framework that the internal state of this object has changed so that it changes the user interface in this subtree. 

As a result, the Flutter framework should schedule a build for this State object.

For that reason, when we press the button for four times, we get the following output on the console.

print MyHomePageState
print Center Widget
print Column Widget
print MyHomePageState
print Center Widget
print Column Widget
print MyHomePageState
print Center Widget
print Column Widget
print MyHomePageState
print Center Widget
print Column Widget

However, we can change the State without calling setState.

Either through Inherited widgets, or with the help of Provider package.In such cases, the Flutter framework will not schedule a build for each child widget in the subtree.

We’ll take a look at that topic in the next section. 

It’s important to understand the State object and its role, because we’re going to build an E-Commerce app in Flutter.

So stay tuned.

What Next?

Books at Leanpub

Books in Apress

My books at Amazon

Courses at Educative

GitHub repository

Python and Data Science

Twitter

Comments

One response to “Set State in Flutter, build method and Internal State”

  1. […] In our previous section we have seen a simple Stateful widget where we have used a counter to press the button and change the internal state of the object. As a result, it calls the build method in Flutter each time we press the button. […]

Leave a Reply