I am using Flutter to build an app and trying to show an input dialogue box when app is run for the first time. For this I use shared preferences library. However, when I open the app, the dialogue box is shown every time regardless if it is the first time or not. Also, as I type a single letter in the input box, another dialogue box opens up in front of the previous one. I have attached the code below:

 class HomeMenu extends StatefulWidget {
  const HomeMenu({Key? key}) : super(key: key);

  @override
  State<HomeMenu> createState() => _HomeMenuState();
 }

class _HomeMenuState extends State<HomeMenu> with TickerProviderStateMixin {
  late TabController _controller;
  TextEditingController textController = TextEditingController();

  var newLaunch;

  Future<void> showDialogIfFirstLoaded(BuildContext thisContext) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    bool _newLaunch = prefs.getBool('newLaunch') ?? true;
    newLaunch = _newLaunch;
    if (newLaunch) {
      showDialog(
        context: thisContext,
        builder: (BuildContext context) {
          return AlertDialog(
            content: TextField(
              controller: textController,
              onChanged: (value) =>
                  setState(() => name = textController.text),
            ),
            actions: <Widget>[
              TextButton(
                child: const Text('OK'),
                onPressed: () {
                  Navigator.pop(thisContext);
                  prefs.setBool(newLaunch, false);
                },
              ),
            ],
          );
        },
      );
    }
  }

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

  @override
  Widget build(BuildContext context) {
    Future.delayed(Duration.zero, () => showDialogIfFirstLoaded(context));
    return Scaffold(
      appBar: AppBar(
        bottom: TabBar(
          controller: _controller,
          tabs: tabs,
        ),
      ),
      body: TabBarView(
        controller: _controller,
        children: [
           null
        ],
      ),
    );
  }
}


Solution 1: Harish Sharma

you are calling showDialogIfFirstLoaded in build method, and on TextFiled onChange method you are calling setState. Every time you call setState build method re-build again and it calling showDialogIfFirstLoaded on every setState.

I don't know your requirement but it not good to call setState in onChange method of TextField. You can use ValueListenableBuilder if you require to update some part of UI on every single char type in text filed

If you want's to open that dialog only one time then create a method to get whether it's first launch or not and if yes then call showDialogIfFirstLoaded method.

For more details see below code -

   @override
      void initState() {
        super.initState();
        _controller = TabController(length: 3, vsync: this);
        _isFirstLaunch();
      }

    _isFirstLaunch()async{
       final prefs = await SharedPreferences.getInstance();
       final bool? isfirstLaunch = prefs.getBool('first_launch');
       if(!isfirstLaunch ?? true){
        await prefs.setBool('first_launch', true);
        showDialogIfFirstLoaded(context);
       }

and update your build method like this -

 @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        bottom: TabBar(
          controller: _controller,
          tabs: tabs,
        ),
      ),
      body: TabBarView(
        controller: _controller,
        children: [
           null
        ],
      ),
    );
  }