123

As I'm learning Flutter I've come to navigation. I want to pass data between screens similarly to passing data between Activities in Android and passing data between View Controllers in iOS. How do I do it in Flutter?

Related questions:

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
  • You may use a library implementing the `Redux` framework in Flutter. For example, `flutter_flux` implements a uni-directional data flow pattern comprised of `Actions`, `Stores` and `StoreWatchers` (https://pub.dartlang.org/packages/flutter_flux). Provides a good framework to control app state and also to pass data around – Kartik Dec 20 '18 at 06:40
  • The last link is actually a duplicate of https://stackoverflow.com/questions/46057353/controlling-state-from-outside-of-a-statefulwidget/51460832 – Rémi Rousselet Dec 20 '18 at 09:50
  • And so does the second link actually – Rémi Rousselet Dec 20 '18 at 09:50
  • TODO add an answer for the provider package – Suragch Jun 24 '19 at 22:34
  • Sadly none of the answer helps with realtime streams when using StreamProvider and Provider.of() after Navigator.push(), it just isn't realtime anymore. – filip Oct 23 '19 at 11:59
  • https://stackoverflow.com/questions/58497893/whats-the-best-way-to-get-firebase-data-in-realtime-after-navigator-push @Suragch – filip Oct 23 '19 at 13:57

13 Answers13

259

This answer will cover both passing data forward and passing data back. Unlike Android Activities and iOS ViewControllers, different screens in Flutter are just widgets. Navigating between them involves creating something called a route and using the Navigator to push and pop the routes on and off the stack.

Passing data forward to the next screen

enter image description here

To send data to the next screen you do the following things:

  1. Make the SecondScreen constructor take a parameter for the type of data that you want to send to it. In this particular example, the data is defined to be a String value and is set here with this.text.

    class SecondScreen extends StatelessWidget {
      final String text;
      SecondScreen({Key key, @required this.text}) : super(key: key);
    
      ...
    
  2. Then use the Navigator in the FirstScreen widget to push a route to the SecondScreen widget. You put the data that you want to send as a parameter in its constructor.

    Navigator.push(
        context,
        MaterialPageRoute(
          builder: (context) => SecondScreen(text: 'Hello',),
        ));
    

The full code for main.dart is here:

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    title: 'Flutter',
    home: FirstScreen(),
  ));
}

class FirstScreen extends StatefulWidget {
  @override
  _FirstScreenState createState() {
    return _FirstScreenState();
  }
}

class _FirstScreenState extends State<FirstScreen> {

  // this allows us to access the TextField text
  TextEditingController textFieldController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('First screen')),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [

          Padding(
            padding: const EdgeInsets.all(32.0),
            child: TextField(
              controller: textFieldController,
              style: TextStyle(
                fontSize: 24,
                color: Colors.black,
              ),
            ),
          ),

          RaisedButton(
            child: Text(
              'Go to second screen',
              style: TextStyle(fontSize: 24),
            ),
            onPressed: () {
              _sendDataToSecondScreen(context);
            },
          )

        ],
      ),
    );
  }

  // get the text in the TextField and start the Second Screen
  void _sendDataToSecondScreen(BuildContext context) {
    String textToSend = textFieldController.text;
    Navigator.push(
        context,
        MaterialPageRoute(
          builder: (context) => SecondScreen(text: textToSend,),
        ));
  }
}

class SecondScreen extends StatelessWidget {
  final String text;

  // receive data from the FirstScreen as a parameter
  SecondScreen({Key key, @required this.text}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Second screen')),
      body: Center(
        child: Text(
          text,
          style: TextStyle(fontSize: 24),
        ),
      ),
    );
  }
}

Passing data back to the previous screen

enter image description here

When passing data back you need to do the following things:

  1. In the FirstScreen, use the Navigator to push (start) the SecondScreen in an async method and wait for the result that it will return when it finishes.

    final result = await Navigator.push(
        context,
        MaterialPageRoute(
          builder: (context) => SecondScreen(),
        ));
    
  2. In the SecondScreen, include the data that you want to pass back as a parameter when you pop the Navigator.

    Navigator.pop(context, 'Hello');
    
  3. Then in the FirstScreen the await will finish and you can use the result.

    setState(() {
      text = result;
    });
    

Here is the complete code for main.dart for your reference.

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    title: 'Flutter',
    home: FirstScreen(),
  ));
}

class FirstScreen extends StatefulWidget {
  @override
  _FirstScreenState createState() {
    return _FirstScreenState();
  }
}

class _FirstScreenState extends State<FirstScreen> {

  String text = 'Text';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('First screen')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [

            Padding(
              padding: const EdgeInsets.all(32.0),
              child: Text(
                text,
                style: TextStyle(fontSize: 24),
              ),
            ),

            RaisedButton(
              child: Text(
                'Go to second screen',
                style: TextStyle(fontSize: 24),
              ),
              onPressed: () {
                _awaitReturnValueFromSecondScreen(context);
              },
            )

          ],
        ),
      ),
    );
  }

  void _awaitReturnValueFromSecondScreen(BuildContext context) async {

    // start the SecondScreen and wait for it to finish with a result
    final result = await Navigator.push(
        context,
        MaterialPageRoute(
          builder: (context) => SecondScreen(),
        ));

    // after the SecondScreen result comes back update the Text widget with it
    setState(() {
      text = result;
    });
  }
}

class SecondScreen extends StatefulWidget {
  @override
  _SecondScreenState createState() {
    return _SecondScreenState();
  }
}

class _SecondScreenState extends State<SecondScreen> {
  // this allows us to access the TextField text
  TextEditingController textFieldController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Second screen')),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [

          Padding(
            padding: const EdgeInsets.all(32.0),
            child: TextField(
              controller: textFieldController,
              style: TextStyle(
                fontSize: 24,
                color: Colors.black,
              ),
            ),
          ),

          RaisedButton(
            child: Text(
              'Send text back',
              style: TextStyle(fontSize: 24),
            ),
            onPressed: () {
              _sendDataBack(context);
            },
          )

        ],
      ),
    );
  }

  // get the text in the TextField and send it back to the FirstScreen
  void _sendDataBack(BuildContext context) {
    String textToSendBack = textFieldController.text;
    Navigator.pop(context, textToSendBack);
  }
}
Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
  • 8
    How can I do this if my second page is a stateful widget to? I can't call setState in the constructor... – MrLalatg May 18 '19 at 23:28
  • 7
    @MrLalatg use "widget.$variable_name" to get the data if you're using a stateful widget. String tempText = widget.text – Nagaraj Alagusundaram May 27 '19 at 23:06
  • Is it okay to send arbitrary large amount of data like this? I am aware that in Android, passing data via "Activities" should be kept minimal. – user3245268 Sep 19 '19 at 19:37
  • 2
    @user3245268, I'm not aware of any restriction on the amount of data. I would assume, though, that if it is an extremely large amount of data then you would just query the database or something from the second screen rather than pass it from the first screen. – Suragch Sep 20 '19 at 01:52
  • 4
    If user presses back button of mobile device instead of pressing 'RaisedButton' button then how can we pass and send back textFieldController.text to first screen ? please suggest. – Kamlesh Aug 25 '20 at 08:53
  • I was stuck at passing data to previous screen using callback. This came to the rescue. – Sagar V Jan 29 '21 at 08:58
  • And what if the screen we want to navigate is dynamic and we don't know about it's name we just know about the params – Ario Feb 10 '22 at 10:49
  • 1
    @coditori, I haven't had to implement that particular use case, but check out the go_router package. That's what I'm starting to use now for navigation. – Suragch Feb 15 '22 at 23:44
  • How to do this with map string dynamic data? – Jessen Jie Apr 30 '23 at 11:30
  • @JessenJie, You can change the `String` type in the example here to `Map`. – Suragch May 02 '23 at 07:34
42

This solution is very easy by passing variables in constructor:

first page:

Navigator.of(context).push(MaterialPageRoute(builder:(context)=>SecondPage('something')));

second page:

class SecondPage extends StatefulWidget {
  String something;
  SecondPage(this.something);
  @override
  State<StatefulWidget> createState() {
    return SecondPageState(this.something);
  }
}
class SecondPageState extends State<SecondPage> {
  String something;
  SecondPageState(this.something);
  @override
  Widget build(BuildContext context) {
   return Scaffold(
    appBar: AppBar(
    //now you have passing variable
    title: Text(something),
   ),
   ...
  }
CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
Mahmoud Salah Eldin
  • 1,739
  • 16
  • 21
  • 16
    you dont need to pass data to state here ```return SecondPageState(this.something);``` you can access data in SecondPageState as ```widget.something``` directly without passing to state; – Mahesh Jamdade Nov 11 '19 at 07:50
40

Get Perfect Solution :

  1. From 1st Screen navigate to others as:

    Navigator.pushNamed(context, "second",arguments: {"name" : 
      "Bijendra", "rollNo": 65210});
    },
    
  2. On Second Screen in build method get as :

    @override
    Widget build(BuildContext context) {
        final  Map<String, Object>rcvdData = ModalRoute.of(context).settings.arguments;
        print("rcvd fdata ${rcvdData['name']}");
        print("rcvd fdata ${rcvdData}");
    
        return Scaffold(appBar: AppBar(title: Text("Second")),
          body: Container(child: Column(children: <Widget>[
          Text("Second"),
        ],),),);
    
    }
    
Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
Bijendra Singh
  • 617
  • 6
  • 4
34

Easiest way

FirstPage.dart

 Navigator.push(
          context,
          MaterialPageRoute(
              builder: (context) => PasswordRoute(usernameController)));

//usernameController is String value,If you want to pass multiple values add all

SecondPage.dart

class PasswordRoute extends StatefulWidget {
  final String usernameController;//if you have multiple values add here
PasswordRoute(this.usernameController, {Key key}): super(key: key);//add also..example this.abc,this...

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

class _PasswordPageState extends State<PasswordRoute> {
 @override
  Widget build(BuildContext context) {
...child: Text(widget.usernameController);
}
}
  • 1
    widget.usernameController worked for me, but not just usernameController, as @Suragch said, I don't know if that is because of a different method or a newer flutter version – Ian Ronk Feb 22 '20 at 13:57
  • If I refer to `usernameController` in the `_PasswordPageState` class, I receive the error: `usernameController not defined`. If I use `widget.usernameController`, this works perfectly. – DaReal Aug 05 '20 at 19:44
17

Answers above are useful for a small app, but if you want to remove the headache of continuously worrying about a widgets state, Google presented the Provider package. https://pub.dev/packages/provider

Have a look into that one, or watch these videos from Andrea Bizzotto: https://www.youtube.com/watch?v=MkFjtCov62g // Provider: The Essential Guide https://www.youtube.com/watch?v=O71rYKcxUgA&t=258s // Provider: Introduction

Learn how to use the Provider package, and you are set for life :)

serg
  • 178
  • 1
  • 5
5

First Screen : //send data to second screen

 Navigator.push(context, MaterialPageRoute(builder: (context) {
                          return WelcomeUser(usernameController.text);

                          }));

Second Screen : //fetch data from first screen

 final String username;
  WelcomeUser(this.username);

//use data to display

body: Container(
    child: Center(
      child: Text("Welcome "+widget.username,
      textAlign: TextAlign.center,
      ),
    ),
  ),
Varsha
  • 51
  • 1
  • 1
4

Navigators in Flutter are similar to the Intent in Android. There are two classes we are dealing with FirstScreen and SecondScreen.

In order to pass the data between the first screen to second do the following: First of all add parameter in the SecondScreen class constructor

Now in the FirstScreen class provide the parameter

Navigator.push(context, MaterialPageRoute(builder: (context)=>SecondScreen(key_name:"Desired Data"));

So in the above line the "key_name" is the name of the parameter given in the SecondScreen class. The "Desired Data" is data should be passed through the key to the SecondScreen class.

That's it you are done!!!

Hasan A Yousef
  • 22,789
  • 24
  • 132
  • 203
Prajwal Shinde
  • 195
  • 1
  • 6
3

Passing Data to back screen flutter

  1. Home Page

    import 'package:flutter/material.dart';
    import 'package:flutter/src/widgets/container.dart';
    import 'package:flutter/src/widgets/framework.dart';
    import 'package:sqflite_offline/View/Add_data.dart';
    
     class HomeScreen extends StatefulWidget {
     const HomeScreen({super.key});
    
      @override
       State<HomeScreen> createState() => _HomeScreenState();
       }
    
       class _HomeScreenState extends State<HomeScreen> {
        List<Method> items = []; // => List of items that come form next page.
       @override
      Widget build(BuildContext context) {
    return Scaffold(
    appBar: AppBar(
      title: Text("Hello"),
    ),
    floatingActionButton: FloatingActionButton(
      onPressed: () {
        Navigator.of(context)
            .push<Method>(MaterialPageRoute(builder: (_) => AddData()))
            // fetching data form next page.
            .then((value) => setState(() {
                  if (value?.title_Ctr != "" && value?.desc_Ctr != "") {
                    items.add(Method(
                        title_Ctr: value!.title_Ctr,
                        desc_Ctr: value.desc_Ctr));
                  }
                }));
      },
      child: Icon(Icons.add),
    ),
    body: items.isNotEmpty
        ? Column(children: [
            Expanded(
                child: ListView.builder(
                    itemCount: items.length,
                    itemBuilder: ((context, index) {
                      return Container(
                        margin:
                            EdgeInsets.only(top: 10, left: 10, right: 10),
                        padding: EdgeInsets.only(left: 10, right: 10),
                        height: 80,
                        decoration: BoxDecoration(
                            color: Colors.pinkAccent,
                            borderRadius: BorderRadius.circular(10)),
                        child: Center(
                          child: ListTile(
                            title: Text(items[index].title_Ctr),
                            subtitle: Text(items[index].desc_Ctr),
                            leading: Icon(Icons.emoji_people),
                          ),
                        ),
                      );
                    })))
          ])
        : Center(
            child: Text("No Record Found"),
            ));
         }
        } 
    
  2. Add List Page

     import 'package:flutter/material.dart';
     import 'package:flutter/src/widgets/container.dart';
     import 'package:flutter/src/widgets/framework.dart';
    
         class AddData extends StatefulWidget {
         const AddData({super.key});
    
            @override
            State<AddData> createState() => _AddDataState();
      }
    
     // Creating a Class and constructor.
     class Method {
     late String title_Ctr;
      late String desc_Ctr;
      Method({required this.title_Ctr, required this.desc_Ctr});
               }
    
        class _AddDataState extends State<AddData> {
       // Creating a TextEditingController for two Fiends,
        //one is for title TextField and second is for Description TextField.
        TextEditingController titleCtr = TextEditingController();
        TextEditingController descCtr = TextEditingController();
    
       // Creating a Method for Passing a data to back page.
       OnPressed(BuildContext context) {
         var data = Method(title_Ctr: titleCtr.text, desc_Ctr: descCtr.text);
         Navigator.pop(context, data);
           }
    
           @override
              Widget build(BuildContext context) {
                return Scaffold(
              appBar: AppBar(title: Text("Add Data")),
                 body: Form(child: Builder(builder: (context) {
                  return Column(children: [
                    TextFormField(
                      controller: titleCtr,
          decoration: InputDecoration(hintText: "title"),
          validator: (value) {
            var newValue = value ?? "";
            if (newValue.isEmpty) {
              return 'title is Required';
            }
            return null;
          },
        ),
        TextFormField(
          controller: descCtr,
          decoration: InputDecoration(hintText: "Description"),
          validator: (value) {
            var newValue = value ?? "";
            if (newValue.isEmpty) {
              return 'Discription is Required';
            }
            return null;
          },
        ),
        MaterialButton(
          color: Colors.red,
          onPressed: () {
            if (Form.of(context)?.validate() ?? false) {
              OnPressed(context);
            }
          },
          child: Text("Save"),
        )
      ]);
    })));
       }
       }
    
  3. screenshot

enter image description here

  • Imagine have to read whole project to understand a small concept. Can you someone summerize this big code – jknair0 Jan 03 '23 at 13:10
1

1) From where you want to push :

onPressed: () async {
                        await Navigator.pushNamed(context, '/edit',
                            arguments: userData);
                        setState(() {
                          userData = userData;
                        });}

2) From Where you want to pop :

    void updateData() async{
    WorldTime instance = locations;
    await instance.getData();
    Navigator.pop(context, userData);
  }
Anil Kumar
  • 1,830
  • 15
  • 24
0

If you use get package then try this . passing data with get package

check get package package link

Jewel Rana
  • 2,397
  • 1
  • 19
  • 28
0

Here's another approach.

Nothing wrong with the other answers. I've tried all of the methods mentioned using global wide widgets like provider, third-party solutions, Navigator arguments, etc. This approach differs by allowing one to chain calls and pass precise data of any type required to the widget using it. We can also gain access to a completion handler event and can use this technique without being constrained to Navigator objects.

Here's the tldr:

tldr; We have to turn our thinking on its head a bit. Data can be passed to the called widget when you navigate to it by using final arguments with default values in the destination widget. Using an optional function you can get data back from the 'child' (destination) widget.

The complete explanation can be found using this SO answer., (Gist)

Tommie C.
  • 12,895
  • 5
  • 82
  • 100
0

I just want to be here to help that 1% who might go through what I did Lol

Don't forget to put an "await" infront of "Navigator.push" in the first page, otherwise no data will be returned to the first page when you pop from the second page...

Dharman
  • 30,962
  • 25
  • 85
  • 135
Manbus
  • 706
  • 4
  • 9
0

Passing Data to back screen flutter

First Screen

final result = await Navigator.of(context).push(MaterialPageRoute(builder: (context)=>const PaymentScreen()));
                          

Second Screen

String selected = "Credit/Debit";
Navigator.pop(context,selected);