Search code examples
flutterdialogsetstate

setState() or markNeedsBuild() called during build CupertinoTabScaffold


I have code using CupertinoTabScaffold to display bottom navigation, I want to display the bottom dialog sheet on one of the menus, but get an error

he following assertion was thrown building Builder (dirty, dependencies: [_InheritedTheme, _LocalizationsScope- [GlobalKey # a6d51]]): setState () or markNeedsBuild () called during build.

this is my code

class AppTab extends StatefulWidget {
final dataUser;
AppTab(this.dataUser);

@override
_AppTabState createState() => _AppTabState(dataUser);
}

class _AppTabState extends State<AppTab> {
final user;
_AppTabState(this.user);

 @override
Widget build(BuildContext context) {
return BlocListener<AuthenticationBloc, AuthenticationState>(
  listener: (context, state) {
    if (state == AuthenticationState.unauthenticated()) {
      Navigator.pushNamedAndRemoveUntil(context, '/login', (route) => false, arguments: state.loginPageState);
    }
  },
  child: CupertinoTabScaffold(
    controller: tabController,
    tabBar: CupertinoTabBar(
      activeColor: HexColor("#26ADE4"),
      inactiveColor: HexColor("#707070"),
      items: [
        BottomNavigationBarItem(
          label: "Menu 1",
          icon: Image.asset("assets/icon_tab_home.png", height: 25, width: 22),
          activeIcon: Image.asset("assets/icon_tab_home_active.png", height: 25, width: 22),
        ),
        BottomNavigationBarItem(
          label: "Menu 2",
          icon: Image.asset("assets/icon_tab_buat_bill.png", height: 25, width: 22),
          activeIcon: Image.asset("assets/icon_tab_buat_bill_active.png", height: 25, width: 22),
        ),
        BottomNavigationBarItem(
          label: "Menu 3",
          icon: Image.asset("assets/icon_tab_account.png", height: 25, width: 22),
          activeIcon: Image.asset("assets/icon_tab_account_active.png", height: 25, width: 22),
        ),
      ],
    ),
    tabBuilder: (context, index) {
      if (index == 0) {
        return HomePage(user);
      } else if (index == 1) {
        return _bottomSheetMore(context);
      }
      return AccountPage(user);
    }),
);
}

_bottomSheetMore(context) {
showModalBottomSheet(
  context: context,
  builder: (builder) {
    return new Container(
      padding: EdgeInsets.only(
        left: 5.0,
        right: 5.0,
        top: 5.0,
        bottom: 5.0,
      ),
      decoration: new BoxDecoration(
          color: Colors.white,
          borderRadius: new BorderRadius.only(
              topLeft: const Radius.circular(10.0),
              topRight: const Radius.circular(10.0))),
      child: new Wrap(
        children: <Widget>[
          new ListTile(
            title: const Text(
              'Menu Akun',
              style: TextStyle(
                fontSize: 14.0,
                fontWeight: FontWeight.w700,
              ),
            ),
            subtitle: Text("Pilih salah satu"),
          ),
          new Divider(
            height: 10.0,
          ),
          new ListTile(
            title: const Text(
              'Menu Home',
              style: TextStyle(
                fontSize: 14.0,
                fontWeight: FontWeight.w700,
              ),
            ),
          ),
          new Divider(
            height: 10.0,
          ),
          new ListTile(
            title: const Text(
              'Logout',
              style: TextStyle(
                fontSize: 14.0,
                fontWeight: FontWeight.w700,
              ),
            ),
            onTap: () async {
              // Add Here
            },
          ),
        ],
      ),
    );
  },
);
 }
 }

Solution

  • It displays that error because the widget should be drawn first before displaying the modal bottom sheet. To display your modal bottom sheet, call it inside a Future.delayed(...).

    You can try something like this.

    import 'package:flutter/cupertino.dart';
    import 'package:flutter/material.dart';
    
    void main() => runApp(MaterialApp(home: AppTab()));
    
    class AppTab extends StatefulWidget {
      @override
      _AppTabState createState() => _AppTabState();
    }
    
    class _AppTabState extends State<AppTab> {
      final CupertinoTabController _controller = CupertinoTabController();
      final List<CupertinoPageScaffold> _pages = <CupertinoPageScaffold>[
        CupertinoPageScaffold(
          child: const Center(child: Text('Home Page')),
        ),
        CupertinoPageScaffold(
          child: const Center(child: Text('2nd Page')),
        ),
        CupertinoPageScaffold(
          child: const Center(child: Text('Account Page')),
        ),
      ];
    
      bool _isBottomSheetShown = false;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: CupertinoTabScaffold(
            controller: _controller,
            tabBar: CupertinoTabBar(
              activeColor: Colors.red,
              inactiveColor: Colors.blue,
              items: const [
                BottomNavigationBarItem(
                  label: "Menu 1",
                  icon: Icon(Icons.ac_unit),
                ),
                BottomNavigationBarItem(
                  label: "Menu 2",
                  icon: Icon(Icons.local_activity),
                ),
                BottomNavigationBarItem(
                  label: "Menu 3",
                  icon: Icon(Icons.assessment),
                ),
              ],
            ),
            tabBuilder: (context, index) {
              return CupertinoTabView(
                builder: (BuildContext context) {
                  if (_controller.index == 1 && index == 1) {
                    _bottomSheetMore(context);
                  }
    
                  return _pages[index];
                },
              );
            },
          ),
        );
      }
    
      Future<void> _bottomSheetMore(context) async {
        if (_isBottomSheetShown) {
          return;
        }
    
        _isBottomSheetShown = true;
    
        await Future<void>.delayed(
          const Duration(milliseconds: 10),
          () => showModalBottomSheet<void>(
            context: context,
            builder: (_) {
              return Container(
                height: 300,
                padding: const EdgeInsets.all(5),
                decoration: const BoxDecoration(
                  color: Colors.white,
                  borderRadius: BorderRadius.only(
                    topLeft: const Radius.circular(10.0),
                    topRight: const Radius.circular(10.0),
                  ),
                ),
                child: Wrap(
                  children: <Widget>[
                    ListTile(
                      title: const Text(
                        'Menu Akun',
                        style: TextStyle(
                          fontSize: 14.0,
                          fontWeight: FontWeight.w700,
                        ),
                      ),
                      subtitle: const Text("Pilih salah satu"),
                    ),
                    const Divider(height: 10.0),
                    ListTile(
                      title: const Text(
                        'Menu Home',
                        style: TextStyle(
                          fontSize: 14.0,
                          fontWeight: FontWeight.w700,
                        ),
                      ),
                    ),
                    const Divider(height: 10.0),
                    ListTile(
                      title: const Text(
                        'Logout',
                        style: TextStyle(
                          fontSize: 14.0,
                          fontWeight: FontWeight.w700,
                        ),
                      ),
                      onTap: () async {
                        // Add Here
                      },
                    ),
                  ],
                ),
              );
            },
          ),
        );
    
        _isBottomSheetShown = false;
      }
    
      @override
      void dispose() {
        super.dispose();
        _controller.dispose();
      }
    }