Null value occur while pass arguments using a named route - flutter

When I pass the value of userRole from ( Profile screen to Cancel screen ). I am not able to return value using model route. It sends a null value. Please help.
my profile.dart file
Widget displayProfile(BuildContext context, DataSnapshot dataValues) {
final values = dataValues.value;
final userRole = values['role'];
return Padding(
padding: const EdgeInsets.all(
AppPadding.p16,
),
child: Column(
children: [
ListTile(
title: Text(
AppString.cancelledOrder,
style: themeData.textTheme.headline4,
),
trailing: const Icon(Icons.chevron_right),
onTap: () {
Navigator.of(context).pushNamed(
PageRoutes.cancelOrderScreen,
arguments: userRole);
},
),
],
}
cancelOrder.dart file where the argument is passed
class CancelOrderScreen extends StatefulWidget {
const CancelOrderScreen({Key? key}) : super(key: key);
#override
State<CancelOrderScreen> createState() => _CancelOrderScreenState();
}
class _CancelOrderScreenState extends State<CancelOrderScreen> {
#override
Widget build(BuildContext context) {
final userRole = ModalRoute.of(context)!.settings.arguments as dynamic;
return Scaffold(
appBar: AppBar(
title: const Text(userRole),
),
),
}
my separate route.dart file
class RouteGenerator {
static Route<dynamic> getRoute(RouteSettings routeSettings) {
switch (routeSettings.name) {
case PageRoutes.profile:
return MaterialPageRoute(builder: (_) => const Profile());
case PageRoutes.cancelOrderScreen:
return MaterialPageRoute(builder: (_) => const CancelOrderScreen());
default:
return unDefinedRoute();
}
}

Related

How to access attributes of the widgets children

I am opening a new activity with the startTimer method How do I set the cyclesVal variable to an attribute of my custom NumberSelector widget so I can pass it to the next activity. Any way to access an internal variable of the NumberSelector widget would work too since I have set the attribute to the variable.
class ExcersizeInput extends StatelessWidget {
const ExcersizeInput({super.key});
void startTimer(BuildContext context) {
int cyclesVal = 1;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => TimerPage(
cycleCount: cyclesVal,
),
));
}
#override
Widget build(BuildContext context) {
return Column(children: [
Column(
children: [
NumberSelector(
title: "Cycles",
),
],
),
ElevatedButton.icon(
onPressed: (() => {startTimer(context)}),
label: const Text("Start"),
icon: const Icon(Icons.start_outlined),
)
]);
}
}
Here's the NumberSelector code:
class NumberSelector extends StatefulWidget {
final String title;
const NumberSelector(
{super.key, required this.title});
#override
State<NumberSelector> createState() => _NumberSelectorState();
}
class _NumberSelectorState extends State<NumberSelector> {
int selectorValue = 1;
void updateValue(ifAdd) {
setState(() {
if (ifAdd) {
if (selectorValue < 9999) {
selectorValue++;
}
} else {
if (selectorValue > 1) {
selectorValue--;
}
}
});
}
#override
Widget build(BuildContext context) {
final ColorScheme colors = Theme.of(context).colorScheme;
return Column(
children: [
Text(widget.title,
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
Row(
children: [
GestureDetector(
child: IconButton(
onPressed: () => updateValue(false),
icon: const Icon(Icons.remove),
style: styleContainedButton),
},
),
Text(selectorValue.toString()),
style: const TextStyle(fontSize: 16)),
GestureDetector(
child: IconButton(
onPressed: () => updateValue(true),
icon: const Icon(Icons.add),
style: styleContainedButton),
},
),
],
),
],
);
}
}
To get a variable from a stateful widget in flutter it needs a key that is linked to the state of the widget. Via the key, the variables can be accessed.
Define a widget you want to access from its parent and that contains any value:
class TestWidget extends StatefulWidget {
TestWidget({Key? key}) : super(key: key);
#override
State<TestWidget> createState() => TestWidgetState();
}
class TestWidgetState extends State<TestWidget> {
int? anyvalue;
#override
Widget build(BuildContext context) {
return Container();
}
}
Declare the key in the parent widget. Make sure the name of the state does not start with an underscore:
GlobalKey<TestWidgetState> _widget_key = GlobalKey();
Give the key to the widget in the build method of the parent widget:
TestWidget(
key: _widget_key,
)
Now the value of the child widget can be accessed in the parent widget via the key:
void afunction() {
print(_widget_key.currentState!.anyvalue);
}

Could not find the correct Provider<Products> above this ProductItemScreen Widget

I am getting an Error: Could not find the correct Provider above this ProductItemScreen Widget whenever I press on a product Item even though I have set my provider as a parent of MaterialApp()
Below is the product_overview_screen.dart file which shows a gridview of the products on my screen
class ProductsOverviewScreen extends StatelessWidget {
static const routeName = '/prod_overview_Screen';
const ProductsOverviewScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final prodData = Provider.of<Products>(context); //Recieves data from provider file
return Scaffold(
appBar: AppBar(
title: const Text('My Shop'),
backgroundColor: Theme.of(context).primaryColor,
),
body: GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
childAspectRatio: 3 / 2,
),
itemCount: prodData.items.length,
itemBuilder: (ctx, i) => ProductItem(
imageUrl: prodData.items[i].imageUrl,
title: prodData.items[i].title,
id: prodData.items[i].id,
),
),
);
}
}
Below is also the Product_item.dart file where I pass the Id of the product to the next screen where I display the data of that particular product on the screen
class ProductItemScreen extends StatelessWidget {
static const routeName = '/prod_item_screen';
const ProductItemScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final prodIdPassed = ModalRoute.of(context)!.settings.arguments as String;
final prod = Provider.of<Products>(context).findById(prodIdPassed);
return Scaffold(
appBar: AppBar(
title: Text(prod.title),
backgroundColor: Theme.of(context).primaryColor,
),
body: Column(
children: [Image.asset(prod.imageUrl), Text(prod.title)],
),
);
}
}
Also below is the main.dart file where I have setup my provider for all available listeners
void main() {
runApp(
ChangeNotifierProvider(
create: (ctx) => Products(),
child: MaterialApp(
theme: ThemeData.light().copyWith(primaryColor: Colors.purple),
initialRoute: ProductsOverviewScreen.routeName,
routes: {
ProductsOverviewScreen.routeName: (ctx) =>
const ProductsOverviewScreen(),
ProductItemScreen.routeName: (ctx) => const ProductItemScreen(),
},
),
),
);
}
Right below runApp, explicitly declare the class:
ChangeNotifierProvider<Products>(
You did'nt provided providermodel class here Check this provider.
Mainmethod (not changed)
void main() {
var changeNotifierProvider = ChangeNotifierProvider(
create: (ctx) => Products(),
child: MaterialApp(
theme: ThemeData.light().copyWith(primaryColor: Colors.purple),
initialRoute: ProductsOverviewScreen.routeName,
routes: {
ProductsOverviewScreen.routeName: (ctx) =>
const ProductsOverviewScreen(),
ProductItemScreen.routeName: (ctx) => const ProductItemScreen(),
},
),
);
runApp(changeNotifierProvider);
}
Products provider (Added)
class Products extends ChangeNotifier {
Product? _findById;
List<Product> _items = [];
List<Product> get items => _items;
set items(List<Product> value) {
_items = value;
notifyListeners();
}
set items2(List<Product> value) {
_items = value;
// notifyListeners();
}
Product? get findById => _findById;
set findById(Product? value) {
_findById = _items.where((element) => element.id == value!.id).first;
notifyListeners();
}
set findById2(Product? value) {
_findById = _items.where((element) => element.id == value!.id).first;
// notifyListeners();
}
}
Sample Code
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
var changeNotifierProvider = ChangeNotifierProvider(
create: (ctx) => Products(),
child: MaterialApp(
theme: ThemeData.light().copyWith(primaryColor: Colors.purple),
initialRoute: ProductsOverviewScreen.routeName,
routes: {
ProductsOverviewScreen.routeName: (ctx) =>
const ProductsOverviewScreen(),
ProductItemScreen.routeName: (ctx) => const ProductItemScreen(),
},
),
);
runApp(changeNotifierProvider);
}
class ProductItemScreen extends StatelessWidget {
static const routeName = '/prod_item_screen';
const ProductItemScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final prodIdPassed = ModalRoute.of(context)!.settings.arguments as String;
var product = new Product(imageUrl: "", title: "", id: prodIdPassed);
Provider.of<Products>(context).findById2 = product;
final prod = Provider.of<Products>(context).findById;
return Scaffold(
appBar: AppBar(
title: Text(prod!.title),
backgroundColor: Theme.of(context).primaryColor,
),
body: Column(
children: [Image.network(prod.imageUrl), Text(prod.title)],
),
);
}
}
class ProductsOverviewScreen extends StatelessWidget {
static const routeName = '/prod_overview_Screen';
const ProductsOverviewScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final prodData = Provider.of<Products>(context);
List<Product> item = [];
item.add(Product(
imageUrl:
"https://cdn.pixabay.com/photo/2013/07/13/14/08/apparel-162192_1280.png",
title: "lava",
id: "1"));
item.add(Product(
imageUrl:
"https://hips.hearstapps.com/hmg-prod.s3.amazonaws.com/images/classic-accessories-1516305397.jpg",
title: "hi",
id: "2"));
item.add(Product(
imageUrl: "https://images.indianexpress.com/2019/09/toys.jpg",
title: "hi",
id: "4"));
item.add(Product(
imageUrl: "https://m.media-amazon.com/images/I/51zEsraniRL._UX569_.jpg",
title: "hi",
id: "3"));
prodData.items2 = item; //Recieves data from provider file
return Scaffold(
appBar: AppBar(
title: const Text('My Shop'),
backgroundColor: Theme.of(context).primaryColor,
),
body: GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
childAspectRatio: 3 / 2,
),
itemCount: prodData.items.length,
itemBuilder: (ctx, i) => ProductItem(
imageUrl: prodData.items[i].imageUrl,
title: prodData.items[i].title,
id: prodData.items[i].id,
),
),
);
}
}
class ProductItem extends StatelessWidget {
var imageUrl;
var id;
var title;
ProductItem({Key? key, this.imageUrl, this.title, this.id}) : super(key: key);
#override
Widget build(BuildContext context) {
var column = Column(
children: [
Expanded(
child: Container(
// height: 125,
child: Image.network(
imageUrl,
alignment: Alignment.center,
fit: BoxFit.cover,
),
),
),
Center(
child: Text(
title + " " + id,
style: TextStyle(fontSize: 12),
))
],
);
return InkWell(
onTap: () {
Navigator.pushNamed(context, ProductItemScreen.routeName,
arguments: id);
},
child: column);
}
}
class Product {
var title;
var imageUrl;
var id;
Product({this.imageUrl, this.title, this.id});
}
class Products extends ChangeNotifier {
Product? _findById;
List<Product> _items = [];
List<Product> get items => _items;
set items(List<Product> value) {
_items = value;
notifyListeners();
}
set items2(List<Product> value) {
_items = value;
// notifyListeners();
}
Product? get findById => _findById;
set findById(Product? value) {
_findById = _items.where((element) => element.id == value!.id).first;
notifyListeners();
}
set findById2(Product? value) {
_findById = _items.where((element) => element.id == value!.id).first;
// notifyListeners();
}
}

How do I display the output of a method from the first page on a second page in flutter?

The app is supposed to take in the first name on the first page and reverse it, then print the output on the second page. I'm having trouble implementing the output from the _incrementCounter method into the SecondScreen class in order to print the output on the second page.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext ctxt) {
return MaterialApp(
home: FirstScreen(),
);
}
}
class FirstScreen extends StatelessWidget {
String t1 = '', output = '';
RevName name = RevName();
void _incrementCounter() {
setState(() {
output = name.reverseName(t1) as String;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Center(child: Text("Reverse Name")),
),
body: Column(
children: [
Padding(
padding: EdgeInsets.all(20.0),
child: TextField(keyboardType: TextInputType.name,
decoration: const InputDecoration(
labelText: 'Enter Your First Name',
border: OutlineInputBorder()),
onChanged: (text) {
t1 = text;
},
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: OutlinedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondScreen()),
);
_incrementCounter();
},
child: Text('Reverse Name'),
),
),
],
)
);
}
void setState(Null Function() param0) {}
}
class SecondScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Reverse Name"),
),
body: Column(
children: [
Center(
child: OutlinedButton(
onPressed: (){
Navigator.pop(context);
},
child: Text('Back')
),
),
],
)
);
}
}
The class where name is being reversed and returning the reversed name
class RevName {
String name, reversed;
RevName({this.name = '', this.reversed = ''});
String reverseName(name) {
reversed = name.split('').reversed.join('');
return '$reversed';
}
}
You've RevName model class, to store name and reverse name. You can modify like,
class RevName {
String name, reversed;
RevName({
required this.name,
}) : reversed = name.split('').reversed.join('');
}
Now let's pass data using constructor. Remove state RevName and follow this,
child: OutlinedButton(
onPressed: () {
RevName revName = RevName(name: t1);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen(
data: revName,
),
),
);
},
child: const Text('Reverse Name'),
),
On receiver side SecondScreen.
class SecondScreen extends StatelessWidget {
final RevName data;
const SecondScreen({
Key? key,
required this.data,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Reverse Name"),
),
body: Column(
children: [
Text(data.name),
Text(data.reversed),
You can follow this answer to explore alternative way.

Flutter Could not find the correct Provider<> above Widget when trying to create ExpansionPanelList

I am trying to build ExpansionPanelList and im using Provider there. To be honest i cannot fully understand the Provider but I am using it as i could not figure other way to make it work. I have seen provider used similar way in somewhere else so i think im not completely wrong here?
Thanks for any help!
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Product {
final String name;
final String price;
Product({this.name, this.price});
}
class DatabaseService {
final CollectionReference productsCollection =
Firestore.instance.collection('products');
List<Product> productListFromSnapshot(QuerySnapshot snapshot) {
// cycle through each document and create product list
return snapshot.documents.map((doc) {
return Product(
name: doc.data['name'] ?? "",
price: doc.data['price'] ?? 0,
);
}).toList();
}
Stream<List<Product>> get products {
return productsCollection.snapshots().map(productListFromSnapshot);
}
}
class ProductList extends StatefulWidget {
ProductList({Key key}) : super(key: key);
#override
_ProductListState createState() => _ProductListState();
}
class _ProductListState extends State<ProductList> {
#override
Widget build(BuildContext context) {
final products = Provider.of<List<Product>>(context) ?? [];
return Container(
child: ExpansionPanelList.radio(
children: products.map<ExpansionPanel>((Product product) {
return ExpansionPanelRadio(
value: product.name,
headerBuilder: (BuildContext context, bool isExpanded) {
return ListTile(
title: Text(product.name),
);
},
body: ListTile(
title: Text(product.name),
subtitle: Text('some text here as subtitle text'),
trailing: Icon(Icons.delete),
onTap: () {
setState(() {
products.removeWhere((currentItem) => product == currentItem);
});
},
),
);
}).toList(),
),
);
}
}
____________________
class Home extends StatelessWidget {
final AuthService _auth = AuthService();
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blue[50],
appBar: AppBar(
title: Text('test'),
backgroundColor: Colors.blue[400],
elevation: 0.0,
actions: <Widget>[
FlatButton.icon(
onPressed: () async {
await _auth.signout();
},
icon: Icon(Icons.person),
label: Text('LogOut'))
],
),
body: HomeScreen(),
);
}
}
class HomeScreen extends StatefulWidget {
HomeScreen({Key key}) : super(key: key);
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
return Center(
child: Column(children: <Widget>[
RaisedButton(
color: Colors.blue[600],
child: Text('ProductList'),
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (contex) => ProductList()));
}),
]),
);
}
}

PushNamed issue: Type 'FillData' (a Statefulwidget) is not a subtype of type 'List<Object>'

I'm new in Flutter. I'm trying to push a List from NewData to FillData screen with pushNamed. But it said:
The following _TypeError was thrown while handling a gesture:
type 'FillData' is not a subtype of type 'List'
If i remove the comment in '/FillData', i receive null data instead. What should i do?
This is my code:
SettingNavigator
class SettingNavigator extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => Home(),
'/NewData': (context) => NewData(),
// '/FillData': (context) => FillData(), (in comment)
}
onGenerateRoute: (setting) {
if (setting.name == '/FillData') {
final ChartGroupData chartName = setting.arguments;
final List<ChartGroupData> groupNames = setting.arguments;
return MaterialPageRoute(builder: (context) {
return FillData(
chartName: chartName,
gName: groupNames,
);
});
}
return null;
},
);
}
}
NewData
import 'package:flutter/material.dart';
class NewData extends StatefulWidget {
List<ChartGroupData> groupNames;
NewData({Key key, #required this.groupNames}) : super(key: key);
#override
NewDataStage createState() => NewDataStage();
}
class NewDataStage extends State<NewData> {
TextEditingController _nameCtrl = new TextEditingController();
var textFields = <Widget>[];
var groupTECs = <TextEditingController>[];
#override
void initState() {
super.initState();
textFields.add(createCustomTextField());
}
Widget createCustomTextField() {
var groupCtrl = TextEditingController();
groupTECs.add(groupCtrl);
return Container(
padding: EdgeInsets.fromLTRB(0, 5, 0, 0),
child: Row(
children: <Widget>[
Expanded(flex: 3, child: Text("Group ${textFields.length}")),
Container(
constraints: BoxConstraints.tightFor(width: 120, height: 60),
child: TextField(
controller: groupCtrl,
),
),
],
),
);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Center(child: Text("New Chart")),
),
body: Container(
alignment: AlignmentDirectional.center,
constraints: BoxConstraints.expand(),
child: Column(
children: <Widget>[
Text(
"Your chart name",
style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
),
TextField(
style: TextStyle(fontSize: 20),
controller: _nameCtrl,
),
Expanded(
flex: 3,
child: Container(
child: ListView.builder(
shrinkWrap: true,
itemCount: textFields.length,
itemBuilder: (BuildContext context, int index) {
return textFields[index];
},
),
),
),
SizedBox(
height: 60,
width: 120,
child: RaisedButton(
onPressed: _onTapNext,
child: Text("NEXT"),
color: Colors.green,
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _onTapCreate,
child: Icon(Icons.add, color: Colors.white),
shape: CircleBorder(),
),
),
);
}
void _onTapNext() {
/// Push Groups name to FillData
widget.groupNames = List<ChartGroupData>();
for (int i = 0; i < textFields.length; i++) {
var name = groupTECs[i].text;
widget.groupNames.add(ChartGroupData(name));
}
print(widget.groupNames.toString());
Navigator.pushNamed(context, '/FillData',
arguments: FillData(
gName: widget.groupNames,
chartName: ChartGroupData(_nameCtrl.text),
));
}
void _onTapCreate() {
setState(() {
textFields.add(createCustomTextField());
});
}
}
FillData
class FillData extends StatefulWidget {
final ChartGroupData chartName;
final List<ChartGroupData> gName;
FillData({Key key, #required this.chartName, #required this.gName})
: super(key: key);
#override
FillDataStage createState() => FillDataStage();
}
class FillDataStage extends State<FillData> {
void _showDialog() {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text("Received Data"),
content: Text(widget.chartName.toString()),
);
},
);
}
void _onTapPrintReceivedData() {
print(widget.gName);
print(widget.chartName);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Center(
child: Text("Fill your Data"),
),
),
body: Center(
child: RaisedButton(
onPressed: () {
_onTapPrintReceivedData();
_showDialog();
},
child: Text("Print Data"),
),
),
));
}
}
Class ChartGroupData
lass ChartGroupData {
final String groupNames;
ChartGroupData(this.groupNames);
#override
String toString() {
return 'Group: $groupNames';
}
}
You have 2 problems with your code:
1- you cant user routes with onGenerateRoute, because now the app doesn't know where to go, to the widget that you didn't pass anything to (inside routes) or to the widget inside the onGenerateRoute.
2- arguments is a general object that you can put whatever you want inside of it, and doing this:
final ChartGroupData chartName = setting.arguments; final
List groupNames = setting.arguments;
passes the same value to two different objects, I solved this by doing the following (it's not the best but will give you a rough idea of what you should do)
created a new object that contains the data to be passed:
class ObjectToPass {
final ChartGroupData chartName;
final List<ChartGroupData> groupNames;
ObjectToPass({this.chartName, this.groupNames});
}
changed FillData implementation:
class FillData extends StatefulWidget {
final ObjectToPass objectToPass;
FillData({Key key, #required this.objectToPass}) : super(key: key);
#override
FillDataStage createState() => FillDataStage();
}
...
void _showDialog() {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text("Received Data"),
content: Text(widget.objectToPass.chartName.toString()),
);
},
);
}
void _onTapPrintReceivedData() {
print(widget.objectToPass.groupNames);
print(widget.objectToPass.chartName);
}
to navigate to FillData you would:
Navigator.pushNamed(
context,
'/FillData',
arguments: ObjectToPass(
chartName: ChartGroupData(_nameCtrl.text),
groupNames: groupNames,
),
);
finally this is how your MaterialApp should look like:
return MaterialApp(
initialRoute: '/NewData',
onGenerateRoute: (setting) {
if (setting.name == '/FillData') {
return MaterialPageRoute(builder: (context) {
return FillData(
objectToPass: setting.arguments,
);
});
} else if (setting.name == '/NewData') {
return MaterialPageRoute(builder: (_) => NewData());
}
return null;
},
);
you can pass a list instead of the object I created and get your objects from it by it's index.