Home > Enterprise >  setState is not updating the UI even though the variable isn't declared in the build method
setState is not updating the UI even though the variable isn't declared in the build method

Time:11-24

The setState function isn't updating the text in the List even though the variable isn't declared in the build method.

I'm trying to make the text change in the first and second tab of the bottomNavigationBar.

Here is the code:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  static const String _title = 'Flutter Code Sample';

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: _title,
      home: MyStatefulWidget(),
    );
  }
}

class MyStatefulWidget extends StatefulWidget {
  const MyStatefulWidget({super.key});

  @override
  State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}


class _MyStatefulWidgetState extends State<MyStatefulWidget> {
  int _selectedIndex = 0;
  static const TextStyle optionStyle =
  TextStyle(fontSize: 30, fontWeight: FontWeight.bold);

  int _volume = 0;

  void changeVolume(){
    _volume  = 1;
  }

  late final List<Widget> _widgetOptions = <Widget>[
    Row(
      children: <Widget>[
        IconButton(
          icon: const Icon(Icons.volume_up),
          tooltip: 'Increase volume by 1',
          onPressed: (){
            setState(() {
              changeVolume();
            });
          },
        ),
        Expanded(
          child: Text('Volume : $_volume'),
        ),
        Expanded(
          child: FittedBox(
            child: FlutterLogo(),
          ),
        ),
      ],
    ),
    Text('Volume  : $_volume'),
    Text(
      'Index 2: School',
      style: optionStyle,
    ),
  ];

  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {


    return Scaffold(
      appBar: AppBar(
        title: const Text('BottomNavigationBar Sample'),
      ),
      body: Center(
        child: _widgetOptions.elementAt(_selectedIndex),
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            label: 'Home',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.business),
            label: 'Business',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.school),
            label: 'School',
          ),
        ],
        currentIndex: _selectedIndex,
        selectedItemColor: Colors.amber[800],
        onTap: _onItemTapped,
      ),
    );
  }
}

Most of the times this problem comes up on this site it is because someone declared the variable in the build method but I'm not doing that here, so I'm not sure why the text isn't updating.

CodePudding user response:

setState will re-run the build method, so if you declare _widgetOptions outside the build method, it's content won't change. setState on _selectedIndex will work because it just selects one item in _widgetOptions.

So remove late final and move the declaration of _widgetOptions into the build method:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  static const String _title = 'Flutter Code Sample';

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: _title,
      home: MyStatefulWidget(),
    );
  }
}

class MyStatefulWidget extends StatefulWidget {
  const MyStatefulWidget({super.key});

  @override
  State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> {
  int _selectedIndex = 0;
  static const TextStyle optionStyle =
      TextStyle(fontSize: 30, fontWeight: FontWeight.bold);

  int _volume = 0;

  void changeVolume() {
    _volume  = 1;
  }

  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    List<Widget> _widgetOptions = <Widget>[
      Row(
        children: <Widget>[
          IconButton(
            icon: const Icon(Icons.volume_up),
            tooltip: 'Increase volume by 1',
            onPressed: () {
              setState(() {
                changeVolume();
              });
            },
          ),
          Expanded(
            child: Text('Volume : $_volume'),
          ),
          Expanded(
            child: FittedBox(
              child: FlutterLogo(),
            ),
          ),
        ],
      ),
      Text('Volume  : $_volume'),
      Text(
        'Index 2: School',
        style: optionStyle,
      ),
    ];

    return Scaffold(
      appBar: AppBar(
        title: const Text('BottomNavigationBar Sample'),
      ),
      body: Center(
        child: _widgetOptions.elementAt(_selectedIndex),
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            label: 'Home',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.business),
            label: 'Business',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.school),
            label: 'School',
          ),
        ],
        currentIndex: _selectedIndex,
        selectedItemColor: Colors.amber[800],
        onTap: _onItemTapped,
      ),
    );
  }
}

Of course you can optimize it but the above code works in DartPad as expected.

(By the way, late marks a member variable that has no value assigned to it when you declare, but you make sure it will have a value before any part of your code accesses it. So in fact it has no real meaning when you assign a non-null value to the variable.)

  • Related