Setting variable as navigator route flutter

I am building an app where I am trying to retrieve data from calling navigator.push

onTap: () async {
    widget.dogData = await Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => AddDogScreen(),
      ),
    );

    widget.setDocId(widget.dogData['docId']);
    setState(() {
      widget.dogData;
    });
  },

However, I want to link to yet another screen after AddDogScreen and pass the data by popping from that screen.

SO we would have :

Initial screen -> AddDogScreen -> Another screen (then pop back to initial screen with the data)

Then the third screen should pop back to the initial screen with the data. How can I set this up, since I want to get the data from two screens?

Thanks a lot in advance.


To do what you want to do there are two useful features in Flutter navigation:

  1. final value = await Navigator.push() returns the value that the pushed screen returned, and you can await it to wait for the returning of the next screen.
  2. Navigator.pop(value) allows you to return a value from a screen, while going back.

All you have to do here is play with those to do what you want. Here is an example of the type of flow that you want:

class InitialScreen extends StatelessWidget {
  onPressed(BuildContext context) async {
    final List<String>? addDogScreenResult = await Navigator.of(context).push<
            List<String>>(
        MaterialPageRoute<List<String>>(builder: (context) => AddDogScreen()));
    // Do something with the result
    print("We got the values: ${addDogScreenResult}");
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Center(
            child: ElevatedButton(
                onPressed: () => onPressed(context),
                child: const Text("Click to go to AddDogScreen"))));
  }
}

class AddDogScreen extends StatelessWidget {
  onPressed(BuildContext context) async {
    final String? anotherScreenResult = await Navigator.of(context)
        .push<String>(MaterialPageRoute(builder: (context) => AnotherScreen()));
    if (anotherScreenResult != null) {
      const addDogScreenString = "Some value AddDogScreen generates";

      // We can combine a value that we got in this screen and the return value of AnotherScreen.
      final List<String> addDogScreenReturnValue = [
        addDogScreenString,
        anotherScreenResult
      ];

      // We simply return the list using .pop() function
      // Notice that we can type the .pop() function with the return type that we give it. It is not absolutely necessary, but it adds in clarity and safety.
      Navigator.of(context).pop<List<String>>(addDogScreenReturnValue);
      return;
    }
    Navigator.of(context).pop();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Center(
            child: ElevatedButton(
                onPressed: () => onPressed(context),
                child: const Text("Click to go to AnotherScreen"))));
  }
}

class AnotherScreen extends StatelessWidget {
  onPressed(BuildContext context) async {
    const String anotherScreenResult = "Some value";
    Navigator.of(context).pop<String>(anotherScreenResult);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Center(
            child: ElevatedButton(
                onPressed: () => onPressed(context),
                child: const Text("Click to go back to initial page"))));
  }
}

Also, I see in your code widget.dogData = .... Maybe it's on purpose but if not, this is not how you should manipulate state. State variables should be defined in the class that extends your StatefulWidget, and you should be able to read it using dogData, and update it using:

setState({
  dogData = 'Something';
})