HOW DO I SEND DATA FROM A SCREEN TO ANOTHER SCREEN - flutter

itemBuilder: (context, index) => MyImage( image: API.image +'/' `your text`+snapshot.data[index['MainPicture'].toString(), title: snapshot.data[index]['productName'],`your text`
subname: snapshot.data[index]['productSubname'],`your text`
price: snapshot.data[index][price].toString(),`your text`
discount: '% ' +
snapshot.data[index]['productDiscount'].toString(),`your text`
),
I want these parametres to make them to another Screen your text

Use a navigator like go_router
Later follow the steps accordingly to pass your parameters.
Definition
GoRoute(
path: '/sample/:id1/:id2', 👈 Defination of params in the path is important
name: 'sample',
builder: (context, state) => SampleWidget(
id1: state.params['id1'],
id2: state.params['id2'],
),
),
Passing the params
ElevatedButton(
onPressed: () {
var param1 = "param1";
var param2 = "param2";
context.goNamed("sample", params: {'id1': param1, 'id2': param2});
},
child: const Text("Hello"),
),
Receiver widget:
class SampleWidget extends StatelessWidget {
String? id1;
String? id2;
SampleWidget({super.key, this.id1, this.id2});
#override
Widget build(BuildContext context) {
...
}
}
And refer this answer: Flutter: go_router how to pass multiple parameters to other screen?

Related

Flutter GoRouter won't let me pass int value as queryparam

Flutter GoRouter won't let me pass any value which is not String as parameter.
Error Received
The following _TypeError was thrown while handling a gesture:
type 'int' is not a subtype of type 'Iterable<dynamic>'
Page which receives the int parameter:
class ItemOne extends StatelessWidget {
final int id;
const ItemOne({super.key, required this.id});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Item 1'),
),
body: Text('This is page for with id: $id'),
);
}
}
GoRouter defination
GoRoute(
path: '/one',
name: 'one',
builder: (context, state) {
return ItemOne(
id: state.queryParams['idGiven'] as int,
);
},
),
Button which passes the int value
IconButton(
onPressed: () => context.pushNamed(
'one',
queryParams: <String, dynamic>{
'idGiven': 111,
},
),
icon: const Text('Push One'),
),
Yes, indeed.
queryParams is a map from String to String.
If you want to pass something else, you need to convert it to a string and back.
Or use the extra field if you are passing whole objects.

Use StreamBuilder as root of navigation stack with GoRouter

Currently I have a StreamBuilder switching between a HomePage and LandingPage depending on the current auth state. The issue I have encountered is that I cannot pop the stack to the original /landing directory on a state change. This means that when a user logs in, the AuthPage remains on the top of the stack and the StreamBuilder builds the HomePage underneath.
AuthGate
class AuthGate extends StatelessWidget {
const AuthGate({super.key});
#override
Widget build(BuildContext context) {
return StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
return snapshot.hasData ? const HomePage() : const LandingPage();
},
);
}
}
LandingPage
This pushes the AuthPage to the stack.
class LandingPage extends StatelessWidget {
const LandingPage({super.key});
...
Row(
children: <Widget>[
FloatingActionButton.extended(
heroTag: UniqueKey(),
onPressed: () {
context.push('/auth');
},
label: const Text('Get started'),
),
FloatingActionButton.extended(
heroTag: UniqueKey(),
onPressed: () {
context.push('/auth');
},
label: const Text('Log in'),
),
],
)
...
}
}
Stack before auth change
Stack after auth change
Note how the AuthPage remains on the top of the stack but the Widget under StreamBuilder changes to the HomePage
(This is my first Stack question so please feel free to ask me to amend any information etc.)
If you are using GoRouter, then what you want to achieve should be done through GoRouter similarly to this:
GoRouter(
refreshListenable:
GoRouterRefreshListenable(FirebaseAuth.instance.authStateChanges()),
initialLocation: '/auth',
routes: <GoRoute>[
GoRoute(
path: '/landing',
name: 'landing',
builder: (context, state) {
return LandingPage()
},
routes: [
GoRoute(
path: 'auth',
name: 'auth',
builder: (context, state) => const AuthPage(),
),
],
),
GoRoute(
path: '/home',
name: 'home',
builder: (context, state) => const HomePage(),
),
],
errorBuilder: (context, state) {
return const Scaffold(
body: Text('404'),
);
},
redirect: (context, state) async {
final userRepo = injector.get<UserRepository>();
final user = FirebaseAuth.instance;
const authPaths = [
'/landing',
'/landing/auth',
];
bool isAuthPage = authPaths.contains(state.subloc);
if(user != null) {
if (isAuthPage) {
return '/home';
}
}
if(!isAuthPage) {
return '/auth';
}
return null;
},
);
class GoRouterRefreshListenable extends ChangeNotifier {
GoRouterRefreshListenable(Stream stream) {
notifyListeners();
_subscription = stream.asBroadcastStream().listen(
(_) {
notifyListeners();
},
);
}
late final StreamSubscription _subscription;
#override
void dispose() {
_subscription.cancel();
super.dispose();
}
}
Please also read documentation on of GoRouter.

Parsing deeply nested GraphQL Responses

I have a GraphQL schema which I've created mutations & queries from for my Flutter App :
type Mutation {
accounts: AccountsMutation!
}
type Query {
accounts: AccountsQuery!
}
type AccountsMutation {
auth: AuthenticationController!
}
type AccountsQuery {
auth: AuthenticationQueryController!
}
type AuthenticationController {
forgotPassword(email: Email!): Boolean!
resetPassword(
email: Email!
password: Password!
confirmPassword: Password!
code: String!
): IdentityResult!
login(email: Email!, password: Password!, rememberMe: Boolean! = false): User
tokenLogin(
email: Email!
password: Password!
rememberMe: Boolean! = false
): TokenLogin
refreshAccessToken(refreshToken: RefreshToken!): RefreshedAccessToken!
logOut: Boolean!
register(
email: Email!
password: Password!
confirmPassword: Password!
locale: Locale
timeZone: UserTimeZone
): User
}
type AuthenticationQueryController {
currentUser: User!
frontServiceToken: String!
}
scalar BOAccessToken
scalar BORefreshToken
scalar AccessToken
scalar Email
scalar ExternalId
type IdentityError {
code: String
description: String
}
type IdentityResult {
succeeded: Boolean!
errors: [IdentityError]
}
scalar Locale
scalar Password
type RefreshedAccessToken {
isValid: Boolean!
accessToken: AccessToken
expiresIn: Int!
}
scalar RefreshToken
scalar RoleId
scalar RoleName
scalar SecurityStamp
type TokenLogin {
user: User!
accessToken: AccessToken!
refreshToken: RefreshToken!
expiresIn: Int!
}
type User {
id: UserId!
firstName: String
lastName: String
locale: Locale!
timeZone: UserTimeZone!
email: Email!
firebaseToken: String
}
scalar UserId
scalar UserTimeZone
scalar UserTokenId
As you can see, my queries & mutations are deeply nested, how would you recommend parsing my responses ?
Currently I've created an Accounts() class that than parses an Auth() than than parses...
But I feel I'm creating unnecessary intermediary classes with this approach, are there better approaches more suited to GraphQL ?
Go to this Link:
https://pub.dev/packages/graphql_flutter
Use this :
graphql_flutter: ^5.1.0
Example:
import 'package:flutter/material.dart';
import 'package:trash_themes/themes.dart';
import './graphql_bloc/main.dart' show GraphQLBlocPatternScreen;
import './graphql_widget/main.dart' show GraphQLWidgetScreen;
import 'fetchmore/main.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'GraphQL Flutter Demo',
theme: DraculaTheme().makeDarkTheme(context: context),
home: Builder(
builder: (BuildContext context) => Scaffold(
appBar: AppBar(
title: const Text('GraphQL Demo App'),
),
body: Center(
child: Column(
children: <Widget>[
Spacer(),
Flexible(
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute<GraphQLWidgetScreen>(
builder: (BuildContext context) =>
GraphQLBlocPatternScreen(),
),
);
},
child: const Text('GraphQL BloC pattern'),
)),
Spacer(),
Flexible(
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute<GraphQLWidgetScreen>(
builder: (BuildContext context) =>
const GraphQLWidgetScreen(),
),
);
},
child: const Text('GraphQL Widget'),
)),
Spacer(),
Flexible(
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute<FetchMoreWidgetScreen>(
builder: (BuildContext context) =>
const FetchMoreWidgetScreen(),
),
);
},
child: const Text('Fetchmore (Pagination) Example'),
)),
],
),
),
),
),
);
}
}

How to use dynamic global list in flutter

I am new to Flutter and attempting sample mutual fund app to cover all basic widgets.
Requirement -> After selecting MF scheme, when user confirms on "buyNow" screen, corresponding scheme should get added to global dynamic list in "Cart" screen. This is basically a cart which is accessible to user on any screen, similar to shopping cart. I want to update cart list on "buyNow" screen and display same on "Cart" screen.
I have followed link to learn about 'provider' method of flutter to solve this, but not able to do.
PFB code
Main.dart
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => CartModel(),
child: MaterialApp(
home: Schemelist(),
routes: {
'/landing': (context) => Landing(),
'/schemelist': (context) => Schemelist(),
'/schemeBuy': (context) => SchemeBuy(),
'/buyNow': (context) => BuyNow(),
'/cart': (context) => Cart(),
},
),
),
);
}
Cartmodel.dart
import 'package:flutter/foundation.dart';
class CartModel with ChangeNotifier{
String schemeName;
String type;
String fromDate;
String toDate;
double amount;
List<CartModel> _cartList=[];
CartModel({this.amount,this.fromDate,this.schemeName,this.toDate,this.type});
void addToCart(CartModel cartObj){
_cartList.add(cartObj);
notifyListeners();
}
double get totalAmount =>
_cartList.fold(0, (total, current) => total + current.amount);
}
BuyNow.dart
RaisedButton(
onPressed: () {
_cart=new CartModel(amount:1000,fromDate:_dateTime.toString(),schemeName:widget.investmentObj.schemeName,toDate:_dateTime1.toString(),type:'SIP');
var cart = Provider.of<CartModel>(context);
cart.addToCart(_cart);
Navigator.pushNamed(context, '/cart');
},
child: Text('Yes'),
),
Cart.dart //where I will display dynamic list
Widget build(BuildContext context) {
var cart = Provider.of<CartModel>(context);
return Scaffold(
appBar: AppBar(
title: Text('Cart'),
centerTitle: true,
),
body: ListView.builder(
itemCount: --not able to access list--
itemBuilder: (context, index) => ListTile(
title: Text(
-------
),
),
),
);
}
First we should modify CartModel class. The fields (such as schemeName) should belong to the CartItem class, and the CartModel should only do its own thing (addToCart and others).
class CartModel with ChangeNotifier {
List<CartItem> _itemList = [];
// An unmodifiable view of the items in the cart.
UnmodifiableListView<CartItem> get itemList => UnmodifiableListView(_itemList);
void addToCart(CartItem item) {
_itemList.add(item);
notifyListeners();
}
double get totalAmount => _itemList.fold(0, (total, current) => total + current.amount);
}
class CartItem{
String schemeName;
String type;
String fromDate;
String toDate;
double amount;
CartItem({this.amount, this.fromDate, this.schemeName, this.toDate, this.type});
}
Then, in Cart.dart
Widget build(BuildContext context) {
var itemList = Provider.of<CartModel>(context).itemList;
return Scaffold(
appBar: AppBar(
title: Text('Cart'),
centerTitle: true,
),
body: ListView.builder(
itemCount: itemList.length,
itemBuilder: (_, index) {
var item = itemList[index];
return Text(item.schemeName);
},
),
);
}
You will get a error while click RaisedButton:
Tried to listen to a value exposed with provider, from outside of the widget tree.
This is likely caused by an event handler (like a button's onPressed) that called
Provider.of without passing `listen: false`.
To fix it, edit BuyNow.dart:
RaisedButton(
onPressed: () {
var _item = CartItem(amount: 1000, fromDate: _dateTime.toString(), schemeName: widget.investmentObj.schemeName, toDate: _dateTime1.toString(), type: 'SIP');
//just set listen to false
var cart = Provider.of<CartModel>(context, listen: false);
cart.addToCart(_item);
Navigator.pushNamed(context, '/cart');
},
child: Text('Yes'),
),

Flutter Build Tiles onTap showing error " undefined name 'context'"

I am trying to build multi level list view, when we tap child items, it should pass parameter to other page named QuizOptionsDialog.
1) I am able to print the root.name using print(root.name); using onTap
2) but when we try to navigate using following code it is showing undefined name 'context'
Full Code and error
enter code hereCode page 1/2
enter code hereCode page 2/2
enter code hereError
import 'package:flutter/material.dart';
import 'package:iti/quiz/ui/widgets/quiz_options.dart';
class ExpansionTileDemo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('List of Question Papers'),
),
body: ListView.builder(
itemCount: data.length,
itemBuilder: (BuildContext context, int index) => CategoryItem(
data[index],
),
),
),
);
}
}
// Welcome to another flutter tutorial
// In this video we will see how to create a multi-level Expansion List
// First Let's create a class for each row in the Expansion List
class Category {
final String id;
final String name;
final List<Category>
children; // Since this is an expansion list ...children can be another list of entries
Category(this.id, this.name, [this.children = const <Category>[]]);
}
// This is the entire multi-level list displayed by this app
final List<Category> data = <Category>[
Category(
'1',
'Main Category',
<Category>[
Category(
'1.1',
'Sub Category',
<Category>[
Category('1.1.1', 'Sub-Sub Category', <Category>[
Category('1.1.1.1', 'Sub-Sub-Sub Category',),
Category('1.1.1.2', 'Sub-Sub-Sub Category',),
]),
Category('1.1.2','Sub-Sub Category',
<Category>[
Category('1.1.2.1', 'Sub-Sub-Sub Category',),
Category('1.1.2.2', 'Sub-Sub-Sub Category',),
]
),
Category('1.1.3', 'Sub-Sub Category',
<Category>[
Category('1.1.3.1', 'Sub-Sub-Sub Category',),
Category('1.1.3.2', 'Sub-Sub-Sub Category',),
]
),
],
),
],
),
];
// Create the Widget for the row
class CategoryItem extends StatelessWidget {
const CategoryItem(this.category);
final Category category;
// This function recursively creates the multi-level list rows.
Widget _buildTiles(Category root) {
if (root.children.isEmpty) {
return ListTile(
title: Text(root.name),
onTap: () {
print(root.name);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => QuizOptionsDialog(category: category,),
),
);
},
);
}
return ExpansionTile(
key: PageStorageKey<Category>(root),
title: Text(root.name),
children: root.children.map<Widget>(_buildTiles).toList(),
);
}
#override
Widget build(BuildContext context) {
return _buildTiles(category);
}
_categoryPressed(BuildContext context,Category category) {
showModalBottomSheet(
context: context,
builder: (sheetContext) => BottomSheet(
builder: (_) => QuizOptionsDialog(category: category,),
onClosing: (){},
),
);
}
}
Change your CategoryItem class to also accept a BuildContext context variable in the constructor and assign it to a BuildContext variable like you did with category, and then pass that context in from the main widget when creating new CategoryItem. This will give you access to a context and should allow you to do your navigation.
To do so:
Storing the context in your CategoryItem class so each instance has access to it.
class CategoryItem extends StatelessWidget {
const CategoryItem(this.category, this.context);
final Category category;
final BuildContext context;
Updating the ListView.builder() to instantiate the updated CategoryItem class appropriately and pass the context.
body: ListView.builder(
itemCount: data.length,
itemBuilder: (BuildContext context, int index) => CategoryItem(
data[index], context
),
),
And now you should have access to the BuildContext for your page where you needed it.