How to pass arguments to pushReplacementNamed? - flutter

In my application, when the 'Next' button is clicked, pushReplacementNamed is fired and the arguments are passed to the next screen. How can I implement a route with arguments? Need exactly pushReplacementNamed.
Here is an example code with push:
_changeColor(String color) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ColorWidget(color: color),
),
);
}
...
ElevatedButton(
onPressed: () => _changeColor(selectedValue),
child: const Text('Next'),
)
How can one also pass arguments with pushReplacementNamed?
My main.dart:
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
routes: {
'/': (context) => HomeScreenWidget(),
'/color_picker': (context) => ColorPickerWidget(),
'/text_value': (context) => TextValueScreen(),
},
initialRoute: '/',
);
}
}

You have to send data in arguments key and get it back by route.
Check this document https://docs.flutter.dev/cookbook/navigation/navigate-with-arguments

Simply use
var argument = 'example string';
Navigator.pushNamed(
context,
'/otherscreen',
arguments: {'exampleArgument': argument},
);
and extract the arguments as follows:
#override
Widget build(BuildContext context) {
final arguments = (ModalRoute.of(context)?.settings.arguments ?? <String, dynamic>{}) as Map;
print(arguments['exampleArgument']);
return Scaffold(...);

Related

Problem with Null Check Operator Being Called on a Null Value in a Use Case Involving Named Routing with Arguments

[PLEASE SEE EDIT BELOW]
I am using named routing (with arguments) to send a user from page2 back to page1 in my app when a button is pressed:
onPressed: () {
bool resumeProcess = true;
Navigator.pushNamed(context, '/',
arguments: RouteArguments(resumeProcess: resumeProcess));
},
My named routing is set up in my MaterialApp:
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
final RouteArguments args =
ModalRoute.of(context)!.settings.arguments as RouteArguments;
return MaterialApp(
title: 'Reactive BLE Test',
theme: ThemeData(
primarySwatch: Colors.pink,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
initialRoute: '/',
routes: {
'/': (context) => MyHomePage(
title: 'Reactive BLE Test',
resumeProcess: args.resumeProcess,
),
'/myWifiPage': (context) => const MyWifiPage(),
},
navigatorKey: navigatorKey,
);
}
}
I'm using the following class in conjunction with ModalRoute to retrieve args.resumeProcess:
class RouteArguments {
final bool resumeProcess;
RouteArguments({required this.resumeProcess});
}
and
final RouteArguments args =
ModalRoute.of(context)!.settings.arguments as RouteArguments;
I'm unable to retrieve args.resumeProcess because my bang operator is being used on a null value at runtime.
I have tried different ways to solve this problem with no success. There are some good posts about this, but none seem to fit my use case (or maybe I'm missing something).
Is there a better way to send these arguments, or is there a way to deal with this null problem at runtime?
[BEGIN EDIT HERE]
While trying to fix the problem above, I turned to the recipe for doing this provided in the Flutter Docs: Pass arguments to a named route. This resulted in the following changes.
New ScreenArguments Class:
class ScreenArguments {
final String title;
final String resumeProcess;
ScreenArguments({required this.title, required this.resumeProcess});
}
New ExtractArgumentsScreen Class
class ExtractArgumentsScreen extends StatelessWidget {
const ExtractArgumentsScreen({super.key});
static const routeName = '/';
#override
Widget build(BuildContext context) {
final args = ModalRoute.of(context)!.settings.arguments as ScreenArguments;
return Scaffold(
appBar: AppBar(
title: Text(args.title),
),
body: Center(
child: Text(args.resumeProcess),
),
);
}
}
My MaterialApp is located in MyApp. This is where I register the widget in the routes table per the docs. I've commented out the old routes.
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Reactive BLE Test',
theme: ThemeData(
primarySwatch: Colors.pink,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
initialRoute: '/',
// routes: {
// '/': (context) => MyHomePage(
// title: 'Reactive BLE Test',
// resumeProcess: args.resumeProcess,
// ),
// '/myWifiPage': (context) => const MyWifiPage(),
// },
routes: {
ExtractArgumentsScreen.routeName: (context) =>
const ExtractArgumentsScreen(),
},
navigatorKey: navigatorKey,
);
}
}
And finally, I navigate to the widget in my second page (returning from page 2 to page 1) using Navigator.pushNamed().
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: const Text('Reactive BLE Test'),
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios),
onPressed: () {
String title = 'Reactive BLE Test';
String resumeProcess = 'true';
Navigator.pushNamed(
context,
ExtractArgumentsScreen.routeName,
arguments: ScreenArguments(
title: title,
resumeProcess: resumeProcess,
),
);
},
),
actions: const [],
),
body: Column(
children: <Widget>[
I get a new, but similar error:
type 'Null' is not a subtype of type 'ScreenArguments' in type cast
Can anyone tell me what am I doing wrong? Thanks in advance.
I think you are calling it on the wrong place. these ModalRoute.of(context) must be associated with context that having modal routes, or routes. in this case MaterialApp which hold the routes. But you are calling it before the MaterialApp is created. try to move it into inside of MyHomePage and read the arguments inside there instead of passing it as arguments on the material app Routes.
for example:
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Reactive BLE Test',
theme: ThemeData(
primarySwatch: Colors.pink,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
initialRoute: '/',
routes: {
'/': (context) => MyHomePage(
title: 'Reactive BLE Test',
),
'/myWifiPage': (context) => const MyWifiPage(),
},
navigatorKey: navigatorKey,
);
}
and inside your MyHomePage widget:
#override
Widget build(BuildContext context) {
final RouteArguments args =
ModalRoute.of(context)!.settings.arguments as RouteArguments;
var resumeProcess = args.resumeProcess;
return Scaffold(
// your home page class
);
}

Could not find the correct Provider above the BlocListener Widget

I'm trying to use Bloc provider for user authentication in my flutter app. When I try to access the data i'm always getting this error even though I double checked all the files.
This is the error i'm getting:
Error: Could not find the correct Provider<StateStreamable<Object?>> above this
BlocListener<StateStreamable<Object?>, Object?> Widget
This happens because you used a `BuildContext` that does not include the provider
main.dart:
void main() async {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => AuthBloc(LoginInitState(), AuthRepository()))
],
child: MaterialApp(
title: 'Flutter app',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity),
home: const LoginPage(),
),
);
}
}
parts from login.dart:
#override
void initState() {
authBloc = BlocProvider.of<AuthBloc>(context);
super.initState();
}
######################################################
return Scaffold(
backgroundColor: Colors.grey[300],
body: BlocListener(
listener: (context, state) {
if (state is UserLoginSuccessState) {
Navigator.push(context,
MaterialPageRoute(builder: (context) => const HomeScreen()));
}
},
child: SafeArea...
I'm still new to flutter and struggling with the state management part, I'd be glad if anybody can help!
In your BlocListener you're missing the State and the Bloc
Here's what I mean
BlocListener<AuthBloc, AuthState>(
listener: (context, state) {
if (state is UserLoginSuccessState) {
Navigator.push(context,
MaterialPageRoute(builder: (context) => const HomeScreen()));
}
},
child: SafeArea...

Flutter passed arguments return null allways

In this simple flutter GitHub repository implementation we have a simple screen which we want to pass arguments and getting them in destination screen, for example
form ScreenA() we want pass arguments to ScreenB()
defined routes:
routes: {
'/page/one': (context) => ScreenA(),
'/page/two': (context) => ScreenB(),
'/page/three': (context) => ScreenC(),
},
fist of all we have ScreenA():
class ScreenA extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Navigation with params'),
),
body: Center(
child: ElevatedButton(
child: Text('Click me!'),
onPressed: () {
Navigator.pushNamed(
context,
'/page/two',
arguments: PageArguments(
id: 1,
title: "Example Title"
),
);
},
),
),
);
}
}
and destination page as ScreenB() is:
class ScreenB extends StatelessWidget {
#override
Widget build(BuildContext context) {
final arguments = ModalRoute.of(context)!.settings.arguments;
/* getting currect route name */
print(ModalRoute.of(context)!.settings.name);
/* getting NULL arguments */
print(arguments);
return Scaffold();
}
}
class PageArguments{
final int id;
final String title;
PageArguments({required this.id, required this.title});
}
It seems that Persistent Bottom Navigation Bar package does not pass correctly route information, it could be a bug. If you put a breakpoint in the build method of ScreenB, you will see that not only the argument is null, but route name is also wrong.
I tried in main.dart with onGenerateRoute and it worked for me this way:
List<PersistentBottomNavBarItem> _navBarsItems() {
return [
PersistentBottomNavBarItem(
title: 'Home',
icon: Icon(Icons.home),
routeAndNavigatorSettings: RouteAndNavigatorSettings(
initialRoute: '/page/one',
onGenerateRoute: (RouteSettings settings) {
WidgetBuilder builder;
// Manage your route names here
switch (settings.name) {
case '/page/one':
builder = (BuildContext context) => ScreenA();
break;
case '/page/two':
builder = (BuildContext context) => ScreenB();
break;
default:
throw Exception('Invalid route: ${settings.name}');
}
return MaterialPageRoute(
builder: builder,
settings: settings,
);
})),
PersistentBottomNavBarItem(title: 'Help', icon: Icon(Icons.help)),
];
}
About casting the argument, I agree with the other answer.
Main and MyApp for reference
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ScreenA(),
routes: {
'/page/one': (context) => ScreenA(),
'/page/two': (context) => ScreenB(),
},
);
}
}
Update ClassB with below code no modification required in ClassA
class ScreenB extends StatelessWidget {
#override
Widget build(BuildContext context) {
final arguments =
ModalRoute.of(context)!.settings.arguments as PageArguments;
print(ModalRoute.of(context)!.settings.name);
print(arguments.id);
print(arguments.title);
print("------");
return Scaffold();
}
}
Key update:
Typecasting the argument.
final arguments =
ModalRoute.of(context)!.settings.arguments as PageArguments;
Console log:

How do I pass the data from the checked List Tile to the next page (Basket) in flutter

I previously used a Listview to present my data, but I was advised to use a Checkbox List tile now I'm stuck on how to pass data. I want to pass the selected/checked List tile's data to the next page (Basket). How do I do that, please help me.
I am new to Flutter.
The Main Method Class
import 'package:cart_app1/Basket.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(),
initialRoute: '/',
routes: {
'/': (context) => MyHomePage(),
Basket.routeName: (context) => Basket(),
},
);
}
}
The product list class
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Map<String, bool> values = {
"GIN COCKTAILS \nClover Club \t R65": false,
"Beers & Ciders \nHeineken NRB \t R29": false,
"Vodka \nCiroc \t R35": false,
"Vodka \nCruz Vodka \t R30": false,
"COGNAC \nHennesy \t R40": false,
"Tequilla \nEl Jimador \t R30": false,
"Non-Alcoholic \nSoft-Drink \t R20": false,
"WHISK[E]Y \nJohnie Walker Red \t R25": false,
};
var toggle = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("The Wing Republic"),
actions: <Widget>[
IconButton(
icon: Icon(Icons.shopping_basket),
onPressed: () {
Navigator.of(context)
.pushNamed(Basket.routeName, arguments: values);
},
),
],
),
body: ListView(
children: values.keys.map((String key) {
return CheckboxListTile(
title: Text(key),
subtitle: Text(""),
isThreeLine: false,
value: values[key],
onChanged: (bool value) {
setState(() {
values[key] = value;
});
},
);
}).toList(),
),
);
}
}
The Basket Method
import 'package:flutter/material.dart';
class Basket extends StatelessWidget {
static const routeName = '/Basket';
#override
Widget build(BuildContext context) {
final data = ModalRoute.of(context).settings.arguments as Map<String, bool>;
return Scaffold();
}
}
You can pass arguments in pushNamed method.
Navigator.of(context).pushNamed(Basket.routeName, arguments: values);
On the receiving end, use ModalRoute to get the values.
import 'package:flutter/material.dart';
class Basket extends StatelessWidget {
static const routeName = '/Basket';
#override
Widget build(BuildContext context) {
final data = ModalRoute.of(context).settings.arguments as Map<String, bool>;
return Scaffold();
}
}
Routes in MyApp is incorrect. If you want to use routes, you can't use home property. Instead of home specify initialRoute.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
visualDensity: VisualDensity.adaptivePlatformDensity,
),
initialRoute: '/',
routes: {
'/': (context) => MyHomePage(),
Basket.routeName: (context) => Basket(),
},
);
}
}

How make button which open random page in Flutter?

This is my first post, so sorry for errors. I have a problem I can't make a button which open radom page in my apk. The project is connected with Coronavirus.
You can easily use RouteGenerator and list with needed random pages for this task.
main.dart:
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Random pages',
theme: ThemeData(
primarySwatch: Colors.blue,
),
initialRoute: 'start_page',
onGenerateRoute: RouteGenerator.generateRoute,
);
}
}
route_generator.dart:
class RouteGenerator {
static List<String> myRandomPages = [
'first_page',
'second_page'
];
static String getRandomNameOfRoute(){
return myRandomPages[Random().nextInt(myRandomPages.length)];
}
static Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) {
case 'start_page':
return MaterialPageRoute(builder: (_) => StartPage());
case 'first_page':
return MaterialPageRoute(builder: (_) => FirstPage()); // FirstPage - is just a Widget with your content
case 'second_page':
return MaterialPageRoute(builder: (_) => SecondPage());// Also custom Widget
//... other random or not pages
}
}
}
start_page.dart:
class StartPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Start page'),
),
body: Center(
child: RaisedButton(
child: Text('Go to random page'),
onPressed: () => Navigator.of(context).pushNamed(RouteGenerator.getRandomNameOfRoute()),
),
)
);
}
}