is there anybody who can help me parsing json data to another page using Hero Widget? - flutter

I want somebody to help me to parse JSON data to another Page in HERO widget in the flutter. I parse data to the first page but failed to parse data to another page

make the model for JSON data use PODO style
like this which will deal with all JSON data to parsed to the view class.
class ProductResponse{
List<ProductDetail> results;
ProductResponse({this.results});
ProductResponse.fromJson(Map<String,dynamic> json){
if(json['results'] !=null){
results=new List<ProductDetail>();
json['results'].forEach((v){
results.add(new ProductDetail.fromJson(v));
});
}
}
Map<String,dynamic> toJson(){
final Map<String, dynamic> data = new Map<String, dynamic>();
if (this.results != null) {
data['results'] = this.results.map((v) => v.toJson()).toList();
}
return data;
}
}
class ProductDetail{
int phone;
int price;
int qty;
String amount;
String place;
String image_name;
String image_url;
String vendor_name;
String description;
String category;
String productName;
String images;
ProductDetail({this.phone, this.price, this.qty, this.amount, this.vendor_name,
this.description, this.category, this.productName, this.images,this.image_name,this.image_url,this.place});
ProductDetail.fromJson(Map<String,dynamic> json){
phone = json['phone'];
price = json["price"];
qty = json['qty'];
amount =json['amount'];
vendor_name =json['vendor_name'];
description = json['description'];
category = json['category'];
images = json['images'];
productName = json['productName'];
image_url =json['image_url'];
image_name =json['image_name'];
place =json['place'];
}
Map<String,dynamic> toJson(){
final Map<String,dynamic> data =new Map<String,dynamic>();
data['phone'] =this.phone;
data['price'] =this.price;
data['qty'] =this.qty;
data['amount'] =this.amount;
data['vendor_name'] =this.vendor_name;
data['description'] =this.description;
data['category'] =this.category;
data['productName'] =this.productName;
data['images'] =this.images;
data['place'] = this.place;
data['image_url'] =this.image_url;
data['image_name'] =this.image_name;
return data;
}
}
make the class which will make the request to the server
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:kilimo_biashara1/Model/Dialogs.dart';
import 'package:kilimo_biashara1/Model/ProductDetail.dart';
import 'package:kilimo_biashara1/Products/ProductPage.dart';
class Products extends StatefulWidget {
#override
_ProductsState createState() => _ProductsState();
}
class _ProductsState extends State<Products> {
String url = "put your api url here";
ProductResponse detail;// declare the class from PODO
fetchProduct() async {
var response = await http.get(url);
var decodeJson = jsonDecode(response.body);
print("response" + response.body);
setState(() {
detail = ProductResponse.fromJson(decodeJson);
});
print(detail);//debug
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: detail == null
? Center(
child: CircularProgressIndicator(),
)
: StaggeredGridView.countBuilder(
crossAxisCount: 4,
itemCount: detail.results.length,
itemBuilder: (BuildContext context, int index) {
return ProductPage(
detail: detail.results[index]
); //Return the product page
},
staggeredTileBuilder: (_) => StaggeredTile.fit(2),
mainAxisSpacing: 4.0,
crossAxisSpacing: 4.0,
)
);
}
}
here are sample codes of the product page.in this product page it will show cards with few details in it
import 'package:flutter/material.dart';
import 'package:kilimo_biashara1/Products/DetailProduct.dart';
import 'package:kilimo_biashara1/Model/ProductDetail.dart';
class ProductPage extends StatelessWidget {
final ProductDetail detail;
ProductPage({#required this.detail});
#override
Widget build(BuildContext context) {
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4.0),
),
elevation: 4.0,
margin: EdgeInsets.all(4.0),
child: InkWell(
radius: 4.0,
child: getCardView(context),
onTap: (){
Navigator.push(
context,
MaterialPageRoute(builder: (context) =>
DetailProduct(
detail: detail,
),
),
);
},
),
);
}
//////
getCardView(BuildContext context){
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Hero(
tag: detail,//this key /tag will be the same with another page also
child: Container(
height: 200.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
image: DecorationImage(
image: NetworkImage("${detail.image_url}"
,
),
fit: BoxFit.cover),
),
), ),
// Image.asset("images/ndz.jpg"),
Padding(
padding: const EdgeInsets.all(4.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("sold by: "+
detail.vendor_name,
overflow: TextOverflow.ellipsis,
style: Theme
.of(context)
.textTheme
.body2,
),
Text("product: "+
detail.productName,
softWrap: true,
overflow: TextOverflow.ellipsis,
style: Theme
.of(context)
.textTheme
.body2,
),
Text("price: ${detail.price} ${detail.amount}"
,
softWrap: true,
overflow: TextOverflow.ellipsis,
style: Theme
.of(context)
.textTheme
.body2,
),
],
),
),
],
);
}
}
when it is clicked/tapped it will direct to the other page which will provide full details about the products
import 'package:flutter/material.dart';
import 'package:kilimo_biashara1/Model/ProductDetail.dart';
import 'package:kilimo_biashara1/payment/Payment.dart';
class DetailProduct extends StatelessWidget {
final ProductDetail detail;
DetailProduct({this.detail});
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
persistentFooterButtons: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(15.0, 5.0, 40.0, 5.0),
child: new Text("SHOPPING",
style: new TextStyle(fontSize: 25.0,color: Colors.green,fontWeight: FontWeight.bold),
),
),
new FlatButton(
child: new Icon(Icons.shopping_cart,size: 35.0,),
onPressed:(){
Navigator.of(context).push(
new MaterialPageRoute(
builder: (BuildContext context)=>new Payment()));
} ,
),
],
body:new Scaffold(
body:ListView(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Hero(
tag: detail,
child: Container(
height: 250.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16.0),
shape: BoxShape.rectangle,
image: DecorationImage(
image: NetworkImage(
"${detail.image_url}",
),
fit: BoxFit.cover,
),
),
),
),
// Image.asset("images/ndz.jpg"),
SizedBox(
height: 16.0,
),
Text("vendor name: "+
detail.vendor_name,
style: Theme.of(context).textTheme.title,
),
SizedBox(
height: 16.0,
),
Text("product name: "+
detail.productName,
style: Theme.of(context).textTheme.subhead,
),
SizedBox(
height: 16.0,
),
Text("vendor place: "+
detail.place,
style: Theme.of(context).textTheme.subhead,
),
SizedBox(
height: 16.0,
),
Text("price: ${detail.price} ${detail.amount}",
style: Theme.of(context).textTheme.subhead,
),
SizedBox(
height: 16.0,
),
Text("short description: "+
detail.description,
style: Theme.of(context).textTheme.subhead,
),
SizedBox(
height: 16.0,
),
Text("Category: "+detail.category,
style: Theme.of(context).textTheme.subhead,
),
SizedBox(
height: 16.0,
),
Text("contacts: ${detail.phone}",
style: Theme.of(context).textTheme.subhead,
),
],
),
),
],
),
),
),
);
}
}
after following these steps you should reach to a point where you parse data from the API with hero widget

Related

Filter search filter data showing from Api using provider

how to implement search filter with API using provider? i have post Api when user hit Api it send back data to the user
My api
Future<List<Searchmodel>>Search(String keyword) async {
final response=await http.post(Uri.parse('https://example.com/api/library/search'),body: {
'Keyword': keyword,
});
if(response.statusCode==200){
final json=response.body..toString().replaceAll("\n","");
var data=jsonDecode(json);
final Iterable fetch = data["success"];
return fetch.map((category) => Searchmodel.fromJson(category)).toList();
}else{
throw Exception("Unable to perform request!");
}
}
//model class
class Searchmodel {
Searchmodel({
required this.id,
required this.name,
required this.file,
required this.categoryId,
required this.subcategoryId,
required this.userId,
required this.status,
required this.createdAt,
required this.updatedAt,});
Searchmodel.fromJson(dynamic json) {
id = json['id'];
name = json['name'];
file = json['file'];
categoryId = json['category_id'];
subcategoryId = json['subcategory_id'];
userId = json['user_id'];
status = json['status'];
createdAt = json['created_at'];
updatedAt = json['updated_at'];
}
int? id;
String? name;
String? file;
int? categoryId;
int? subcategoryId;
String? userId;
String? status;
String? createdAt;
String? updatedAt;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['id'] = id;
map['name'] = name;
map['file'] = file;
map['category_id'] = categoryId;
map['subcategory_id'] = subcategoryId;
map['user_id'] = userId;
map['status'] = status;
map['created_at'] = createdAt;
map['updated_at'] = updatedAt;
return map;
}
}
//UI
import 'package:flutter/material.dart';
import 'package:livinghopegeetzaboor/Statemanagment/Library/LibraryProvider.dart';
import 'package:livinghopegeetzaboor/constant/AppColor.dart';
import 'package:livinghopegeetzaboor/sacreen/Geet/Geetsubcategory.dart';
import 'package:livinghopegeetzaboor/services/Services.dart';
import 'package:provider/provider.dart';
class Homepage extends StatefulWidget {
#override
State<Homepage> createState() => _HomepageState();
}
class _HomepageState extends State<Homepage> {
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Center(
child: Padding(
padding: EdgeInsets.fromLTRB(0, 0, 0, 0),
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
height: 100,
width: 100,
child: Image.asset('images/logo.png')
),
// Note: Same code is applied for the TextFormField as well
// Note: Same code is applied for the TextFormField as well
// Note: Same code is applied for the TextFormField as well
Padding(
padding: EdgeInsets.all(40),
child:Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20.0),
color: AppColor.color,
),
height: 80,
child: Padding(
padding: EdgeInsets.all(8.0),
child: Row(
children: [
Expanded(
child: TextField(
decoration: InputDecoration(
suffixIcon: Icon(Icons.search,
size: 40,
),
enabledBorder: OutlineInputBorder(
borderSide: const BorderSide(color: Colors.white, width:2),
),
filled: true, //<-- SEE HERE
fillColor: Colors.white,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(20),
),
hintText: 'Search for any songs',
),
),
),
SizedBox(width: 3,),
// Container(
// height: 59,
// width: 59,
// color:Colors.white,
// constraints: const BoxConstraints(
//
// ),
// // child: Icon(
// // Icons.search_sharp,
// // size: 40,
// // color: AppColor.color,
// // ),
// )
],
),
),
),
),
Consumer<LibraryProvider>(
builder:(BuildContext context, value, Widget? child) {
if(value.isloading){
return CircularProgressIndicator(
color: AppColor.color,
);
}else{
return GridView.builder(
itemCount:value.library.length,
scrollDirection: Axis.vertical,
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 4.0,
mainAxisSpacing: 4.0
),
itemBuilder: (BuildContext context, int index){
var subtitle=value.library[index].name2;
return Padding(
padding: EdgeInsets.all(10),
child: Material(
color: Colors.white.withOpacity(0.0),
child: InkWell(
splashColor: Colors.orange,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
color: Colors.black,
),
//color: Colors.black,
height:200,
width: 200,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.all(5),
child: Center(
child: Text(value.library[index]!.name.toString(),style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 18
),),
),
),
SizedBox(height: 4,),
Padding(
padding: EdgeInsets.fromLTRB(10, 0, 0, 0),
child: Center(
child: subtitle!=null ? Text(value.library[index].name2.toString(),style: TextStyle(
color: Colors.white,
fontSize: 12
),):Text('',style: TextStyle(
color: Colors.white,
fontSize: 12
),)
),
),
],
),
),
onTap: (){
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Geetsubcategory(id: value.library[index].id,)),
);
},
),
),
);
},
);
}
},
)
],
),
// your main content
),
),
)
);
}
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_){
Provider.of<LibraryProvider>(context,listen: false).fetchlibary();
// Add Your Code here.
});
}
}
You need to add the api call from inside your provider function.
Like this
class SearchProvider with ChangeNotifier, DiagnosticableTreeMixin {
List<dynamic>? _items;
List<dynamic>? get items => _items;
void searchItems(String val) async {
_items = await ApiCall.search(val);
notifyListeners();
}
}
You can create a common object of provider on the page to use throughout your page like this.
SearchProvider _provider = SearchProvider();
and use it like this in your provider and consumer on the same page
ChangeNotifierProvider<SearchProvider>(
create: (_) => _provider,
child: Column(
children: [
... // other code
Consumer<SearchProvider>(
builder: (context, provider, w) {
if (provider.items != null && provider.items!.isNotEmpty) {
return SearchList(list: provider.items);
}
return const Center(child: Text("Nothing found"));
},
),
]),
),
Then setup one TextField to type & fire search query. You can setup onSubmitted callback of the Textfield to call your provider method.
TextField(
onSubmitted: (val) {
if(val.length > 0){
_provider.searchItems(val);
}
else {
_provider.getAll();
}
},
decoration: const InputDecoration(
labelText: 'Search',
suffixIcon: Icon(Icons.search),
),
Using this code will call the provider method when you press enter after typing some query in TextField. That provider method will then call the api & store the response in a List then call notifyListeners() from that method to notify all the consumers. Those consumer then try to read the list & create a listview if they find data in the list.
Here is an example of full working code:
https://dartpad.dev/?id=a5950f2da95d58e32dc9176433d9bcb3
I have also added the onload list which can be filtered from api itself & it user delete search query it will again call the full list api for data.
Hope this will help you understand things better using provider.

How to increase quantity of same item click in Flutter (Bloc)

In HomePage.dart i am fetching an api data by using http package and flutter_bloc for the architecture. Now once i get the data and if i select any of the item in HomePage.dart then it will be added in MenuCart.dart. Now the problem i am facing is
If i click the same item more than one time then it shows duplicate card widget.
In MenuCart.dart i have added two buttons + & - and i increases the quantity to 3 then i decided to delete the item. Now i go back to previous screen and again i add that same item and if i go to MenuCart.dart then quantity displays 4.
What i am Expecting
It should not generate duplicate card instead quantity should increment.
After i delete an item and again add the same item then quantity must show 1.
In HomePage.dart i show very limited amount of code because of lengthy, but i have added the code of bloc and MenuCart.dart.
HomePage.dart
GestureDetector(
onTap: () {
BlocProvider.of<MenuCartBloc>(context)
.add(AddToCart(data[index])); // Here adding an item in MenuCart
},
child: Container(
child: Center(
child: Padding(
padding: const EdgeInsets.all(4.5)
child: Text('ADD'),
),
),
),
);
DishMenuTypesId.dart (Model)
class DishMenuTypesIdData {
String? id;
int? status;
int? quantity;
String? dishPrice;
String? photo;
DishMenuTypesIdData({
this.id,
this.status,
this.quantity = 1,
this.dishPrice,
this.photo,
});
DishMenuTypesIdData.fromJson(Map<String, dynamic> json) {
id = json['id']?.toString();
status = json['status']?.toInt();
quantity = 1;
dishPrice = json['dish_price']?.toString();
photo = json['photo']?.toString();
}
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data['id'] = id;
data['status'] = status;
data["quantity"] = quantity;
data['dish_price'] = dishPrice;
data['photo'] = photo;
return data;
}
}
class DishMenuTypesId {
String? message;
List<DishMenuTypesIdData?>? data;
DishMenuTypesId({
this.message,
this.data,
});
DishMenuTypesId.fromJson(Map<String, dynamic> json) {
message = json['message']?.toString();
if (json['data'] != null) {
final v = json['data'];
final arr0 = <DishMenuTypesIdData>[];
v.forEach((v) {
arr0.add(DishMenuTypesIdData.fromJson(v));
});
this.data = arr0;
}
}
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data['message'] = message;
if (this.data != null) {
final v = this.data;
final arr0 = [];
v!.forEach((v) {
arr0.add(v!.toJson());
});
data['data'] = arr0;
}
return data;
}
}
MenuCartBloc.dart
class MenuCartBloc extends Bloc<MenuCartEvent, MenuCartState> {
MenuCartBloc() : super(MenuLoaded([]));
List<DishMenuTypesIdData> cart = [];
#override
Stream<MenuCartState> mapEventToState(
MenuCartEvent event,
) async* {
yield MenuLoading();
try {
if (event is AddToCart) {
final itemExist = cart.where((e) => e.id == event.dish.id);
if (itemExist.isNotEmpty) {
print("Not Empty"); // Here i am expecting to show qty 2 if i click an item more than 1 time
} else {
print("Empty");
cart.add(event.dish);
}
} else if (event is DeleteToCart) {
cart.remove(event.dish);
} else if (event is ClearAllCart) {
cart = [];
}
yield MenuLoaded(cart);
} catch (e) {
yield MenuFailed(e.toString());
}
}
}
MenuCartEvent.dart
abstract class MenuCartEvent {}
class AddToCart extends MenuCartEvent {
late final DishMenuTypesIdData dish;
AddToCart(this.dish);
}
class DeleteToCart extends MenuCartEvent {
late final DishMenuTypesIdData dish;
DeleteToCart(this.dish);
}
class ClearAllCart extends MenuCartEvent {}
MenuCartState.dart
abstract class MenuCartState {}
class MenuLoading extends MenuCartState {}
class MenuLoaded extends MenuCartState {
final List<DishMenuTypesIdData> cart;
MenuLoaded(this.cart);
}
class MenuFailed extends MenuCartState {
final String message;
MenuFailed(this.message);
}
MenuCart.dart
Widget build(BuildContext context) {
var height = MediaQuery.of(context).size.height;
var width = MediaQuery.of(context).size.width;
var lang = translator.activeLanguageCode;
return Container(
child: Padding(
padding: const EdgeInsets.only(top: 20.0, left: 10.0, right: 10.0),
child: BlocBuilder<MenuCartBloc, MenuCartState>(
builder: (context, state) {
DishMenuTypesIdData _cart = DishMenuTypesIdData();
if (state is MenuLoading) {
return PlatformCircularProgressIndicator();
} else if (state is MenuLoaded) {
return SingleChildScrollView(
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Your Reservation Details'.tr(),
style: TextStyle(
color: TuxedoColor.blackColor,
fontSize: lang == "en" ? 14.0 : 15.0,
fontFamily: lang == "en"
? 'OpenSansBold'
: 'ArabicSemiBold'),
),
GestureDetector(
onTap: () {
BlocProvider.of<MenuCartBloc>(context)
.add(ClearAllCart());
},
child: Row(
children: [
Icon(
Icons.close,
color: TuxedoColor.redColor,
size: 15.0,
),
Text(
'Clear All'.tr(),
style: TextStyle(
color: TuxedoColor.redColor,
fontSize: 12.0,
fontFamily: lang == "en"
? 'OpenSansRegular'
: 'ArabicLight'),
),
],
),
)
],
),
SizedBox(
height: 25.0,
),
Container(
height: height * 0.3,
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: state.cart.length,
itemBuilder: (BuildContext context, int index) {
_cart = state.cart[index];
return Dismissible(
key: UniqueKey(),
background: Container(
alignment: AlignmentDirectional.centerEnd,
color: TuxedoColor.redBrightColor,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(
Icons.delete,
color: TuxedoColor.whiteColor,
),
),
),
onDismissed: (direction) {
BlocProvider.of<MenuCartBloc>(context)
.add(DeleteToCart(_cart));
setState(() {
state.cart.removeAt(index);
});
},
direction: DismissDirection.endToStart,
child: Card(
child: Row(
children: [
Stack(
children: [
ClipRRect(
borderRadius:
BorderRadius.circular(10.0),
child: CachedNetworkImage(
imageUrl:
_cart.photo!,
placeholder: (context, url) => Center(
child:
PlatformCircularProgressIndicator()),
errorWidget:
(context, url, error) =>
Icon(Icons.error),
width: width * 0.25,
height: height * 0.1,
fit: BoxFit.fill,
),
),
Positioned(
bottom: 0,
right: 0,
child: Container(
decoration: BoxDecoration(
color: TuxedoColor.redColor,
borderRadius: lang == "en"
? BorderRadius.only(
bottomRight:
Radius.circular(
10),
)
: BorderRadius.only(
bottomLeft:
Radius.circular(
10),
)),
height: 25.0,
width: 45.0,
child: Padding(
padding: const EdgeInsets.only(
left: 3.0, right: 3.0),
child: FittedBox(
fit: BoxFit.fitWidth,
child: Text(
'${_cart.dishPrice!} \SR',
style: TextStyle(
color: TuxedoColor
.whiteColor,
fontFamily: lang == "en"
? 'OpenSansSemiBold'
: 'ArabicSemiBold'),
),
),
),
),
)
],
),
Padding(
padding:
const EdgeInsets.only(left: 10.0),
child: Column(
children: [
Container(
width: width * 0.6,
child: Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Container(
height: 25.0,
child: Row(
children: [
MaterialButton(
color: TuxedoColor
.redColor,
shape: CircleBorder(),
onPressed: () {
setState(() {
var minus = state
.cart[index]
.quantity;
if (minus !=
null) {
minus--;
state
.cart[index]
.quantity = minus;
}
});
},
child: Text(
'-',
style: TextStyle(
color: Colors
.white,
fontSize: 20),
),
),
Text(_cart.quantity
.toString()),
MaterialButton(
color: TuxedoColor
.redColor,
shape: CircleBorder(),
onPressed: () {
setState(() {
var add = state
.cart[index]
.quantity;
if (add != null) {
add++;
state
.cart[index]
.quantity = add;
}
});
},
child: Text(
'+',
style: TextStyle(
color: Colors
.white,
fontSize: 20),
),
),
],
),
),
GestureDetector(
onTap: () {
BlocProvider.of<
MenuCartBloc>(
context)
.add(DeleteToCart(
state.cart[
index]));
},
child: Icon(
Icons.delete,
color:
TuxedoColor.redColor,
),
)
],
),
),
SizedBox(
height: 15.0,
)
],
),
),
],
),
),
);
}),
),
],
),
);
} else if (state is MenuFailed) {
return Center(child: Text(state.message));
}
return Container();
},
)),
);
}
Something like that should work:
final itemExist = cart.firstWhere((e) => e.id == event.dish.id, orElse: () => null);
if (itemExist != null) {
itemExist.quantity++;
print("Not Empty"); // Here i am expecting to show qty 2 if i click an item more than 1 time
} else {
print("Empty");
cart.add(event.dish);
}
Basically, we need to check whether the cart already has a dish with the same id or not. If the cart contains the dish with this id we increment dish quantity else we just add a dish to the cart.
Besides that, I see a potential problem with your code. Bloc to work properly requires either creating new instances of models or properly implementing of equals and hashCode. So even the code above may not work properly due to this problem. Be aware of it. You can fix it using this package.

How to update the amount value when the user add data and store it to list view flutter

Hi guys I'm new in flutter and need your help. In this case, i'm able to add the budget and retrieve it to list view. but now I'm trying show only 1 list for each category but count for the amount? so if the user add the new budget, the amount will be count and updated to the list view (with the same currency). Anyone can help me how to list the the same category but with updated amount if the user add the budget in the budget code? Please help. Thank you. For the database, I'm using Firestore in the firebase
choose budget category
import 'package:flutter/material.dart';
import 'package:monger_app/localization/localization_constants.dart';
import 'package:monger_app/page/detail.dart';
import 'package:monger_app/theme/colors.dart';
class BudgetSettings extends StatefulWidget {
#override
_BudgetSettingsState createState() => _BudgetSettingsState();
}
class _BudgetSettingsState extends State<BudgetSettings> {
List<Container> categorylist = new List();
var character=[
{"name":"Food", "image":"food.png"},
{"name":"Social-Life", "image":"travel.png"},
{"name":"Transportation", "image":"transportation.png"},
{"name":"Beauty", "image":"makeup.png"},
{"name":"Household", "image":"household.png"},
{"name":"Education", "image":"education.png"},
{"name":"Health", "image":"health.png"},
{"name":"Gift", "image":"gift.png"},
{"name":"Other", "image":"other.png"},
];
_makelist() async {
for (var i = 0; i < character.length; i++) {
final newcharacter = character[i];
final String image = newcharacter["image"];
categorylist.add(
new Container(
padding: new EdgeInsets.all(20.0),
child: new Card( child:
SingleChildScrollView(
child: new Column(
children: <Widget>[
new Hero(
tag: newcharacter['name'],
child: new Material(
child: new InkWell(
onTap: ()=> Navigator.of(context).push(new MaterialPageRoute(
builder: (BuildContext context)=> new Detail(name: newcharacter['name'], image: image,),
)),
child:
new Image.asset("assets/$image", fit: BoxFit.contain,),
)
),
),
//new Image.asset('assets/$image', fit: BoxFit.cover,),
new Padding(padding: new EdgeInsets.all(5.0),),
new Text(newcharacter['name'], style: new TextStyle(fontSize: 18.0),),
],
),
),
),
),
);
}
}
#override
void initState() {
_makelist();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: primary,
title: Text(getTranslated((context), "budget_settings"),
),),
body: new GridView.count(
crossAxisCount: 2,
children: categorylist,
),
);
}
}
Add budget code
import 'package:dropdownfield/dropdownfield.dart';
import 'package:flutter/material.dart';
import 'package:monger_app/localization/localization_constants.dart';
import 'package:monger_app/page/account.dart';
import 'package:monger_app/page/budgetsettings.dart';
import 'package:monger_app/theme/colors.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
class Detail extends StatefulWidget {
Detail({this.name, this.image});
final String name;
final String image;
#override
_DetailState createState() => _DetailState();
}
class _DetailState extends State<Detail> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text(getTranslated((context), "budget_settings"),),
elevation: 0,
brightness: Brightness.light,
backgroundColor: primary,
leading: IconButton(
onPressed: (){
Navigator.pop(context, MaterialPageRoute(builder: (context) => Account()));
},
icon: Icon(Icons.arrow_back_ios,
size: 20,
color: Colors.black,),
),
),
body: new ListView(
children: <Widget>[
new Container(
height: 250.0,
child:
new Hero(tag: widget.name,
child: new Material(
child: new InkWell(
child: new Image.asset("assets/${widget.image}", fit: BoxFit.contain,),
)
))
),
new Name(name: widget.name,),
],
),
);
}
}
class Name extends StatefulWidget {
Name({this.name});
final String name;
#override
_NameState createState() => _NameState();
}
class _NameState extends State<Name> {
#override
String selectCurrency;
final currencySelected = TextEditingController();
var _formKey = GlobalKey<FormState>();
List <String> currencycategories = [
"IDR",
"MYR",
"USD",
"CNY"
];
Widget build(BuildContext context) {
final amount = TextEditingController();
FirebaseFirestore firestore = FirebaseFirestore.instance;
CollectionReference collect= firestore.collection("Budget");
final FirebaseAuth _auth = FirebaseAuth.instance;
final User user =_auth.currentUser;
final uid = user.uid;
return Form(
key: _formKey,
child: Padding(
padding: EdgeInsets.all(20.0),
child: Column(
children: <Widget>[
Container(
child: Center(
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.all(20.0),
child: Text(
widget.name,
textAlign: TextAlign.center,
style: new TextStyle(
fontSize: 25.0,
color: primary,
fontWeight: FontWeight.bold,
),
),
),
Row(
children: <Widget> [
new Expanded(child: new DropDownField(
controller: currencySelected,
labelText: getTranslated((context), "currency_hint"),
enabled: true,
itemsVisibleInDropdown: 4,
items: currencycategories,
onValueChanged: (dynamic value) {
selectCurrency = value;
},
value: selectCurrency,
required: false,
),
flex: 2,
),
new SizedBox(
width: 10.0,
),
new Expanded(child: TextFormField(
validator: (input) {
if (input.isEmpty) return 'Please fill up the text fields';
},
controller: amount,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: getTranslated((context), "budget_enter"),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: secondary),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: secondary),
),
),
),
flex: 2,
)],
),
Padding(
padding: EdgeInsets.all(20.0),
child: ElevatedButton(
onPressed: () async {
final FirebaseAuth _auth = FirebaseAuth
.instance;
final User user = _auth.currentUser;
final uid = user.uid;
if(!_formKey.currentState.validate()){
return;
}
_formKey.currentState.save();
collect.add({
'name': this.widget.name,
'currency': selectCurrency,
'amount': amount.text,
});
amount.text = "";
Navigator.pop(context);
},
child: Text(getTranslated((context), "save_button").toUpperCase(), style: TextStyle (
fontSize: 14,
)),
style: ButtonStyle(
padding: MaterialStateProperty.all<EdgeInsets>(EdgeInsets.all(20.0)),
foregroundColor: MaterialStateProperty.all<Color>(Colors.white),
backgroundColor: MaterialStateProperty.all<Color>(Colors.pink),
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
side: BorderSide(color: secondary)
),
),
),
),
)
],
),
)
)
],
),
)
);
}
}
Retrieve it to list view code
import 'package:flutter/material.dart';
import 'package:monger_app/localization/localization_constants.dart';
import 'package:monger_app/page/transaction.dart';
import 'package:monger_app/theme/colors.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import './transaction.dart' as expense;
import './transaction.dart' as income;
class TransactionMonthly extends StatefulWidget {
#override
_TransactionMonthlyState createState() => _TransactionMonthlyState();
}
class _TransactionMonthlyState extends State<TransactionMonthly> with SingleTickerProviderStateMixin {
TabController controller;
#override
void initState() {
controller = new TabController(vsync: this, length: 2);
super.initState();
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(getTranslated(context, 'budget_title'),),
elevation: 0,
brightness: Brightness.light,
backgroundColor: primary,
leading: IconButton(
onPressed: (){
Navigator.pop(context, MaterialPageRoute(builder: (context) => Transactions()));
},
icon: Icon(Icons.arrow_back_ios,
size: 20,
color: Colors.black,),
),
),
body: StreamBuilder(
stream: FirebaseFirestore.instance.collection('Budget').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasData) {
return ListView(
children: snapshot.data.docs.map((document) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(children: [
SizedBox(width: 10,),
Text(document.get('name'),style: TextStyle(fontSize: 16,
color: primary,
fontWeight: FontWeight.w600
),),
],
),
SizedBox(height: 10,),
Row(children: [
SizedBox(width: 10,),
Text(document.get('currency'),style: TextStyle(fontSize: 16,
color: primary,
fontWeight: FontWeight.w600
),),
SizedBox(width: 10,),
Text(document.get('amount'),style: TextStyle(fontSize: 16,
color: primary,
fontWeight: FontWeight.w600
),),
],
),
SizedBox(height: 8,),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
GestureDetector(
onTap: (){
Widget cancelButton = FlatButton(child: Text("Cancel"),
onPressed: (){
Navigator.pop(context);
},
);
Widget deleteButton = FlatButton(child: Text("Delete"),
onPressed: (){
FirebaseFirestore.instance.collection('Budget').doc(document.id).delete();
Navigator.pop(context);
},
);
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Delete Budget'),
content: Text('Are you sure want to delete it?'),
actions: [
cancelButton,
deleteButton,
],
),
);
//_showDeleteDialog();
//_showDeleteDialog(document: document);
},
child: Row(
children: [
Icon(Icons.delete_forever_outlined,
color: Colors.red,
),
SizedBox(width: 6,),
Text('Delete', style: TextStyle(fontSize: 16,
color: Colors.red,
fontWeight: FontWeight.w600
), ),
],
),
)
],
)
],)
);
}).toList(),
);
}
return Center(
child: CircularProgressIndicator(),
);
}
),
);
}
}
I think what you want is a sort of group by name so that you show the food category with 160 in your example instead of two times the same category (Correct me if i am wrong). What i suggest is to first create a class named Budget with your three attributes name, currency and amount. When you receive your data in TransactionMonthly Streambuilder you need to group your snapshot data by name. Once you done that you create a list of budgets List<Budget> budgets so that you add the sum of amount per name of budgets.
Here is a sample code that you can use, you can simply copy paste it and try on DartPad by yourself:
import 'package:collection/collection.dart';
//DATA coming from your Firebase collection
Map<String, dynamic> data1 = {
'name':"Food",
'currency': "IDR",
'amount': 100,
};
Map<String, dynamic> data2 = {
'name':"Food",
'currency': "IDR",
'amount': 60,
};
Map<String, dynamic> data3 = {
'name':"Beauty",
'currency': "USD",
'amount': 120,
};
//Your Budget class
class Budget{
String currency;
String name;
int amount;
#override
String toString(){
return "Budget: {currency: $currency, name: $name, amount: $amount}";
}
Budget({required this.currency, required this.name, required this.amount});
}
void main(){
List<Map<String, dynamic>> snapshot = [data1, data2, data3];
List<Budget> budgets = [];
//you group your budgets by name so you will have: {'Food': [data1, data2], 'Beauty':[data3]}
final groupByName = groupBy(snapshot, (Map<String, dynamic> doc)=>doc['name']);
print(groupByName);
int totalAmount = 0;
String currency = "";
groupByName.forEach((name, value){
//because we grouped by name every value will have same currency
currency = value[0]['currency'];
//Here we calculate the sum of all Food
totalAmount = value.map((e)=>e['amount']).reduce((v, e)=> v+e);
budgets.add(Budget(currency: currency, amount: totalAmount, name: name));
});
//budgets is your final list to use in your listView
print(budgets);
}

How to solve: type 'List<dynamic>' is not a subtype of type 'String' [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 2 years ago.
Improve this question
I want to update image from listofTaskNotApprove class which it will pass the object of documentsnapshot into the EditTaskNotApprove. Before I update the image, I need to display specific of image where the user will be select specific info from listofTaskNotApprove. The problem is how to display the current index of image into the new screen?
ListOfTaskNotAccepted class.
import 'package:carousel_pro/carousel_pro.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:fyp/screen/RecordOfficer/EditTaskNotApprove.dart';
import 'package:fyp/shared/Loading.dart';
import 'package:google_fonts/google_fonts.dart';
class ListOfTaskNotAccepted extends StatefulWidget {
#override
_ListOfTaskNotAcceptedState createState() => _ListOfTaskNotAcceptedState();
}
final FirebaseAuth auth = FirebaseAuth.instance;
Stream<QuerySnapshot> getUser(BuildContext context) async* {
final FirebaseUser rd = await auth.currentUser();
yield* Firestore.instance.collection("Task").where('uid',isEqualTo: rd.uid).where("verified", isEqualTo: 'TidakSah').snapshots();
}
class _ListOfTaskNotAcceptedState extends State<ListOfTaskNotAccepted> {
List<NetworkImage> _listOfImages = <NetworkImage>[];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Aduan Tidak Diterima"),
backgroundColor: Colors.redAccent,
),
body: Container(
child: StreamBuilder(
stream: getUser(context),
builder: (context, snapshot){
if (snapshot.hasError || !snapshot.hasData) {
return Loading();
} else{
return ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (BuildContext context, int index){
DocumentSnapshot da = snapshot.data.documents[index];
_listOfImages =[];
for(int i =0; i <da['url'].length; i++){
_listOfImages.add(NetworkImage(da['url'][i]));
}
return Card(
child:ListTile(
title: Container(
alignment: Alignment.centerLeft,
child: Column(
children: <Widget>[
SizedBox(height: 5.0),
Container(alignment: Alignment.centerLeft,
child: Row(
children: [
Text("Sumber Aduan: ", style: GoogleFonts.asap(fontWeight: FontWeight.bold)),
Text(da['sumberAduan'], style: GoogleFonts.asap(fontWeight: FontWeight.bold)),
],
),
),
SizedBox(height: 5.0),
Container(alignment: Alignment.centerLeft,
child: Row(
children: [
Text("Nombor Aduan: ", style: GoogleFonts.lato(fontWeight: FontWeight.bold)),
Text(da['noAduan'], style: GoogleFonts.lato(fontWeight: FontWeight.bold)),
],
),
),
SizedBox(height: 5.0),
Container(alignment: Alignment.centerLeft,
child: Row(
children: [
Text("Lokasi: ", style: GoogleFonts.lato(fontWeight: FontWeight.bold)),
Text(da['kawasan'] + " " + da['naJalan'], style: GoogleFonts.lato(fontWeight: FontWeight.bold)),
],
),
),
SizedBox(height: 5.0),
Container(alignment: Alignment.centerLeft,
child: Row(
children: [
Text("Kategori: ", style: GoogleFonts.arimo(fontWeight: FontWeight.w500)),
Text(da['kategori'], style: GoogleFonts.arimo(fontWeight: FontWeight.w500)),
],
),
),
Column(
children: [
Container(
margin: EdgeInsets.all(10.0),
height: 200,
decoration: BoxDecoration(
color: Colors.white
),
width: MediaQuery.of(context).size.width,
child: Carousel(
boxFit: BoxFit.cover,
images: _listOfImages,
autoplay: false,
indicatorBgPadding: 5.0,
dotPosition: DotPosition.bottomCenter,
animationCurve: Curves.fastLinearToSlowEaseIn,
animationDuration: Duration(milliseconds: 2000),
),
)
],
)
],
),
),
subtitle: Container(
child: Column(
children: [
SizedBox(height: 5.0),
Container(alignment: Alignment.centerLeft,
child: Row(
children: [
Text("Catatan: ", style: GoogleFonts.arimo(fontWeight: FontWeight.w500)),
Text(da['comments'], style: GoogleFonts.arimo(fontWeight: FontWeight.w500)),
],
),
),
],
),
),
onTap: () {Navigator.push(context, MaterialPageRoute(builder: (context) => EditTask(da:da)));}
)
);
});
}
}),
)
);
}
}
Here is EditTask class which I need to display current index of image that selected by user.
import 'package:carousel_pro/carousel_pro.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class EditTask extends StatefulWidget {
final DocumentSnapshot da;
const EditTask({Key key, this.da}) : super(key: key);
#override
_EditTaskState createState() => _EditTaskState(da);
}
class _EditTaskState extends State<EditTask> {
DocumentSnapshot da;
_EditTaskState(DocumentSnapshot da){
this.da = da;
}
TextEditingController _noAduan;
TextEditingController _sumberAduan;
TextEditingController _kategori;
DateTime myDateTime = DateTime.now();
#override
void initState(){
super.initState();
_noAduan = TextEditingController(text: widget.da.data['noAduan']);
_sumberAduan =TextEditingController(text: widget.da.data['sumberAduan']);
_kategori = TextEditingController(text: widget.da.data['kategori']);
myDateTime = (da.data['date']).toDate();
_listOfImages = NetworkImage(da.data['url']) as List<NetworkImage>; // this line show the error
}
List <String> sumber = <String> ['Sistem Aduan MBPJ', 'Sistem Aduan Waze', 'Sistem Aduan Utiliti'];
List <String> kate = <String> ['Segera', 'Pembaikan Biasa'];
String kategori;
String sumberAduan;
List <NetworkImage> _listOfImages = <NetworkImage>[];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Kemaskini Aduan"),
backgroundColor: Colors.redAccent,
),
body: Container(
padding: const EdgeInsets.all(16.0),
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
SizedBox(height: 10.0),
TextFormField(
decoration:InputDecoration(
hintText: myDateTime.toString(),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5))),
onChanged: (value){
setState(() {
myDateTime = value as DateTime;
print(myDateTime);
});
},
),
SizedBox(height: 10.0),
TextFormField(
decoration:InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5))),
controller: _noAduan,
),
SizedBox(height: 10.0),
DropdownButtonFormField(
hint:Text(widget.da.data['sumberAduan']),
decoration: InputDecoration(
prefixIcon: Icon(Icons.perm_contact_calendar),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(5))),
isExpanded: true,
value: sumberAduan,
onChanged: (newValue) {
setState(() {
sumberAduan = newValue;
_sumberAduan.text = sumberAduan;
});
},
items: sumber.map((sum){
return DropdownMenuItem(
value: sum,
child: new Text(sum),
);
}).toList(),
),
SizedBox(height: 10.0),
DropdownButtonFormField(
hint:Text(widget.da.data['kategori']),
decoration: InputDecoration(
prefixIcon: Icon(Icons.perm_contact_calendar),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(5))),
isExpanded: true,
value: kategori,
onChanged: (newValue) {
setState(() {
kategori = newValue;
_kategori.text = kategori;
});
},
items: kate.map((ka){
return DropdownMenuItem(
value: ka,
child: new Text(ka),
);
}).toList(),
),
Container(
margin: EdgeInsets.all(10.0),
height: 200,
decoration: BoxDecoration(
color: Colors.white
),
width: MediaQuery.of(context).size.width,
child: Carousel(
boxFit: BoxFit.cover,
autoplay: false,
//images: _listOfImages,
indicatorBgPadding: 5.0,
dotPosition: DotPosition.bottomCenter,
animationCurve: Curves.fastLinearToSlowEaseIn,
animationDuration: Duration(milliseconds: 2000),
),
)
],
),
),
)
);
}
}
the image that need to display for update image
This is how I want to display image in class EditTask when the user want to update information from ListOfTaskNotApprove
The error show that "type 'List' is not a subtype of type 'String'"
Can someone help me? because I had tried many method to solve this problem but it didn't work for me.
You say:
_listOfImages = NetworkImage(da.data['url']) as List<NetworkImage>; // this line show the error
Yes, you can't cast a NetworkImage to a List<NetworkImage>. You probably meant:
_listOfImages = [ NetworkImage(da.data['url']) ] as List<NetworkImage>;
The thing is your are getting a list of url's from Firebase storage which are Strings but you are adding this strings into a list of type Network Image which is wrong. As a list of string cannot be converted to a list of Network Image.
There are 2 ways to resolve this-
Change your list type to List and then wherever you show this image use
list data as an argument for Network Image.
List<String> urls=new List();
//suppose list is not empty
....
return NetworkImage(urls[i]);
....
While adding url's to your list add an Network Image object and directly use the list item while showing images.
List<NetworkImage> list=new List();
list.add(NetworkImage('some url'));
....
return list[i];
....

How to pass data back from a widget?

I have a screen where users can add a location. Here, I have separated all my widgets into there own files as illustrated below;
import 'package:cached_network_image/cached_network_image.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:fluttershare/pages/location/location_help_screen.dart';
import 'package:fluttershare/widgets/common_widgets/customDivider.dart';
import 'package:uuid/uuid.dart';
import '../../widgets/camp_type_select.dart';
import '../../widgets/extra_location_notes.dart';
import '../../widgets/location_input.dart';
import '../../widgets/opening_times.dart';
import '../../widgets/post_media.dart';
import '../../widgets/space_avalibility.dart';
import '../../widgets/utility_type_select.dart';
import '../../widgets/width_restriction.dart';
import '../../widgets/height_restriction.dart';
import '../../models/locations.dart';
import '../../models/user.dart';
import '../home.dart';
class AddNewLocation extends StatefulWidget {
static const routeName = '/add-new-location';
final User currentUser;
AddNewLocation({this.currentUser});
_AddNewLocationState createState() => _AddNewLocationState();
}
class _AddNewLocationState extends State<AddNewLocation> {
String postId = Uuid().v4();
final _scaffoldKey = GlobalKey<ScaffoldState>();
PlaceLocation _pickedLocation;
int storyPostCount = 0;
bool isLoading = false;
void _selectPlace(double lat, double lng) {
_pickedLocation = PlaceLocation(lattitude: lat, longitude: lng);
}
getLocationPostCount() async {
setState(() {
isLoading = true;
});
QuerySnapshot snapshot = await locationPostRef
.document(currentUser.id)
.collection('user_location_posts')
.getDocuments();
setState(() {
storyPostCount = snapshot.documents.length;
});
}
createLocationPostInFirestore(
{String mediaUrl,
String description,
double heightRestriction,
double widthRestriction}) {
locationPostRef
.document(currentUser.id)
.collection("user_location_posts")
.document(postId)
.setData({
"postId": postId,
"ownerId": currentUser.id,
"username": currentUser.username,
"description": description,
"timestamp": timestamp,
"lattitude": _pickedLocation.lattitude,
"longitude": _pickedLocation.longitude,
"max_height": heightRestrictionValue.toStringAsFixed(0),
"max_width": widthRestrictionValue.toStringAsFixed(0),
});
}
handlePostSubmit() {
createLocationPostInFirestore(
heightRestriction: heightRestrictionValue,
widthRestriction: widthRestrictionValue,
);
SnackBar snackbar = SnackBar(
content: Text("Profile Updated"),
);
_scaffoldKey.currentState.showSnackBar(snackbar);
setState(() {
postId = Uuid().v4();
});
}
buildUploadUserHeader() {
return Container(
margin: EdgeInsets.only(bottom: 10),
height: 200,
child: Row(
children: <Widget>[
Expanded(
flex: 2,
child: Container(
color: Colors.blue,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
ListTile(
leading: CircleAvatar(
backgroundImage:
CachedNetworkImageProvider(currentUser.photoUrl)),
),
],
),
),
),
Expanded(
flex: 6,
child: Container(
color: Colors.pink,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Text(currentUser.displayName),
],
),
),
),
],
),
);
}
buildCampUploadForm() {
return Container(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
//buildUploadUserHeader(), //TODO: This is the profile header that is dissabled for now. Work on possibly a header in the future.
Container(
padding: EdgeInsets.all(15),
child: Column(
children: <Widget>[
CampTypeSelect(),
CustomDivider(),
LocationInput(_selectPlace),
CustomDivider(),
HeightRestriction(),
WidthRestriction(),
SpaceAvalibility(),
OpeningTimes(),
CustomDivider(),
PostMedia(),
CustomDivider(),
UtilityServices(),
CustomDivider(),
ExtraLocationNotes(),
Container(
height: 80,
margin: EdgeInsets.only(top: 10, bottom: 10),
child: Row(
children: <Widget>[
Expanded(
child: FlatButton(
color: Colors.black,
onPressed: () => handlePostSubmit(),
child: Text(
"SUBMIT",
style: Theme.of(context).textTheme.display2,
),
padding: EdgeInsets.all(20),
),
)
],
),
),
],
),
),
],
),
));
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
automaticallyImplyLeading: false,
title: const Text(
'Add New Location',
style: TextStyle(color: Colors.black),
),
actions: <Widget>[
// action button
IconButton(
icon: Icon(Icons.info_outline),
color: Colors.black,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
fullscreenDialog: true,
builder: (context) => LocationSubmitHelpScreen()),
);
},
),
// action button
IconButton(
icon: Icon(Icons.close),
color: Colors.black,
onPressed: () {
Navigator.of(context).pop();
},
),
],
),
body: buildCampUploadForm(),
backgroundColor: Colors.white,
);
}
}
What I am trying to do is pass the data back from the widget ExtraLocationNotes()
to the function createLocationPostInFirestore().
For context, this is what my widget looks like;
import 'package:flutter/material.dart';
import 'common_widgets/custom_form_card.dart';
class ExtraLocationNotes extends StatefulWidget {
_ExtraLocationNotesState createState() => _ExtraLocationNotesState();
}
class _ExtraLocationNotesState extends State<ExtraLocationNotes> {
TextEditingController descriptionController = TextEditingController();
#override
Widget build(BuildContext context) {
return CustomFormCard(
child: Column(
children: <Widget>[
Container(
child: Row(
children: <Widget>[
Text(
"EXTRA INFORMATION",
style: TextStyle(
fontSize: 18.0,
color: Colors.black,
fontWeight: FontWeight.w400,
letterSpacing: 2.0,
),
),
],
),
),
SizedBox(height: 20),
TextFormField(
controller: descriptionController,
maxLines: 6,
maxLength: 250,
maxLengthEnforced: true,
style:
new TextStyle(fontSize: 18.0, height: 1.3, color: Colors.black),
decoration: const InputDecoration(
hintText:
"Please write a description of this location for fellow travellers.",
alignLabelWithHint: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.only(),
borderSide: BorderSide(color: Colors.black),
),
),
),
],
),
);
}
}
How do I pass the data back to the parent widget?
You need a callback, which will be triggered in the child widget then the value will be updated in the parent widget:
// 1- Define a pointers to executable code in memory, which is the callback.
typedef void MyCallback(String val);
class ExtraLocationNotes extends StatefulWidget {
// 2- You will pass it to this widget with the constructor.
final MyCallback cb;
// 3- ..pass it to this widget with the constructor
ExtraLocationNotes({this.cb});
_ExtraLocationNotesState createState() => _ExtraLocationNotesState();
}
class _ExtraLocationNotesState extends State<ExtraLocationNotes> {
//..
//...
RaisedButton(
//..
// 4- in any event inside the child you can call the callback with
// the data you want to send back to the parent widget:
onPressed: () {
widget.cb("Hello from the other side!");
}
),
}
Then inside the parent widget you need to catch the data which sent form the child:
class AddNewLocation extends StatefulWidget {
//...
_AddNewLocationState createState() => _AddNewLocationState();
}
class _AddNewLocationState extends State<AddNewLocation> {
// 1- Global var to store the data that we're waiting for.
String _dataFromMyChild = "";
buildCampUploadForm() {
return Container(
//...
//...
// 2- Pass the callback with the constructor of the child, this
// will update _dataFromMyChild's value:
ExtraLocationNotes(cb: (v) => setState(() => _dataFromMyChild = v)),
//..
}
// then
createLocationPostInFirestore() {
// Use _dataFromMyChild's value here
}
}
You can use the BuildContext object to get the context widget (might no be the parent!) couldn't read it all but as i understand that you need to pass the info from the child to the parent ,and you can do it with some like this :-
(context.widget as MyType).doStuff();
Note.
please check first with
print(context.widget.runtimeType);
but to make a better solution make a mutable data object that is passed from parent to the child so when changes happens it reflect's on the parent so you can separate business logic from ui logic.