'How to call function from Stateful widget

I have seen many questions similar to mine in Stack Overflow but it did not fit my case since they were asking to call function from - to Stateful widget.

I want call function located into State Full widget from a non Stateful-Stateless Widget

My code is complicated, I will try to explain it below:

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

  @override
  _ExampleState createState() => _ExampleState();
}

class _ExampleState extends State<Example> {

  void myFunction(){
    print('hello dart');
  }
  ShowDialog showDialog = ShowDialog();
  @override
  Widget build(BuildContext context) {
    return TextButton(
      onPressed: (){
        showDialog.myDialog();
      },
        child: Text('tab me')
    );
  }
}


class ShowDialog {

  Widget myDialog(){
    return showDialog(
      builder: (BuildContext context) {
                return SimpleDialog(
                  
                  backgroundColor: Colors.deepPurple[900],
                  titleTextStyle: const TextStyle(
                      color: Colors.red, fontSize: 18),
                  children: [
                    ElevatedButton(
                        onPressed: () {
                         // here i need to call myFunction() Methood 
                        },
                        child: const Text("tab")
                    ),
                  ],
                )
      },
    );
  }

}

How can I go through this?



Solution 1:[1]

you can call it directly like this:

_ExampleState().myFunction();

The full code:

class Example  extends StatefulWidget {
    const Example ({Key? key, required this.title}) : super(key: key);

      final String title;

      @override
      State<Example > createState() => _ExampleState ();
    }

    class _ExampleState  extends State<Example > {
    
      void myFunction(){
        print('hello dart');
              }
      ShowDialog showDialog = ShowDialog();

     @override
      Widget build(BuildContext context) {

        return Scaffold(
          appBar: AppBar(

            title: Text(widget.title),
          ),
          body: TextButton(
              onPressed: (){
                showDialog.myDialog(context);
              },
              child: Text('tab me')
          )
        );
      }
    }


    class ShowDialog {

      Future myDialog(BuildContext context){
        return showDialog(
            context: context,
          builder: (BuildContext context) {
            return SimpleDialog(

              backgroundColor: Colors.deepPurple[900],
              titleTextStyle: const TextStyle(
                  color: Colors.red, fontSize: 18),
              children: [
                ElevatedButton(
                    onPressed: () {
                      // here i need to call myFunction() Methood
                      _ExampleState().myFunction();
                    },
                    child: const Text("tab")
                ),
              ],
            );
          },
        );
      }

    }

The result:

enter image description here

Solution 2:[2]

So in order to have your showDialog function call MyFunction, you need to pass it as if it was a callback.

To do that, first add a Function callback in your class:

class ShowDialog {
ShowDialog({required this.callback});

VoidCallback callback;


...
}

Then you have to pass the callback when you create the object:

ShowDialog showDialog = ShowDialog(callback: myFunction);

You actually can't do that tho, because this is a class variable, a simple solution is to turn your showDialog variable into a getter, this means showDialog will compute again every time you call it, which is not ideal but I don't think it will be terrible for this specific use-case.

ShowDialog get showDialog => ShowDialog(callback: myFunction);

note the get keyword and the => instead of an equal sign

EDIT:

You can also pass the callback as part of the myDialog method, this is probably actually a better idea:

Widget myDialog(VoidCallback callback) {
    return showDialog(
      builder: (BuildContext context) {
                return SimpleDialog(
                  
                  backgroundColor: Colors.deepPurple[900],
                  titleTextStyle: const TextStyle(
                      color: Colors.red, fontSize: 18),
                  children: [
                    ElevatedButton(
                        onPressed: callback,
                        child: const Text("tab")
                    ),
                  ],
                )
      },
    );
  }

Solution 3:[3]

I wrote this function that returns a Dialog in a separate file :

  Future myDialog({required BuildContext dialogContext, required Function function}){
      return showDialog(
          context: dialogContext,
          builder: (BuildContext context) {
            return SimpleDialog(

              backgroundColor: Colors.deepPurple[900],
              titleTextStyle: const TextStyle(
                  color: Colors.red, fontSize: 18),
              children: [
               ElevatedButton(
                    onPressed: () {
                      function();
                    },
                    child: const Text("tab")
                ),
              ],
            );
          },
        );
      }

then I made 2 functions, the first one just print (Hello first function) and the second function print (Hello Second function) and set the state and rebuild the widget tree

I made 2 TextButton: the first TextButton call the myDialog function and pass the firstFunction as a parameter, the second TextButton call the myDialog function and pass the secondFunction as a parameter :

the code :

class ExampleState  extends State<Example > {

    void firstFunction(){
        print('Hello first function');
      }

      void secondFunction(){
        setState(() {
          print('Hello Second function');
        });

      }

      @override
      Widget build(BuildContext context) {
        print('build');
        return Scaffold(
          appBar: AppBar(

            title: Text(widget.title),
          ),
          body: Center(
            child: Column(
              children: [
                TextButton(
                    onPressed: (){
                      myDialog(dialogContext: context, function: firstFunction );
                    },
                    child: Text('First Dialog')
                ),
                TextButton(
                    onPressed: (){
                      myDialog(dialogContext: context, function: secondFunction );
                    },
                    child: Text('Second Dialog')
                )
              ],
            ),
          )
        );
      }
    }

notice that I added a print('build'); so I can know when it rebuild the widget tree

The result:

enter image description here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Ma Jeed
Solution 2 h8moss
Solution 3 Ma Jeed