Search code examples
flutterdarttabbarprovider

Why do people put TabController inside initState when putting it inside Widget build does the work just fine?


A lot of the tutorial I saw always put the TabController inside initState like this:
(example tutorial: TabBar Widget in flutter)

TabController _tabController;

void initState() {
  super.initState();
  _tabController = TabController(length: 3, vsync: this);
}

but none has explained why, when below example works just fine:

TabController _tabController;

Widget build(BuildContext context) {
  _tabController = TabController(length: 3, vsync: this);
  ...
}

Is there any advantage of putting it inside initState and not inside Widget build?

I'm asking this because I want to use provider for the TabController length and can't do it if I put TabController inside initState, so right now what I'm doing is this:

TabController _tabController;

Widget build(BuildContext context) {
  _tabController = TabController(length: Provider.of<List>(context).listLength, vsync: this);
  ...
}

and I want to know if there is a known bug or whatever reason it is people put the TabController inside initState.


Solution

  • It's because initState only runs once, when the component is created, build runs each time the component is rendered, so if you initialize the state inside build, you will be initializing the state everytime.

    Here is an example, BuildWidget will change its value on each render, while StateWidget will only change its value on initialization.

    class BuildWidget extends StatefulWidget {
      const BuildWidget({Key? key}) : super(key: key);
    
      @override
      _BuildWidgetState createState() => _BuildWidgetState();
    }
    
    class _BuildWidgetState extends State<BuildWidget> {
      final Random _random = Random();
      int _value = 0;
      @override
      Widget build(BuildContext context) {
        _value = _random.nextInt(100);
        return Center(
          child: Text('My value is $_value'),
        );
      }
    }
    
    class StateWidget extends StatefulWidget {
      const StateWidget({Key? key}) : super(key: key);
    
      @override
      _StateWidgetState createState() => _StateWidgetState();
    }
    
    class _StateWidgetState extends State<StateWidget> {
      final Random _random = Random();
      int _value = 0;
      @override
      void initState() {
        _value = _random.nextInt(100);
        super.initState();
      }
    
      @override
      Widget build(BuildContext context) {
        return Center(
          child: Text('My value is $_value'),
        );
      }
    }