Flutter supports several ways on how to send data to another screen resp. widget
Using Constructors
Using Named Routes and RouteSettings
Using Navigator.pushNamed with arguments
Using InheritedWidget
Using Provider as state management solution
Using GlobalKeys
To share data between screens, use Navigator.push() together with MaterialPageRoute and pass data via the constructor or use RouteSettings.
You need to distinguish between (a) a “normal” flow of data between equally important screens or (b) between a primary screen and a secondary screen (e.g. a selection screen that supports the primary screen).
Passing data between primary screens can be realized via the standard route operations push() and pop() together with the data as constructor arguments.
In order to retrieve data from a secondary (supplemental) screen you need to use a Future and call the second screen asynchronously using finalresultawaitNavigator.push(...).
Data can be passed as arguments to other screens/pages (widgets) when they are created using the MaterialPageRoute class in the Navigator.push() method.
import'package:flutter/material.dart';classTodo{finalStringtitle;finalStringdescription;constTodo(this.title,this.description);}voidmain(){runApp(MaterialApp(title:'Passing Data',home:TodosScreen(todos:List.generate(20,(i)=>Todo('Todo $i','A description of what needs to be done for Todo $i',),),),),);}classTodosScreenextendsStatelessWidget{constTodosScreen({super.key,requiredthis.todos});finalList<Todo>todos;@overrideWidgetbuild(BuildContextcontext){returnScaffold(appBar:AppBar(title:constText('Todos'),),body:ListView.builder(itemCount:todos.length,itemBuilder:(context,index){returnListTile(title:Text(todos[index].title),// When a user taps the ListTile, navigate to the DetailScreen.// Notice that you're not only creating a DetailScreen, you're// also passing the current todo through to it.onTap:(){Navigator.push(context,MaterialPageRoute(builder:(context)=>DetailScreen(todo:todos[index]),),);},);},),);}}classDetailScreenextendsStatelessWidget{// In the constructor, require a Todo.constDetailScreen({super.key,requiredthis.todo});// Declare a field that holds the Todo.finalTodotodo;@overrideWidgetbuild(BuildContextcontext){// Use the Todo to create the UI.returnScaffold(appBar:AppBar(title:Text(todo.title),),body:Padding(padding:constEdgeInsets.all(16),child:Text(todo.description),),);}}
ListView.builder(itemCount:todos.length,itemBuilder:(context,index){returnListTile(title:Text(todos[index].title),// When a user taps the ListTile, navigate to the DetailScreen.// Notice that you're not only creating a DetailScreen, you're// also passing the current todo through to it.onTap:(){Navigator.push(context,MaterialPageRoute(builder:(context)=>constDetailScreen(),// Pass the arguments as part of the RouteSettings. The// DetailScreen reads the arguments from these settings.settings:RouteSettings(arguments:todos[index],),),);},);},)
classDetailScreenextendsStatelessWidget{constDetailScreen({super.key});@overrideWidgetbuild(BuildContextcontext){finaltodo=ModalRoute.of(context)!.settings.argumentsasTodo;// Use the Todo to create the UI.returnScaffold(appBar:AppBar(title:Text(todo.title),),body:Padding(padding:constEdgeInsets.all(16),child:Text(todo.description),),);}}
In some cases, you might want to return data from a new screen.
For example, say you push a new screen that presents two options to a user. When the user taps an option, you want to inform the first screen of the user’s selection so that it can act on that information.
You can do this with the Navigator.pop() method using the following steps:
Define the home screen
Add a button that launches the selection screen
Show the selection screen with two buttons
When a button is tapped, close the selection screen
Show a snackbar on the home screen with the selection
import'package:flutter/material.dart';voidmain(){runApp(constMaterialApp(title:'Returning Data',home:HomeScreen(),),);}classHomeScreenextendsStatelessWidget{constHomeScreen({super.key});@overrideWidgetbuild(BuildContextcontext){returnScaffold(appBar:AppBar(title:constText('Returning Data Demo'),),body:constCenter(child:SelectionButton(),),);}}classSelectionButtonextendsStatefulWidget{constSelectionButton({super.key});@overrideState<SelectionButton>createState()=>_SelectionButtonState();}class_SelectionButtonStateextendsState<SelectionButton>{@overrideWidgetbuild(BuildContextcontext){returnElevatedButton(onPressed:(){_navigateAndDisplaySelection(context);},child:constText('Pick an option, any option!'),);}// A method that launches the SelectionScreen and awaits the result from// Navigator.pop.Future<void>_navigateAndDisplaySelection(BuildContextcontext)async{// Navigator.push returns a Future that completes after calling// Navigator.pop on the Selection Screen.finalresult=awaitNavigator.push(context,MaterialPageRoute(builder:(context)=>constSelectionScreen()),);// When a BuildContext is used from a StatefulWidget, the mounted property// must be checked after an asynchronous gap.if(!context.mounted)return;// After the Selection Screen returns a result, hide any previous snackbars// and show the new result.ScaffoldMessenger.of(context)..removeCurrentSnackBar()..showSnackBar(SnackBar(content:Text('$result')));}}classSelectionScreenextendsStatelessWidget{constSelectionScreen({super.key});@overrideWidgetbuild(BuildContextcontext){returnScaffold(appBar:AppBar(title:constText('Pick an option'),),body:Center(child:Column(mainAxisAlignment:MainAxisAlignment.center,children:<Widget>[Padding(padding:constEdgeInsets.all(8),child:ElevatedButton(onPressed:(){// Close the screen and return "Yep!" as the result.Navigator.pop(context,'Yep!');},child:constText('Yep!'),),),Padding(padding:constEdgeInsets.all(8),child:ElevatedButton(onPressed:(){// Close the screen and return "Nope." as the result.Navigator.pop(context,'Nope.');},child:constText('Nope.'),),)],),),);}}
classFirstScreenextendsStatelessWidget{@overrideWidgetbuild(BuildContextcontext){returnScaffold(body:Center(child:ElevatedButton(onPressed:(){Navigator.push(context,MaterialPageRoute(builder:(context)=>SecondScreen(data:'Hello from FirstScreen'),),);},child:Text('Go to Second Screen'),),),);}}classSecondScreenextendsStatelessWidget{finalStringdata;SecondScreen({requiredthis.data});@overrideWidgetbuild(BuildContextcontext){returnScaffold(body:Center(child:Text(data),),);}}
voidmain(){runApp(MaterialApp(routes:{'/second':(context)=>SecondScreen(),},));}Navigator.pushNamed(context,'/second',arguments:'Hello from FirstScreen',);classSecondScreenextendsStatelessWidget{@overrideWidgetbuild(BuildContextcontext){finalargs=ModalRoute.of(context)!.settings.argumentsasString;returnScaffold(body:Center(child:Text(args),),);}}
Navigator.pushNamed(context,'/second',arguments:{'data':'Hello from FirstScreen'},);classSecondScreenextendsStatelessWidget{@overrideWidgetbuild(BuildContextcontext){finalMap<String,dynamic>args=ModalRoute.of(context)!.settings.argumentsasMap<String,dynamic>;returnScaffold(body:Center(child:Text(args['data']),),);}}
classMyInheritedWidgetextendsInheritedWidget{finalStringdata;MyInheritedWidget({requiredthis.data,requiredWidgetchild}):super(child:child);@overrideboolupdateShouldNotify(covariantMyInheritedWidgetoldWidget){returnoldWidget.data!=data;}staticMyInheritedWidget?of(BuildContextcontext){returncontext.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();}}classParentWidgetextendsStatelessWidget{@overrideWidgetbuild(BuildContextcontext){returnMyInheritedWidget(data:'Hello from InheritedWidget',child:ChildWidget(),);}}classChildWidgetextendsStatelessWidget{@overrideWidgetbuild(BuildContextcontext){finalmyInheritedWidget=MyInheritedWidget.of(context);returnText(myInheritedWidget!.data);}}
classDataProviderwithChangeNotifier{String_data='Hello from Provider';Stringgetdata=>_data;voidupdateData(StringnewData){_data=newData;notifyListeners();}}voidmain(){runApp(ChangeNotifierProvider(create:(context)=>DataProvider(),child:MyApp(),),);}classFirstScreenextendsStatelessWidget{@overrideWidgetbuild(BuildContextcontext){returnScaffold(body:Center(child:ElevatedButton(onPressed:(){Provider.of<DataProvider>(context,listen:false).updateData('Updated Data');Navigator.push(context,MaterialPageRoute(builder:(context)=>SecondScreen()),);},child:Text('Go to Second Screen'),),),);}}classSecondScreenextendsStatelessWidget{@overrideWidgetbuild(BuildContextcontext){finaldata=Provider.of<DataProvider>(context).data;returnScaffold(body:Center(child:Text(data),),);}}
finalGlobalKey<SecondScreenState>secondScreenKey=GlobalKey<SecondScreenState>();Navigator.push(context,MaterialPageRoute(builder:(context)=>SecondScreen(key:secondScreenKey)),);classSecondScreenextendsStatefulWidget{SecondScreen({Key?key}):super(key:key);@overrideSecondScreenStatecreateState()=>SecondScreenState();}classSecondScreenStateextendsState<SecondScreen>{Stringdata='Initial Data';voidupdateData(StringnewData){setState((){data=newData;});}@overrideWidgetbuild(BuildContextcontext){returnScaffold(body:Center(child:Text(data),),);}}// Update data from the first screensecondScreenKey.currentState?.updateData('Updated Data');