Search code examples
fluttersetstate

Flutter: how can I deal with multiple setState() method?


I am new in Flutter framework, and do not fully understood how the multiple setStat() method does work. I am calling setState() twice in one function but only first one is working.

I have searched for the solutions here but did't find it. But it was working with this code (reference for the full code github):

  _updateIndex(int index) {
    _selectedTab(index);
    setState(() {
      _selectedIndex = index;
    });
  }

  void _selectedTab(int index) {
    setState(() {
      _lastSelected = 'TAB: $index';
    });
  }

and now it does not work with this code:

  _updateIndex(int index) {
    _selectedTab(index); // works fine
    setState(() {
      _selectedIndex = index; // does not work
    });
  }

  void _selectedTab(int index) {
    switch(index) {
       case 1:
          setState(() {
             _lastSelected = 'TAB: $index';
          });
          break;
       // here is other cases and default
    }
  }

I thought it will work with switch, but I don't think that problem is directly in switch function.

Full code: fab_bottom_app_bar.dart

import 'package:flutter/material.dart';

class FABBottomAppBarItem {
  FABBottomAppBarItem({this.iconData, this.text});
  IconData iconData;
  String text;
}

class FABBottomAppBar extends StatefulWidget {
  FABBottomAppBar({
    this.items,
    this.centerItemText,
    this.height: 60.0,
    this.iconSize: 24.0,
    this.backgroundColor,
    this.color,
    this.selectedColor,
    this.notchedShape,
    this.onTabSelected,
  }) {
    assert(this.items.length == 2 || this.items.length == 4);
  }
  final List<FABBottomAppBarItem> items;
  final String centerItemText;
  final double height;
  final double iconSize;
  final Color backgroundColor;
  final Color color;
  final Color selectedColor;
  final NotchedShape notchedShape;
  final ValueChanged<int> onTabSelected;

  @override
  State<StatefulWidget> createState() => FABBottomAppBarState();
}

class FABBottomAppBarState extends State<FABBottomAppBar> {
  static int _selectedIndex = 0;

  _updateIndex(int index) {
    widget.onTabSelected(index);
    setState(() {
      _selectedIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    List<Widget> items = List.generate(widget.items.length, (int index) {
      return _buildTabItem(
        item: widget.items[index],
        index: index,
        onPressed: _updateIndex,
      );
    });
    items.insert(items.length >> 1, _buildMiddleTabItem());

    return BottomAppBar(
      shape: widget.notchedShape,
      child: Row(
        mainAxisSize: MainAxisSize.max,
        mainAxisAlignment: MainAxisAlignment.spaceAround,
        children: items,
      ),
      color: widget.backgroundColor,
    );
  }

  Widget _buildMiddleTabItem() {
    return Expanded(
      child: SizedBox(
        height: widget.height,
        child: Column(
          mainAxisSize: MainAxisSize.min,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            SizedBox(height: widget.iconSize),
            Text(
              widget.centerItemText ?? '',
              style: TextStyle(color: widget.color, fontSize: 14),
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildTabItem({
    FABBottomAppBarItem item,
    int index,
    ValueChanged<int> onPressed,
  }) {
    Color color = _selectedIndex == index ? widget.selectedColor : widget.color;
    print(Text('COLOR CHANGED $_selectedIndex - $index = $color ---------------------------------'));
    return Expanded(
      child: SizedBox(
        height: widget.height,
        child: Material(
          type: MaterialType.transparency,
          child: InkWell(
            onTap: () => onPressed(index),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Icon(item.iconData, color: color, size: widget.iconSize),
                Text(
                  item.text,
                  style: TextStyle(fontSize: 10, color: color),
                )
              ],
            ),
          ),
        ),
      ),
    );
  }
}

and home.dart where actually bottom bar is used:

import 'dart:async';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flushbar/flushbar.dart';
import 'package:flutter_picker/Picker.dart';

import '../assets/colors.dart';
import '../models/fab_bottom_app_bar.dart';

import 'content/main.dart';
import 'content/results.dart';
import 'content/wallet.dart';
import 'content/questions.dart';
import 'content/profile.dart';

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

  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  static const int MAIN_PAGE = 0;
  static const int RESULTS_PAGE = 1;
  static const int QUESTIONS_PAGE = 2;
  static const int WALLET_PAGE = 3;

  Widget _navigator = mainContent();

  void _selectedTab(int index) {
    print(Text('FAB SELECTED ---------------------------'));
    switch (index) {
      case MAIN_PAGE:
        setState(() {
          _navigator = mainContent();
        });
        break;
      case RESULTS_PAGE:
        setState(() {
          _navigator = resultContent();
        });
        break;
      case QUESTIONS_PAGE:
        setState(() {
          _navigator = questionsContent();
        });
        break;
      case WALLET_PAGE:
        setState(() {
          _navigator = walletContent();
        });
        break;
      default:
        setState(() {
          _navigator = mainContent();
        });
        break;
    }
  }

  @override
  Widget build(BuildContext context) {
    final GlobalKey<ScaffoldState> _scaffoldKey =
        new GlobalKey<ScaffoldState>();

    return Scaffold(
        bottomNavigationBar: FABBottomAppBar(
          color: Colors.grey,
          selectedColor: Colors.white,
          notchedShape: CircularNotchedRectangle(),
          onTabSelected: _selectedTab,
          backgroundColor: AppColors.mainColor,
          items: [
            FABBottomAppBarItem(iconData: Icons.home, text: 'Вплюсе'),
            FABBottomAppBarItem(iconData: Icons.whatshot, text: 'Результаты'),
            FABBottomAppBarItem(iconData: Icons.thumbs_up_down, text: 'Опросы'),
            FABBottomAppBarItem(
                iconData: Icons.account_balance_wallet, text: 'Кошельки')
          ],
        ),
        floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
        floatingActionButton: _buildFab(context));
  }

  Widget _buildFab(BuildContext context) {
    return FloatingActionButton(
      onPressed: () {
        _promoDialog();
      },
      tooltip: 'Increment',
      child: Icon(Icons.add),
      elevation: 2.0,
      backgroundColor: AppColors.mainColor,
    );
  }
}

Solution

  • There are several solutions:

    1. Use another navigation bars which has been provided by pskink in the comments:

      class MyNavigationBar extends StatefulWidget {
        final List<String> list;
      
        MyNavigationBar(this.list);
      
        @override
        _MyState createState() => _MyState();
      }
      
      class _MyState extends State<MyNavigationBar> {
        var current;
      
        @override
        Widget build(BuildContext context) {
          return Container(
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: widget.list.map((t) {
                current ??= t;
                return InkWell(
                  onTap: () => setState(() => current = t),
                  child: Text(
                    t,
                    style: TextStyle(
                      fontSize: 32,
                      color: current == t ? Colors.red : Colors.grey,
                    ),
                  ),
                );
              }).toList(),
            ),
          );
        }
      }
      

      it is easy to cusomize it.

    2. to make _selectedIndex in fab_bottom_app_bar.dart static.