I am Developing Cart Page for e com application. backend is firebase real time database. In cart Page Price Details is calculating when the page load. But when user change the quantity of cart item, It is going to update the cart. But the problem is Cant Calculate the Price Details On this time. When I reopen the page, Price Details is Calculating According to new Cart Quantity. But How can I can do that without re opening the Page
1.This is my cart_page.dart file
//this cart is show when user select my cart From the navigation drawer
import 'package:flutter/material.dart';
import 'package:buy_app_flutter/main.dart';
import 'package:buy_app_flutter/pages/notification_page.dart';
import 'package:buy_app_flutter/pages/search_page.dart';
import 'package:buy_app_flutter/componets/cart_products.dart';
import 'BuyNowDeliveryDetailsPage.dart';
import 'HomePage.dart';
class CartPage extends StatefulWidget {
#override
_CartPageState createState() => _CartPageState();
}
class _CartPageState extends State<CartPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: new DrawerCodeOnly(),
appBar: new AppBar(
elevation: 0.1,
backgroundColor: Colors.orangeAccent,
title: Text("My Cart"),
actions:<Widget> [
new IconButton(icon: Icon(Icons.search,color: Colors.white,), onPressed: (){
showSearch(context: context, delegate: DataSearch());
} ),
new IconButton(icon: Icon(Icons.notifications,color: Colors.white,), onPressed: (){
Navigator.push(context, MaterialPageRoute(builder: (context) => new NotificationPage() ));
} ),
],
),
body:new Cart_products(),
bottomNavigationBar:new Container(
color: Colors.white,
child: Row(
children:<Widget> [
Expanded(child: ListTile(
title: new Text("Total:"),
subtitle:new Text("Rs 00.00") ,
)),
Expanded(
child: new MaterialButton(onPressed: (){
Navigator.push(context, MaterialPageRoute(builder: (context) => new BuyNowDeliveryDetailsActivity() ));
},
child: new Text("Check Out"),
color: Colors.orangeAccent,
)
)
],
),
) ,
);
}
}
2.This is cart_procucts.dart file
import 'package:buy_app_flutter/DBModels/CartModel.dart';
import 'package:buy_app_flutter/DBModels/ProductModel.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
class Cart_products extends StatefulWidget {
#override
_Cart_productsState createState() => _Cart_productsState();
}
class _Cart_productsState extends State<Cart_products> {
//firebase database reference to cart===============================
final Cartref= FirebaseDatabase.instance.reference().child("UserData").child(FirebaseAuth.instance.currentUser.uid).child("Cart");
List<CartModel> Products_on_the_cart=List();
///=============================================firebase end
bool _progressController=true;
//variables to cal total
int TotalItemPrice=0;
int SavedAmount=0;
int DeliveryPrice=0;
int TotalAmount=0;
#override
initState(){
_getCartList(); //call function to get cart from firebase
super.initState();
}
///display the cart Items one by one===============================
#override
Widget build(BuildContext context) {
if(_progressController==true) //reading database
{
return Center(child: new CircularProgressIndicator());
}
else{ //db read end=========
if(Products_on_the_cart.isNotEmpty)
{
_calTotalPrice();
//===============================================if not empty the cart
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ListView.separated(
itemBuilder: (context, index){
return Single_cart_product(
cart_product_key: Products_on_the_cart[index].productKey,
cart_product_name: Products_on_the_cart[index].ProductTitle,
cart_prod_picture: Products_on_the_cart[index].Img,
cart_prod_price: Products_on_the_cart[index].Price,
cart_prod_cutted_price: Products_on_the_cart[index].CuttedPrice,
cart_prod_qty: Products_on_the_cart[index].qty,
cart_product_status: Products_on_the_cart[index].Status,
);
},
//end of item builder
separatorBuilder:(context,index){
return Row();
},
itemCount: Products_on_the_cart.length,
shrinkWrap: true,
),
Divider(),
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Text("PRICE DETAILS",style: TextStyle(fontWeight: FontWeight.bold,color: Colors.grey),),
),
Divider(),
///start of items total price row===========================
Padding(
padding: const EdgeInsets.only(left: 8.0,right: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("price (${Products_on_the_cart.length} items)",style: TextStyle(fontWeight: FontWeight.normal,color: Colors.black),),
Text("Rs ${TotalItemPrice}/-",style: TextStyle(fontWeight: FontWeight.normal,color: Colors.black),),
],
),
),
///end of items total price row============================
///start of delivery price row==================================
Padding(
padding: const EdgeInsets.only(left: 8.0,top: 8.0,right: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Delivery",style: TextStyle(fontWeight: FontWeight.normal,color: Colors.black),),
Text("Rs ${DeliveryPrice}/-",style: TextStyle(fontWeight: FontWeight.normal,color: Colors.green),),
],
),
),
///end of delivery price row============================
Divider(),
///Start of total amount row==================================
Padding(
padding: const EdgeInsets.only(left: 8.0,right: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Total Amount",style: TextStyle(fontWeight: FontWeight.bold,color: Colors.black),),
Text("Rs ${TotalAmount}/-",style: TextStyle(fontWeight: FontWeight.bold,color: Colors.black),),
],
),
),
///end of total amount row===========================================
Divider(),
Padding(
padding: const EdgeInsets.only(left: 8.0,right: 8.0),
child: Text("You saved Rs ${SavedAmount}/- on this order",style: TextStyle(fontWeight: FontWeight.normal,color: Colors.green),),
),
],
);
//end of if not empty the cart===========================================
}
else
{
return Center(child: Text("Your cart is empty !"));
}
}
}
///end of displaying cart items one by one====================================
_getCartList()
async {
//get cart data from firebase==========
try{
await Cartref.once().then((DataSnapshot snapshot) {
var data = snapshot.value;
if(data==null) //if the no cart items found hide progress
{
setState(() {
_progressController=false;
});
}
Products_on_the_cart.clear();
data.forEach((key,value)
async {
CartModel model = new CartModel(
date: value["date"],
time: value["time"],
productKey: value["productKey"],
qty: value["qty"],
);
//use Pref(product ref) to get details from products record
try{
//Product details ref and list
final Pref= FirebaseDatabase.instance.reference().child("Product");
List<ProductModel> product =List();
//====================================
await Pref.child(model.productKey).once().then((DataSnapshot snap) {
// print('Product : ${snap.value}');
model.ProductTitle=snap.value["ProductTitle"];
model.Img=snap.value["img1"];
model.Price=snap.value["Price"];
model.DPrice=snap.value["DPrice"];
model.CuttedPrice=snap.value["CuttedPrice"];
model.Status=snap.value["Status"];
});
}
catch(e)
{
print("Error In try block for get data from real time db ");
print(e);
}
finally
{
setState(() {
_progressController=false;
});
}
//========================================================end of get product details
setState(() {
Products_on_the_cart.add(model);
});
}
);
});
}
catch(e)
{
print(e);
}
// end of get cart data from firebase
}
///function for calculate total price
_calTotalPrice(){
TotalItemPrice=0;
DeliveryPrice=0;
TotalAmount=0;
SavedAmount=0;
Products_on_the_cart.forEach((item){
//set total item price===========
TotalItemPrice=TotalItemPrice+int.parse(item.Price)*int.parse(item.qty);
//set delivery price==========
if (item.DPrice=="FREE")
{
//nothing
}
else
{
if (int.parse(item.DPrice)>DeliveryPrice)
{
DeliveryPrice=int.parse(item.DPrice);
}
}
//set Total amount=======
TotalAmount=TotalItemPrice+DeliveryPrice;
//set saved amount
SavedAmount=SavedAmount + (int.parse(item.CuttedPrice)-int.parse(item.Price))*int.parse(item.qty);
});
}
/// end of calculate total price function
}
///from here design the cart item display ==========================================
class Single_cart_product extends StatefulWidget {
final cart_product_key;
final cart_product_name;
final cart_prod_picture;
final cart_prod_price;
final cart_prod_cutted_price;
String cart_prod_qty;
final cart_product_status;
Single_cart_product({
this.cart_product_key,
this.cart_product_name,
this.cart_prod_picture,
this.cart_prod_price,
this.cart_prod_cutted_price,
this.cart_prod_qty,
this.cart_product_status,
});
#override
_Single_cart_productState createState() => _Single_cart_productState();
}
class _Single_cart_productState extends State<Single_cart_product> {
///==========================start of Cart Item Display Design==================================================================
#override
Widget build(BuildContext context) {
var cartQty=int.parse(widget.cart_prod_qty);
return Card(
child: Column(
children: [
ListTile(
///===============START LEADING SECTION ===============================
leading: FadeInImage.assetNetwork(
placeholder: 'assets/close_box.png',
image:widget.cart_prod_picture,
height: 80.0,
width: 80.0,
fit: BoxFit.contain,
),
///=======END OF LEADING SECTION=======================================
/// =========== TITLE SECTION =========================================
title: new Text(widget.cart_product_name),
///====END OF TITLE SECTION============================================
///========== SUBTITLE SECTION ==================================
subtitle: new Column(
children:<Widget> [
//Row inside the column
new Row(
children: <Widget> [
// =============this section is for the Product price =====
new Container(
alignment: Alignment.topLeft,
child: new Text("\Rs.${widget.cart_prod_price}",
style: TextStyle(
fontSize:16.0,
fontWeight: FontWeight.bold,
color: Colors.black
),
),
),
SizedBox(width: 4,),
//==========this section is for the cutted price of the product=========
Padding(
padding: const EdgeInsets.all(5.0),
child: new Text("\Rs.${widget.cart_prod_cutted_price}",
style: TextStyle(
fontSize:16.0,
fontWeight: FontWeight.normal,
decoration: TextDecoration.lineThrough,
color: Colors.grey
),
),
),
//end of cutted price of the product
],
),
],
),
///=====END OF SUBTITLE SECTION============================================
///======START OF TRAILING SECTION==========================================
trailing:
Column(
children:
<Widget>[
Expanded(child: new IconButton(
padding: const EdgeInsets.all(0.0),
icon: Icon(Icons.arrow_drop_up), onPressed: (){
cartQty++;
print(cartQty);
_updateCartQty(widget.cart_product_key,cartQty.toString()); //call to update cart qty
setState(() {
widget.cart_prod_qty=cartQty.toString();
});
})),
Padding(
padding: const EdgeInsets.all(0.0),
child: new Text(cartQty.toString()),
),
Expanded(child: new IconButton(
padding: const EdgeInsets.all(0.0),
icon: Icon(Icons.arrow_drop_down), onPressed: (){
if(cartQty>1)
{
cartQty--;
print(cartQty);
_updateCartQty(widget.cart_product_key,cartQty.toString()); //call to update cart qty
setState(() {
widget.cart_prod_qty=cartQty.toString();
});
}
})),
],
),
///==END OF TRAILING======================================================================
),
//REMOVE BTN AND STATUS
Row(
children: [
new IconButton(
icon: Icon(Icons.delete),
color:Colors.red ,
onPressed: (){}),
Padding(
padding: const EdgeInsets.all(4.0),
child: new Text(widget.cart_product_status,style: TextStyle(color: Colors.orangeAccent)),
),
],
)
//END OF REMOVE BTN SND STATUS
],
),
);
}
///==========================end of Cart Item Display Design==================================================================
/// function for update cart qty
_updateCartQty(String Pkey,String Qty){
try {
final Cartref = FirebaseDatabase.instance.reference()
.child("UserData")
.child(FirebaseAuth.instance.currentUser.uid)
.child("Cart");
Cartref.child(Pkey).child("qty").set(Qty);
}
catch(Ex)
{
Fluttertoast.showToast(
msg: Ex.message,
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.BOTTOM,
timeInSecForIosWeb: 1,
backgroundColor: Colors.red,
textColor: Colors.white,
fontSize: 16.0);
}
}
}
3.After that I need to view the calculated total amount in Bottom of the cart page
Updating product quantity value in Cart_products class:
You need to create a variable final ValueChanged<int> onProdQty; in Single_cart_product class and then where the quantity value changes use the callback like this widget.onProdQty?.call(here Pass the new quantity value). Also one last step is that in class Cart_products where you are calling Single_cart_product() add this in argument onProdQty: (value) => setState(() { Products_on_the_cart[index].qty = value; }),. By doing this whenever the callback is called the setstate would run in Cart_products class and wherever Products_on_the_cart[index].qty is used it would get updated.
Updating product quantity value in CartPage class:
Note: I'm not sure if these steps would work corectly as I have limited information about your project. But you will surely learn how to pass data around using callbacks:
Follow steps of Updating product quantity value in Cart_products
class and then continue.
Add final ValueChanged<int> onProdQty; in Cart_products class and
then change Single_cart_product() argument in class Cart_products
like this:
Single_cart_product(onProdQty: (value) => setState((){
Products_on_the_cart[index].qty = value;
widget.onProdQty?.call(value); //add this
}),)
Then add List<CartModel> Products_on_the_cart=List(); in the
_CartPageState class in cart_page.dart and at last in the body: Cart_products() make these changes:
Cart_products(onProdQty: (value) => setState((){
Products_on_the_cart[index].qty = value;
}),)
Now use Products_on_the_cart[index].qty in bottomNavigationBar.
Please let me know if you have any further queries.
Related
On the add Weight screen I pick a weight and a date and then it gets stored in a list in the Hive database, once I press "Save".
It also returns me to the Homepage, where I would like to depict the stored list items in a ListView.Builder.
I only get the last item on the list and not the whole list.
When I am at the add weight screen, I see that there are more items in the list, but once I return to homepage only the last one exists.
AddWeightScreen
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:intl/intl.dart';
import 'package:weighty/components/my_alert_box.dart';
import 'package:weighty/data/database.dart';
import 'package:weighty/screens/home_page.dart';
class AddWeight extends StatefulWidget {
const AddWeight({super.key});
#override
State<AddWeight> createState() => _AddWeightState();
}
class _AddWeightState extends State<AddWeight> {
Database db = Database();
final _myBox = Hive.box("mybox");
double _currentValue = 0;
DateTime _dateTime = DateTime.now();
String formattedDate = DateFormat('d MMM yyyy').format(DateTime.now());
final _weightController = TextEditingController();
void _showDatePicker() {
showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2000),
lastDate: DateTime.now(),
).then((value) {
setState(() {
_dateTime = value!;
formattedDate = DateFormat('d MMM yyyy').format(_dateTime);
});
});
}
// add weight from text - keyboard
void _dialogNumber() {
showDialog(
context: context,
builder: (context) {
return MyAlertBox(
controller: _weightController,
hintText: "Enter today's weight...",
onSave: () {
if (double.parse(_weightController.text) > 200) {
_weightController.text = "200";
}
setState(() {
_currentValue = double.parse(_weightController.text);
});
_weightController.clear();
Navigator.pop(context);
},
onCancel: cancelDialogBox);
});
}
//save method
void _saveWeightAndDate() {
setState(() {
db.weightList.add([_currentValue, formattedDate]);
});
db.saveData();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const HomePage(),
),
);
}
// cancel new weight input
void cancelDialogBox() {
// clear textfield
_weightController.clear();
// pop dialog box
Navigator.of(context).pop();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFFD9F0FF),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
const SizedBox(
height: 28.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
"Add Weight",
style: TextStyle(fontSize: 36, fontWeight: FontWeight.w600),
),
MaterialButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text(
'CANCEL',
style: TextStyle(
color: Color(0xff878472),
fontSize: 16.0,
fontWeight: FontWeight.bold,
),
),
),
],
),
const SizedBox(height: 30),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: MaterialButton(
onPressed: _dialogNumber,
child: Text(
_currentValue.toStringAsFixed(2) + " kg",
style: TextStyle(
color: Color(0xFF006B8F),
fontSize: 46,
fontWeight: FontWeight.w500),
),
),
),
const SizedBox(height: 30),
Slider(
value: _currentValue,
min: 0,
max: 200,
onChanged: ((value) {
setState(() {
_currentValue = value;
});
}),
),
const SizedBox(height: 30),
TextButton.icon(
onPressed: _showDatePicker,
icon: const Icon(
Icons.date_range_outlined,
size: 24.0,
color: Color(0xff878472),
),
label: Text(
formattedDate,
style: TextStyle(color: Color(0xff878472), fontSize: 18),
),
),
const SizedBox(height: 30),
ElevatedButton(
onPressed: _saveWeightAndDate,
child: Text(
'SAVE',
style:
TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
style: ElevatedButton.styleFrom(
elevation: 24,
padding:
EdgeInsets.symmetric(vertical: 20, horizontal: 80),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
side: BorderSide(color: Colors.white, width: 2),
),
),
),
const SizedBox(height: 30),
],
),
),
],
),
),
);
}
}
HomePage
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:weighty/components/weight_tile.dart';
import 'package:weighty/data/database.dart';
import 'package:weighty/main.dart';
import 'package:weighty/screens/add_weight.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
void initState() {
//First time ever opening app, Create default data
// if (_myBox.get("WEIGHTLIST") == null) {
// }
//already exists data
db.loadData();
// db.saveData();
super.initState();
}
Database db = Database();
final _myBox = Hive.box("mybox");
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
floatingActionButton: FloatingActionButton(
backgroundColor: Color(0xff006B8F),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const AddWeight(),
),
);
},
child: const Icon(Icons.add),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 28),
Text(
"CURRENT",
style: TextStyle(
color: Colors.grey[500],
fontSize: 16.0,
fontWeight: FontWeight.bold,
),
),
Text(
db.weightList.length == 0
? "00.0 Kg"
: db.weightList.last[0].toStringAsFixed(2) + " kg",
style: TextStyle(
color: Color(0xFF006B8F),
fontSize: 46.0,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 40),
Center(
child: Text(
"GRAPH",
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.bold,
),
),
),
Expanded(
child: ListView.builder(
itemCount: db.weightList.length,
itemBuilder: (context, index) {
return WeightTile(
date: db.weightList[index][1],
weight: db.weightList[index][0].toStringAsFixed(2),
);
},
),
),
ElevatedButton(
onPressed: () {
print(db.weightList.length);
},
child: Text('Print!')),
],
),
),
);
}
}
Hive Database
import 'package:hive_flutter/hive_flutter.dart';
class Database {
//reference the box
final _myBox = Hive.box("mybox");
// Empty Weight List
List weightList = [];
// void createInitialData() {
// weightList = [];
// }
//load the date from the db
void loadData() {
weightList = _myBox.get("WEIGHTLIST");
}
//save the weight
void saveData() {
_myBox.put("WEIGHTLIST", weightList);
}
}
I could not rebuild your project to debug it. so I'm kind of taking a shot in the darkness.
there is a widget called valuelistenablebuilder which gets rebuilt when ever a value changes you can set your box to the value being listened by this widget and rebuild every time a new value is added to your box you can find an example on this page
For whole list you have to first get old list which is saved in hive then sum this list and save in to hive
Output is
For you example you have to update saveData method in to a Database class
void saveData() {
final myBox = Hive.box("mybox");
List? hiveList = [];
hiveList = myBox.get("WEIGHTLIST") ?? [];
if (hiveList!.isNotEmpty) {
hiveList += weightList;
} else {
hiveList = weightList;
}
_myBox.put("WEIGHTLIST", hiveList);
}
You will be get whole list into a homescreen
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);
}
recently I have followed a Youtube tutorial where it shows how to add product item to cart. But in the video, it didn't show how to delete an item from cart. Video link: https://www.youtube.com/watch?v=K8d3qqbP3qk
ProductModel.dart
class ProductModel{
String name;
int price;
String image;
ProductModel(String name, int price, String image){
this.name = name;
this.price = price;
this.image = image;
}
}
ProductScreen.dart
import 'package:flutter/material.dart';
import '../ProductModel.dart';
import 'package:ecommerce_int2/models/product.dart';
class ProductScreen2 extends StatelessWidget {
final ValueSetter<ProductModel> _valueSetter;
ProductScreen2(this._valueSetter);
List<ProductModel> products = [
ProductModel("Grey Jacket", 100, 'assets/jacket_1.png'),
ProductModel("Brown Pants", 60, 'assets/jeans_9.png'),
ProductModel("Grey Pants", 50, 'assets/jeans_6.png'),
ProductModel("Orange Pants", 70, 'assets/jeans_8.png'),
ProductModel("Long Jeans", 80, 'assets/jeans_2.png'),
ProductModel("Black and Blue Cap", 40, 'assets/cap_2.png'),
ProductModel("Black Cap", 30, 'assets/cap_6.png'),
ProductModel("Red Cap", 35, 'assets/cap_4.png'),
];
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: ListView.separated(
itemBuilder: (context, index){
return ListTile(
leading: Image.asset(
products[index].image,
width: 100,
height: 100,
fit: BoxFit.fitWidth,
),
title: Text(products[index].name),
trailing: Text("\RM${products[index].price}", style: TextStyle(color: Colors.redAccent, fontSize: 20, fontWeight: FontWeight.w500),),
onTap: (){
_valueSetter(products[index]);
},
);
},
separatorBuilder: (context, index){
return Divider();
},
itemCount: products.length
),
);
}
}
CheckoutScreen.dart
import 'package:flutter/material.dart';
import 'package:ecommerce_int2/screens/address/add_address_page.dart';
import 'package:device_apps/device_apps.dart';
class CheckoutScreen extends StatelessWidget {
final cart;
final sum;
CheckoutScreen(this.cart, this.sum);
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
ListView.separated(
itemBuilder: (context, index){
return ListTile(
title: Text(cart[index].name),
trailing: Text("\RM${cart[index].price}", style: TextStyle(color: Colors.redAccent, fontSize: 20, fontWeight: FontWeight.w500),),
onTap: (){
},
);
},
separatorBuilder: (context, index){
return Divider();
},
itemCount: cart.length,
shrinkWrap: true,
),
Divider(),
Text("Total : \RM$sum", style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500), textAlign: TextAlign.right,),
SizedBox(
height: 20,
),
Text("Remarks", style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500),),
TextFormField(
decoration: InputDecoration(
hintText: ('Example: Red Cap: Free Size, Grey Jacket: UK, M Size'),
),
maxLines: 5,
),
SizedBox(
height: 50,
),
RaisedButton(
color: Theme.of(context).accentColor,
child: Text('Buy Now',style: TextStyle(color: Colors.white, fontSize: 20)),
onPressed: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (_) => AddAddressPage()));
},
),
RaisedButton(
color: Theme.of(context).accentColor,
child: Text('Try Out',style: TextStyle(color: Colors.white, fontSize: 20)),
onPressed: () => DeviceApps.openApp('com.DefaultCompany.clothestryingfunction2'),
),
],
),
);
}
}
add_to_cart.dart
import 'package:ecommerce_int2/ProductModel.dart';
import 'package:ecommerce_int2/screens/CheckoutScreen.dart';
import 'package:ecommerce_int2/screens/ProductScreen.dart';
import 'package:flutter/material.dart';
class CartApp extends StatefulWidget {
#override
_CartAppState createState() => _CartAppState();
}
class _CartAppState extends State<CartApp> {
List<ProductModel> cart = [];
int sum = 0;
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
title: Text("Add To Cart"),
bottom: TabBar(
tabs: <Widget>[
Tab(text: "Products",),
Tab(text: "Cart",),
],
),
),
body: TabBarView(
children: <Widget>[
ProductScreen2((selectedProduct){
setState(() {
cart.add(selectedProduct);//update
sum = 0;
cart.forEach((item){
sum = sum + item.price;
});
});
}),
CheckoutScreen(cart, sum),
],
),
),
);
}
}
The goal is to remove the selected item from cart and minus the selected item's price from the sum. Can anyone tell me how to do that?
First, you need to add a callback in the CheckoutScreen:
class CheckoutScreen extends StatelessWidget {
final cart;
final sum;
final ValueSetter<ProductModel> _valueDeleter;
CheckoutScreen(this.cart, this.sum, this._valueDeleter);
...
After that, add the callback function when using it in the TabBarView in CartApp:
CheckoutScreen(cart, sum, (deleteProduct) {
setState(() {
// Use this loop instead of cart.removeWhere() to delete 1 item at a time
for (var i = 0; i < cart.length; i++) {
if (cart[i].name == deleteProduct.name) {
cart.removeAt(i);
break;
}
}
sum = 0;
cart.forEach((item) {
sum = sum + item.price;
});
});
}),
Finally, add a button in the ListTile within CheckoutScreen to initiate the delete action (I'm using a Row in title here for simplicity):
ListTile(
...
title: Row(
children: [
IconButton(
icon: Icon(Icons.delete),
color: Colors.red,
onPressed: () => _valueDeleter(cart[index]),
),
Text(cart[index].name),
],
),
trailing: Text(
...
The main reason i want to change the design is that when i scan a product and then scan the same one i get the same product twice while it is the same one so basically i just need to increase the quantity of the product by one.
I have used Provider before but I still can't manage to arrange the classes the right way to make it work.
first i have tried to make a Productobject(which extenes ChangeNotifier) inside ProductCard class and use provider.of(context) and retrieve the quantity of the prodcut.
plus,at the main before the material I used ChangeNotifierProvider
I have a mess in my head and i would love to see someone arranging the code with explanation of how the things works
main
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:sally_smart/utilities/product_notifier.dart';
import 'screens/checkout_screen.dart';
import 'screens/login_screen.dart';
import 'screens/registration_screen.dart';
import 'screens/welcome_screen.dart';
import 'package:provider/provider.dart';
//void main() => runApp(Sally());
void main() {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
.then((_) {
runApp(new Sally());
});
}
class Sally extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
backgroundColor: Colors.teal,
cardColor: Color(0xFF068194),
),
initialRoute: LoginScreen.id,
routes: {
WelcomeScreen.id: (context) => WelcomeScreen(),
RegistrationScreen.id: (context) => RegistrationScreen(),
LoginScreen.id: (context) => LoginScreen(),
CheckoutScreen.id: (context) => CheckoutScreen()
},
);
}
}
ProductCard
import 'package:flutter/material.dart';
import 'package:sally_smart/utilities/constants.dart';
import 'package:sally_smart/utilities/product.dart';
import 'package:sally_smart/utilities/round_icon_button.dart';
class ProductCard extends StatefulWidget {
final Product prodcut;
ProductCard(Product product);
#override
_ProductCardState createState() => _ProductCardState();
}
class _ProductCardState extends State<ProductCard> {
double finalPrice;
// static int quantity = 1;
#override
Widget build(BuildContext context) {
finalPrice = widget.prodcut.quantity * widget.prodcut.productPrice;
return Card(
elevation: 5.0,
child: ListTile(
leading: Padding(
padding: EdgeInsets.only(
left: 2.0,
),
child: Icon(
widget.prodcut.productIcon,
size: 35,
),
),
title: Text(
widget.prodcut.productName,
style: kProductNameTextStyle,
),
subtitle: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
RoundIconButton(
icon: Icons.add,
color: Colors.green,
function: () {
setState(() {
widget.prodcut.quantity++;
});
}),
Text(
'$widget.prodcut.quantity',
),
RoundIconButton(
icon: Icons.remove,
color: Colors.red,
function: () {
setState(() {
widget.prodcut.quantity--;
if ( widget.prodcut.quantity == 0) {
widget.prodcut.quantity++;
}
});
}),
],
),
),
Text(
'${finalPrice.toStringAsFixed(2)} ₪',
style: TextStyle(fontSize: 15),
),
],
),
),
);
}
}
Prodcut
import 'package:flutter/cupertino.dart';
class Product extends ChangeNotifier{
String productName;
double productPrice;
IconData productIcon;
String id;
String barCode;
int quantity;
Product(this.productName, this.productPrice, this.productIcon, this.id,
this.barCode,this.quantity);
}
welcome screen
import 'package:audioplayers/audio_cache.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_barcode_scanner/flutter_barcode_scanner.dart';
import 'package:sally_smart/screens/login_screen.dart';
import 'package:sally_smart/screens/registration_screen.dart';
import 'package:sally_smart/utilities/constants.dart';
import 'package:sally_smart/utilities/product.dart';
import 'package:sally_smart/utilities/product_card.dart';
import 'package:sally_smart/utilities/scan_button_const.dart';
import 'package:sally_smart/utilities/scan_methods.dart';
//import 'package:sally_smart/utilities/scan_pageML.dart';
//import 'package:flutter_camera_ml_vision/flutter_camera_ml_vision.dart';
//import 'package:firebase_ml_vision/firebase_ml_vision.dart';
//working version
//List<ProductCard> shoppingList = [];
final sallyDatabase = Firestore.instance;
class WelcomeScreen extends StatefulWidget {
static const String id = 'welcome_screen';
#override
_WelcomeScreenState createState() => _WelcomeScreenState();
}
class _WelcomeScreenState extends State<WelcomeScreen> {
final _auth = FirebaseAuth.instance;
final textEditorController = TextEditingController();
String _scanBarcode = 'Unknown';
String productName = 'Product Test';
double productPrice;
String productBarCode;
IconData productIcon = Icons.add_shopping_cart;
final List<ProductCard> shoppingList = [];
int productId = 0;
static AudioCache barcodeSound = AudioCache();
// saves barcodes data
List<String> data = [];
Future<void> initPlatformState() async {
String barcodeScanRes;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
barcodeScanRes =
await FlutterBarcodeScanner.scanBarcode("#ff6666", "Cancel", true);
} on PlatformException {
barcodeScanRes = 'Failed to get platform version.';
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_scanBarcode = barcodeScanRes;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFF21bacf),
appBar: AppBar(
elevation: 3,
backgroundColor: Colors.black54,
leading: Icon(
Icons.shopping_basket,
size: 30,
),
title: Text(
'Sally',
textAlign: TextAlign.end,
style: kHeaderTextStyle,
),
actions: <Widget>[
IconButton(icon: Icon(Icons.settings), onPressed: () {}),
VerticalDivider(
color: Color(0x8CFFFFFF),
width: 3,
),
IconButton(
icon: Icon(Icons.power_settings_new),
onPressed: () {
_auth.signOut();
Navigator.pushNamed(context, LoginScreen.id);
}),
VerticalDivider(
color: Color(0x8CFFFFFF),
width: 3,
),
IconButton(icon: Icon(Icons.share), onPressed: () {}),
VerticalDivider(
color: Color(0x8CFFFFFF),
width: 3,
),
// PopupMenuButton(itemBuilder: ),
],
),
body: Container(
decoration: kBackgroundGradientScan,
child: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Text(
'ברוכה הבאה, סאלי',
textAlign: TextAlign.right,
style: kHeaderTextStyle,
),
Padding(
padding: EdgeInsets.only(top: 5, right: 15),
child: Hero(
tag: 'Sally',
child: CircleAvatar(
backgroundImage:
AssetImage('images/missing_avatar_F.png'),
maxRadius: 25,
),
),
)
],
),
Padding(
padding: EdgeInsets.symmetric(vertical: 3, horizontal: 15),
child: TextField(
textAlign: TextAlign.center,
onChanged: (value) async {
_scanBarcode = value;
productPrice = await getProductPrice(_scanBarcode);
productName = await getProductName(_scanBarcode);
},
decoration: kTextFieldDecoration.copyWith(
prefixIcon: IconButton(
icon: Icon(Icons.search),
onPressed: () {
_scanBarcode = '';
textEditorController.clear();
try {
setState(() {
textEditorController.clear();
//checking if a product was already scanned
//adding a ProductCard to the shopping list with the ProductCard const. Works on scan
shoppingList.add(ProductCard( new Product(productName, productPrice, productIcon, shoppingList.length.toString(),
productBarCode, 1)));
});
} catch (e) {
print(e);
}
},
),
// prefix: IconButton(
// icon: Icon(Icons.search),
// onPressed: () {
//
// }),
hintText: '...הכנס ברקוד או שם מוצר ידנית'),
),
),
DividerSally(),
shoppingListBuilder(),
DividerSally(),
Container(
child: Padding(
padding: EdgeInsets.only(bottom: 15.0),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 40),
child: Column(
children: <Widget>[
ScanMainButton(
iconData: Icons.flip,
buttonText: 'סרוק מוצר',
onPressed: () async {
//Navigator.pushNamed(context, ScanScreen.id);
await initPlatformState();
barcodeSound.play('barcode_sound.mp3');
//Changes the product name by referencing to the database
productBarCode = _scanBarcode;
productPrice =
await getProductPrice(_scanBarcode);
productName =
await getProductName(_scanBarcode);
setState(() {
//checking if a product was already scanned
//adding a ProductCard to the shopping list with the ProductCard const. Works on scan
shoppingList.add(ProductCard( new Product(productName, productPrice, productIcon, shoppingList.length.toString(),
productBarCode, 1)));
});
},
color: Colors.teal,
),
ScanMainButton(
iconData: Icons.check,
buttonText: 'מעבר לתשלום',
color: Colors.green,
onPressed: () {
Navigator.pushNamed(
context, RegistrationScreen.id);
}),
],
),
),
),
)
],
),
),
),
));
}
Expanded shoppingListBuilder() {
return Expanded(
child: Container(
color: Colors.black38,
child: ListView.builder(
reverse: true,
itemCount: shoppingList.length,
itemBuilder: (context, index) {
ProductCard item = shoppingList[index];
return Dismissible(
key: Key(item.prodcut.id),
direction: DismissDirection.startToEnd,
onDismissed: (direction) {
setState(() {
shoppingList.removeAt(index);
});
},
background: Container(
child: Icon(
Icons.restore_from_trash,
size: 40,
),
margin: EdgeInsets.symmetric(vertical: 5),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomLeft,
end: Alignment.topRight,
colors: [
Color(0x8C650223),
Color(0x8CB9013E),
],
stops: [0.1, 0.9],
),
),
),
child: item //ListTile(title: Text('${item.productName}.')),
);
},
),
));
}
}
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.