Flutter - Get data using pushName - flutter

I´m trying to send data with pushName. Then i try to get this data to show in a Toast message.
PushName
Navigator.pushNamed(
context,
'/navigator',
arguments: <String, String>{
'instalation': widget.instalation,
'message': DemoLocalizations.of(context)
.text('cancel-message') +
" " +
widget.datameterValue.toString(),
},
);
Trying to retrieve data
class Navigation extends StatefulWidget {
final ConnectionPage args;
Navigation({Key key, this.message, this.instalation, this.args}) : super(key: key);
}
class _NavigationState extends State<Navigation> {
void initState() {
super.initState();
print(widget.args); //NULL
final snackBar = SnackBar(
duration: Duration(seconds: 5),
content: Text(widget.args.messsage+ '.', textAlign: TextAlign.center),
backgroundColor: Colors.red[700],
);
key.currentState.showSnackBar(snackBar);
}
}
The problem: Return null.
So: What is the right way to get data using pushName? In the documentation show how can we get data inside Scaffold but i need to get data in the initState.
UPDATE
Routes
routes: {
'/login': (context) => LoginPage(),
'/navigator': (context) => Navigation(),
'/home': (context) => HomePageScreen(),
'/connect': (context) => ConnectionPage(),
},
UPDATE 2
I try something like this
Navigator.pushNamed(
context,
'/navigator',
arguments: Navigation(
instalation: widget.instalation,
message: DemoLocalizations.of(context)
.text('cancel-message') +
" " +
widget.datameterValue.toString(),
),
);

To do this in initState You need WidgetsBinding.instance.addPostFrameCallback and ModalRoute.of(context).settings.arguments
Demo pass arguments: {'instalation': "123", "message": "456"}
You can see full code and working demo picture below
code snippet use push
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ExtractArgumentsScreen(),
// Pass the arguments as part of the RouteSettings. The
// ExtractArgumentScreen reads the arguments from these
// settings.
settings: RouteSettings(
arguments: {'instalation': "123", "message": "456"},
),
),
);
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
final routeArgs1 =
ModalRoute.of(context).settings.arguments as Map<String, String>;
final instalation = routeArgs1['instalation'];
final message = routeArgs1['message'];
print('instalation ${instalation}');
print('message ${message}');
key.currentState
.showSnackBar(SnackBar(content: Text(message)));
});
}
code snippet use Navigator.pushNamed
return MaterialApp(
// Provide a function to handle named routes. Use this function to
// identify the named route being pushed, and create the correct
// Screen.
routes: {
'/extractArguments': (context) => ExtractArgumentsScreen(),
},
...
Navigator.pushNamed(
context,
ExtractArgumentsScreen.routeName,
arguments: {'instalation': "123", "message": "456"},
);
working demo
full code
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
// Provide a function to handle named routes. Use this function to
// identify the named route being pushed, and create the correct
// Screen.
routes: {
'/extractArguments': (context) => ExtractArgumentsScreen(),
},
onGenerateRoute: (settings) {
// If you push the PassArguments route
if (settings.name == PassArgumentsScreen.routeName) {
// Cast the arguments to the correct type: ScreenArguments.
final ScreenArguments args = settings.arguments;
// Then, extract the required data from the arguments and
// pass the data to the correct screen.
return MaterialPageRoute(
builder: (context) {
return PassArgumentsScreen(
title: args.title,
message: args.message,
);
},
);
}
},
title: 'Navigation with Arguments',
home: HomeScreen(),
);
}
}
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home Screen'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// A button that navigates to a named route that. The named route
// extracts the arguments by itself.
RaisedButton(
child: Text("Navigate to screen that extracts arguments"),
onPressed: () {
// When the user taps the button, navigate to the specific route
// and provide the arguments as part of the RouteSettings.
Navigator.pushNamed(
context,
ExtractArgumentsScreen.routeName,
arguments: {'instalation': "123", "message": "456"},
);
/*Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ExtractArgumentsScreen(),
// Pass the arguments as part of the RouteSettings. The
// ExtractArgumentScreen reads the arguments from these
// settings.
settings: RouteSettings(
arguments: {'instalation': "123", "message": "456"},
),
),
);*/
},
),
// A button that navigates to a named route. For this route, extract
// the arguments in the onGenerateRoute function and pass them
// to the screen.
RaisedButton(
child: Text("Navigate to a named that accepts arguments"),
onPressed: () {
// When the user taps the button, navigate to a named route
// and provide the arguments as an optional parameter.
Navigator.pushNamed(
context,
PassArgumentsScreen.routeName,
arguments: ScreenArguments(
'Accept Arguments Screen',
'This message is extracted in the onGenerateRoute function.',
),
);
},
),
],
),
),
);
}
}
// A Widget that extracts the necessary arguments from the ModalRoute.
class ExtractArgumentsScreen extends StatefulWidget {
static const routeName = '/extractArguments';
#override
_ExtractArgumentsScreenState createState() => _ExtractArgumentsScreenState();
}
class _ExtractArgumentsScreenState extends State<ExtractArgumentsScreen> {
final GlobalKey<ScaffoldState> key = new GlobalKey<ScaffoldState>();
final snackBar = SnackBar(
duration: Duration(seconds: 5),
content: Text("message" + '.', textAlign: TextAlign.center),
backgroundColor: Colors.red[700],
);
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
final routeArgs1 =
ModalRoute.of(context).settings.arguments as Map<String, String>;
final instalation = routeArgs1['instalation'];
final message = routeArgs1['message'];
print('instalation ${instalation}');
print('message ${message}');
key.currentState
.showSnackBar(SnackBar(content: Text(message)));
});
}
#override
void didChangeDependencies() {
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
// Extract the arguments from the current ModalRoute settings and cast
// them as ScreenArguments.
final routeArgs =
ModalRoute.of(context).settings.arguments as Map<String, String>;
final instalation = routeArgs['instalation'];
final message = routeArgs['message'];
return Scaffold(
key: key,
appBar: AppBar(
title: Text(' ${routeArgs['code']} '),
),
body: Column(
children: <Widget>[
Center(
child: Text('instalation ${instalation}'),
),
RaisedButton(
onPressed: () {
key.currentState.showSnackBar(snackBar);
},
),
],
),
);
}
}
// A Widget that accepts the necessary arguments via the constructor.
class PassArgumentsScreen extends StatelessWidget {
static const routeName = '/passArguments';
final String title;
final String message;
// This Widget accepts the arguments as constructor parameters. It does not
// extract the arguments from the ModalRoute.
//
// The arguments are extracted by the onGenerateRoute function provided to the
// MaterialApp widget.
const PassArgumentsScreen({
Key key,
#required this.title,
#required this.message,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Text(message),
),
);
}
}
// You can pass any object to the arguments parameter. In this example,
// create a class that contains both a customizable title and message.
class ScreenArguments {
final String title;
final String message;
ScreenArguments(this.title, this.message);
}

So, I see you're using the simple routes approach.
In order to extract route arguments you need to supply an onGenerateRoute function to your MaterialApp (or Cupertino, I guess).
You can find an exhaustive example on how to do it here, so I won't crowd this answer more than that.
Hope this solves your problem, happy coding!

Related

How to pass data back from modal in flutter?

So basically I am using the showModalBottomSheet widget to show a full screen container that has a GestureDetector that runs this onTap:
onTap: () {
final String testText = "Sup";
Navigator.of(context).pop(testText);
}
This obviously returns the text when I await the result when I call showModalBottomSheet however, I would also like to set enableDrag: true so that we can swipe the modal away.
My question is:
How can I pass an argument/result back when doing a swipe to dismiss? With a function, I can simple do Navigator.of(context).pop(...) but when we swipe, there is no function and so therefore I can't figure out a way to pass arguments when we swipe to dismiss.
Thank you!
When you swipe the pop() method gets called and there isn't anyway to override it but I figured out a way to handle this scenario:
You can use then() method on showModalBottomSheet() like this:
showModalBottomSheet(context: context, builder: (context) => SecondPage()).then((value) {
str = "done";
print("data: $str");
});
Keep in mind that the value that future returns the value that gets returned in pop() method otherwise it is null.
It looks like showModalBottomSheet doesn't have a way to specify the close value. So it always returns null in that case. And there is not much you can do. But the options I see:
use result wrapper to return value by reference. Like that:
class MyResult {
int myValue;
}
class MyBottomWidget ... {
MyResult result;
MyBottomWidget(this.result);
// then you can initialize the value somewhere
// result.myValue = 5;
}
final result = MyResult();
await showModalBottomSheet(context: context, builder: (_) => MyBottomWidget(result);
// and here you can use your value
print('Result value: ${result.myValue});
another way is to return a value if the result of showModalBottomSheet is null which means a modal has been closed / dissmissed.
final result = await showModalBottomSheet(...);
if (result == null) {
// initialize the value with a value you need when modal is closed.
}
You can make a func wrapper to simplify the process:
Future<T> myShowModalBottomSheet<T>(BuildContext context, WidgetBuilder builder, T dismissValue) async {
final value = await showModalBottomSheet<T>(context: context, builder: builder);
return value ?? dismissValue;
}
or like that:
Future<T> myShowModalBottomSheet<T>(BuildContext context, WidgetBuilder builder, T Function() dismissedValueBuilder) async {
final value = await showModalBottomSheet<T>(context: context, builder: builder);
return value ?? dismissedValueBuilder();
}
the other way is to make your own shoModalBottomSheet that will allow to the specified value. The source code of the function is available so it's not that difficult to implement it. It would be the cleanest solution, but still, it has some downsides as well. First is it's much more to do. The other thing is your solution will be not in sync with the native flutter function. I.e. if flutter will change the behavior of that function or widgets you will need to update your code.
search on pub.dev for a package that will have the functionality you need.
Maybe there is some other way, but I'm not aware of it).
Use this widget and add Navigator. Pop.
->Use this WillPopScope
For More Information Visit Click Here
OR
If you want to pass data in pop. Then see Example:-
import 'package:flutter/material.dart';
void main() {
runApp(
const MaterialApp(
title: 'Returning Data',
home: HomeScreen(),
),
);
}
class HomeScreen extends StatelessWidget {
const HomeScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Returning Data Demo'),
),
body: const Center(
child: SelectionButton(),
),
);
}
}
class SelectionButton extends StatelessWidget {
const SelectionButton({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {
_navigateAndDisplaySelection(context);
},
child: const Text('Pick an option, any option!'),
);
}
// A method that launches the SelectionScreen and awaits the result from
// Navigator.pop.
void _navigateAndDisplaySelection(BuildContext context) async {
// Navigator.push returns a Future that completes after calling
// Navigator.pop on the Selection Screen.
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => const SelectionScreen()),
);
// 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')));
}
}
class SelectionScreen extends StatelessWidget {
const SelectionScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Pick an option'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
onPressed: () {
// Close the screen and return "Yep!" as the result.
Navigator.pop(context, 'Yep!');
},
child: const Text('Yep!'),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
onPressed: () {
// Close the screen and return "Nope." as the result.
Navigator.pop(context, 'Nope.');
},
child: const Text('Nope.'),
),
)
],
),
),
);
}
}
Selection Screen:-
Navigator.pop(context, 'Yep!');
Button Code (Navigator Push Like this):-
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => const SelectionScreen()),
For More Information

How can I navigate between 2 classes, one of them requires passing data? in flutter

I have 2 classes, one of them requires passing data, and class B does not have data for this class, for example, the login class passes the registration data to class A, but class B does not have this data, but it needs access to class A?
i used Navigation.of(context).pushNamed(context, classB.id)
but not work
you can use constructor but in this case, whenever you use this class, you have to provide value, also you can make class value nullable and check it on build time. Another way is passing data by Route.
for more navigate-with-arguments
Here are is example:
Passing data using ModalRoute
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => WidgetA(),
settings: RouteSettings(
arguments: "Data for A",
)),
);
Receive Data
class WidgetA extends StatelessWidget {
static final routeName = "/widgetA";
#override
Widget build(BuildContext context) {
final data = ModalRoute.of(context)!.settings;
late String retriveString;
if (data.arguments == null)
retriveString = "empty";
else
retriveString = data.arguments as String;
return Scaffold(
body: Column(
children: [
Text("Widget A"),
Text("Got data from parent $retriveString"),
],
),
);
}
}
Passing Emptydata using ModalRoute
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => WidgetB(),
),
);
On Receiver side
class WidgetB extends StatelessWidget {
static final routeName = "/widgetB";
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Text("Widget B"),
],
),
);
}
}
Passing data using Constructor
must provide while using widget.
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => WidgetC(data: "for C"),
),
);
Receiver
class WidgetC extends StatelessWidget {
final String data;
const WidgetC({Key? key, required this.data}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [Text("Widget C "), Text("data using Constructor: $data")],
),
);
}
}
Passing data(optional) using Constructor
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => WidgetD(),
),
);
Receiver
class WidgetD extends StatelessWidget {
final String? data;
WidgetD({Key? key, this.data = ""}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Text("Widget D nullable "),
Text("data using Constructor: $data")
],
),
);
}
}

Using Navigator with a bottomNavigationBar

What's the correct way of setting up navigation architecture named routes while using a bottomNavigationBar?
Here's my current setup but I feel there's a better way of doing it:
main.dart:
onGenerateRoute: (settings) {
return MaterialPageRoute(
settings: settings,
builder: (context) {
switch (settings.name) {
case NamedRoutes.splashScreen:
return SplashScreen();
case NamedRoutes.login:
return LoginPage();
case NamedRoutes.mainApp:
return NavigatorSetup();
default:
throw Exception('Invalid route: ${settings.name}');
}
});
navigatorSetup.dart:
IndexedStack(
index: Provider.of<RoutesProvider>(context).selectedViewIndex,
children: [FirstMain(), SecondMain(), ThirdMain(), FourthMain()],
), bottomNavigationBar...
in each main files there is the following setup
class FirstMain extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Navigator(
key: Provider.of<RoutesProvider>(context).homeKey,
onGenerateRoute: (settings) {
return MaterialPageRoute(
settings: settings,
builder: (context) {
switch (settings.name) {
case '/':
case NamedRoutes.mainPage:
return MainPage();
case NamedRoutes.singleMainPage:
return SingleMainPage();
default:
throw Exception('Invalid route: ${settings.name}');
}
},
);
},
);
}
}
Then my routes provider looks like this:
class RoutesProvider extends ChangeNotifier {
int _selectedViewIndex = 0;
get selectedViewIndex => _selectedViewIndex;
set selectedViewIndex(int newIndex) {
_selectedViewIndex = newIndex;
notifyListeners();
}
GlobalKey _mainKey = GlobalKey<NavigatorState>();
GlobalKey _homeKey = GlobalKey();
GlobalKey _secondKey = GlobalKey();
GlobalKey _thirdKey = GlobalKey();
GlobalKey _fourthKey = GlobalKey();
get mainKey => _mainKey;
get homeKey => _homeKey;
get secondKey => _secondKey;
get thirdKey => _thirdKey;
get fourthKey => _fourthKey;
}
The way I'm currently changing routes when on another page of the indexedStack
final RoutesProvider routesProvider = Provider.of<RoutesProvider>(context, listen: false);
final GlobalKey thirdKey = routesProvider.thirdKey;
routesProvider.selectedViewIndex = 2;
Navigator.pushReplacementNamed(thirdKey.currentContext, NamedRoutes.third);
The better way to navigate
Creating a route_generator
import 'package:flutter/material.dart';
import 'package:routing_prep/main.dart';
class RouteGenerator {
static Route<dynamic> generateRoute(RouteSettings settings) {
// Getting arguments passed in while calling Navigator.pushNamed
final args = settings.arguments;
switch (settings.name) {
case '/':
return MaterialPageRoute(builder: (_) => FirstPage());
case SecondPage.routeName:
// Validation of correct data type
if (args is String) {
return MaterialPageRoute(
builder: (_) => SecondPage(
data: args,
),
);
}
// If args is not of the correct type, return an error page.
// You can also throw an exception while in development.
return _errorRoute();
default:
// If there is no such named route in the switch statement, e.g. /third
return _errorRoute();
}
}
static Route<dynamic> _errorRoute() {
return MaterialPageRoute(builder: (_) {
return Scaffold(
appBar: AppBar(
title: Text('Error'),
),
body: Center(
child: Text('ERROR'),
),
);
});
}
}
As you can see, you've moved from having bits of routing logic everywhere around your codebase, to a single place for this logic - in the RouteGenerator. Now, the only navigation code which will remain in your widgets will be the one pushing named routes with a navigator.
Before you can run and test the app, there's still a bit of a setup to do for this RouteGenerator to function.
main.dart
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
...
// Initially display FirstPage
initialRoute: '/',
onGenerateRoute: RouteGenerator.generateRoute,
);
}
}
class FirstPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
...
RaisedButton(
child: Text('Go to second'),
onPressed: () {
// Pushing a named route
Navigator.of(context).pushNamed(
SecondPage.routeName,
arguments: 'Hello there from the first page!',
);
},
)
...
}
}
class SecondPage extends StatelessWidget {
static const routeName = "/second";
// This is a String for the sake of an example.
// You can use any type you want.
final String data;
SecondPage({
Key key,
#required this.data,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Routing App'),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
'Second Page',
style: TextStyle(fontSize: 50),
),
Text(
data,
style: TextStyle(fontSize: 20),
),
],
),
),
);
}
}

How to save last opened screen in flutter app

I am trying to reopen last opened screen after boot, Is there any simple way to do so ? sample codes are welcome !
So far I tried a code(which I got somewhere) with SharedPreferences, but it's not working.
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
String lastRouteKey = 'last_route';
void main() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
String lastRoute = preferences.getString(lastRouteKey);
runApp(MyApp(lastRoute));
}
class MyApp extends StatelessWidget {
final String lastRoute;
MyApp(this.lastRoute);
#override
Widget build(BuildContext context) {
bool hasLastRoute = getWidgetByRouteName(lastRoute) != null;
return MaterialApp(
home: Foo(),
initialRoute: hasLastRoute ? lastRoute : '/',
onGenerateRoute: (RouteSettings route) {
persistLastRoute(route.name);
return MaterialPageRoute(
builder: (context) => getWidgetByRouteName(route.name),
);
},
);
}
Widget getWidgetByRouteName(String routeName) {
switch (routeName) {
case '/':
return MainWidget();
case '/':
return SecondRoute();
// Put all your routes here.
default:
return null;
}
}
void persistLastRoute(String routeName) async {
SharedPreferences preferences = await SharedPreferences.getInstance();
preferences.setString(lastRouteKey, routeName);
}
}
class Foo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Foo'),
),
body: Column(
children: <Widget>[
RaisedButton(
child: Text('Open route second'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),
);
},
),
RaisedButton(
child: Text('Open route main'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => MainWidget()),
);
},
),
],
),
);
}
}
class SecondRoute extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Route"),
),
body: Center(
child: RaisedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Go back!'),
),
),
);
}
}
class MainWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("MainWidget"),
),
body: Center(
child: RaisedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Go back!'),
),
),
);
}
}
should I use SQLite or JSON instead of SharedPreferences to make the code simple? thanks.
Demo
A. Navigation
when we are navigating through different screens within app, actually, the route stacks are changing.
So, firstly, we need to figure out how to listen to this changes e.g Push screen, Pop back to users screen.
1. Attaching saving method in each action button
we can actually put this on every navigation-related button.
a. on drawer items
ListTile(
title: Text("Beta"),
onTap: () {
saveLastScreen(); // saving to SharedPref here
Navigator.of(context).pushNamed('/beta'); // then push
},
),
b. on Titlebar back buttons
appBar: AppBar(
title: Text("Screen"),
leading: IconButton(
icon: Icon(Icons.menu),
onPressed: () {
saveLastScreen(); // saving to SharedPref here
Navigator.pop(context); // then pop
},
),
),
c. and also capturing event of Phone Back button on Android devices
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: (){ // will triggered as we click back button
saveLastScreen(); // saving to SharedPref here
return Future.value(true);
},
child: Scaffold(
appBar: AppBar(
title: Text("Base Screen"),
),
Therefore, we will have more code and it will be harder to manage.
2. Listening on Route Changes using Route observer
Nonetheless, Flutter provides on MaterialApp, that we can have some "middleware" to capture those changes on route stacks.
We may have this on our MyApp widget :
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Save Last Route',
navigatorObservers: <NavigatorObserver>[
MyRouteObserver(), // this will listen all changes
],
routes: {
'/': (context) {
return BaseScreen();
},
'/alpha': (context) {
return ScreenAlpha();
},
We can define MyRouteObserver class as below :
class MyRouteObserver extends RouteObserver {
void saveLastRoute(Route lastRoute) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('last_route', lastRoute.settings.name);
}
#override
void didPop(Route route, Route previousRoute) {
saveLastRoute(previousRoute); // note : take route name in stacks below
super.didPop(route, previousRoute);
}
#override
void didPush(Route route, Route previousRoute) {
saveLastRoute(route); // note : take new route name that just pushed
super.didPush(route, previousRoute);
}
#override
void didRemove(Route route, Route previousRoute) {
saveLastRoute(route);
super.didRemove(route, previousRoute);
}
#override
void didReplace({Route newRoute, Route oldRoute}) {
saveLastRoute(newRoute);
super.didReplace(newRoute: newRoute, oldRoute: oldRoute);
}
}
B. How to Start the App
As users interacting through the screens, the Shared Preferences will always store last route name. To make the app navigate correspondingly, we need to make our BaseScreen statefull and override its initState method as below :
return MaterialApp(
routes: {
'/': (context) {
return BaseScreen(); // define it as Main Route
},
class BaseScreen extends StatefulWidget {
#override
_BaseScreenState createState() => _BaseScreenState();
}
class _BaseScreenState extends State<BaseScreen> {
#override
void initState() {
super.initState();
navigateToLastPage();
}
void navigateToLastPage() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
String lastRoute = prefs.getString('last_route');
// No need to push to another screen, if the last route was root
if (lastRoute.isNotEmpty && lastRoute != '/') {
Navigator.of(context).pushNamed(lastRoute);
}
}
C. Working Repo
You may look at this repository that overrides RouteObserver as explained in second option above
Saving and Opening Screen Beta and Screen Delta in different starts
D. Shared Preferences / JSON / SQLite
I suggest to use Shared preferences for simplicity. As we only record simple String for route name, we can only write two lines of code to Save and two lines of code to Load.
If we use JSON file, we need to manually set Path for it using path_provider package.
Moreover, if we use SQLite, we need to setup DB (may consist > 8 more lines), and setup table and also inserting table method.

Flutter: Passing multiple data between screens

I'm new to Flutter. I'm trying to send multiple data to another screen:
// screen1.dart
..
Expanded(
child: RaisedButton(
onPressed: () {
Navigator.push(context,
MaterialPageRoute(
builder: (context) => new Screen2(name: thing.name, email: thing.email, address: thing.address, etc..),
),
);
},
),
),
..
// screen2.dart
class Screen2 extends StatefulWidget{
Screen2({this.name}, {this.email}, {this.address}, etc..);
final String name;
final String email;
final String address;
// etc
#override
State<StatefulWidget> createState() { return new Screen2State();}
}
class Screen2State extends State<Screen2> {
Widget build(BuildContext context) {
return new WillPopScope(
..
child: Scaffold(
..
new Row(
children: <Widget>[
new Text(widget.name),
new Text(widget.email),
new Text(widget.address),
etc..
],
),
)
)
}
But I get the error: A non-null String must be provided to a Text widget.
The data is transferred from TextEditingControllers. It works when there is only 1 data transferred, but fails when there are 2 or more.
What's the correct way to send multiple data between screens?
Everything looks fine but you need to change in the Screen 2 class constructor to this
Screen2({this.name, this.email, this.address, etc..});
Modified Code
// screen1.dart
..
Expanded(
child: RaisedButton(
onPressed: () {
Navigator.push(context,
MaterialPageRoute(
builder: (context) => new Screen2(name: thing.name, email: thing.email, address: thing.address, etc..),
),
);
},
),
),
..
// screen2.dart
class Screen2 extends StatefulWidget{
Screen2({this.name, this.email, this.address, etc..});
final String name;
final String email;
final String address;
// etc
#override
State<StatefulWidget> createState() { return new Screen2State();}
}
class Screen2State extends State<Screen2> {
Widget build(BuildContext context) {
return new WillPopScope(
..
child: Scaffold(
..
new Row(
children: <Widget>[
new Text(widget.name),
new Text(widget.email),
new Text(widget.address),
etc..
],
),
)
)
}
Note: Text Widget will not accept null values so please make sure you are passing all the values. Or you can initialize the variables with the default value to blank
final String name="";
final String email="";
final String address="";
Consider passing the arguments through route arguments. Refer official doc here https://flutter.dev/docs/cookbook/navigation/navigate-with-arguments