How to extract specific value from object stored in cloud firestore and get the sum of those values according to needed filters? - flutter

I am having an issue in extracting specific value and querying with it. Please guide me on this.
Here is a data structure : (Using flutter/dart and cloudFirestore 3.1.18)
What I want to do is
extract the value of "amount" acccording to "isIncome" (boolean) from all the documents in "entries" collection and get the sum of those extracted amount.
to get the all the "amount" done in a day according to "entryLog" as well and show filtered data (according to date and income type) in listview builder as well.
This is the code I have return for getting all those entries in a stream
Stream<List<Entries>> streamAllEntries() {
var uid = FirebaseAuth.instance.currentUser.uid;
var stream = _db
.collection('entries')
.where("addedBy", isEqualTo: uid)
.orderBy('entryLog', descending: true)
.snapshots()
.map((event) =>
event.docs.map((e) => Entries.fromJson(e.data())).toList());
log(stream.toString());
return stream;
}
and this is my code for Showing DailyReports (here -- entries)
class DailyReports extends StatefulWidget {
const DailyReports({Key? key}) : super(key: key);
#override
State<DailyReports> createState() => _DailyReportsState();
}
class _DailyReportsState extends State<DailyReports> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.transparent,
body: getBody(),
);
}
Widget getBody() {
var entries = Provider.of<List<Entries>>(context);
var size = MediaQuery.of(context).size;
return Container(
margin: const EdgeInsets.only(bottom: 5),
child: Column(
children: [
//header card
SizedBox(
height: 140,
child: Card(
child: Padding(
padding: const EdgeInsets.only(
top: 10, bottom: 10, right: 10, left: 10),
child:
//header row
Column(
children: [
//title
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
"Transactions",
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 22),
),
Icon(Ionicons.search, color: ColorTheme().blackColor),
],
),
//day tabs
const SizedBox(height: 10),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView.builder(
itemCount: days.length,
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
var day = days[index];
return weekTabs(size, index, day);
},
),
),
),
],
),
),
),
),
//income/expense card
incomeExpenseStatus(entries),
testbtn(),
// transaction summary
Expanded(child: transactionSection(entries)),
],
),
);
}
testbtn() {
return IconButton(
onPressed: () async {
await FirestoreService().addToAmountCollection(1000, true, "entry8");
},
icon: const Icon(Icons.mail, color: Colors.white));
}
Card incomeExpenseStatus(List<Entries> entries) {
return Card(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"Transaction Summary",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
Container(
padding: const EdgeInsets.symmetric(vertical: 10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
const Text(
"Income",
style: TextStyle(),
),
const Text(
"Rs. 60000",
style: TextStyle(
color: Colors.green,
fontSize: 16,
fontWeight: FontWeight.bold),
),
Container(
width: 1,
height: 20,
color: Colors.black,
),
const Text(
"Expenses",
),
const Text(
"Rs. 100000",
style: TextStyle(
color: Colors.red,
fontSize: 16,
fontWeight: FontWeight.bold),
),
],
),
),
],
),
),
);
}
Widget transactionSection(List<Entries> entries) {
int? totalAmt;
return Card(
child: Column(
children: [
const SizedBox(height: 5),
Text(
"Transactions count : ${entries.length}",
style: const TextStyle(fontSize: 10, color: Colors.red),
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 10),
child: ListView.builder(
physics: const BouncingScrollPhysics(),
itemCount: entries.length,
itemBuilder: (context, index) {
var transaction = entries[index];
DateTime d = DateTime.parse(transaction.entryLog);
return Card(
child: ListTile(
//*?How icon color works
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: ((context) => TransactionDetailScreen(
index: transaction)),
),
);
},
leading: CircleAvatar(
child: Center(
child: Icon(
getCategoryWiseIcon(
transaction.details!.category.toString()),
),
),
),
title: Text(transaction.details!.category.toString()),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(transaction.details!.remarks.toString()),
Text("$d"),
],
),
trailing: transaction.details!.isIncome == true
? Text("IN",
style:
TextStyle(color: ColorTheme().greenColor))
: Text("OUT",
style: TextStyle(
color: ColorTheme().primaryColor))),
);
}),
),
),
],
),
);
}
int activeDay = 1;
#override
void initState() {
activeDay = DateTimeExtractor().getDayAsInteger(activeDay);
super.initState();
}
Widget weekTabs(size, index, day) {
log('Active month:$activeDay');
return GestureDetector(
onTap: () {
setState(() {
activeDay = index;
});
},
child: Row(
children: [
Column(
children: [
Text(
day['label'],
style: TextStyle(
fontSize: 12,
color: activeDay == index
? ColorTheme().primaryColor
: ColorTheme().blackColor.withOpacity(0.8),
fontWeight: activeDay == index
? FontWeight.w500
: FontWeight.normal),
),
const SizedBox(height: 5),
Container(
width: 30,
height: 30,
decoration: BoxDecoration(
color: activeDay == index
? ColorTheme().primaryColor
: ColorTheme().whiteColor,
shape: BoxShape.circle,
border: Border.all(color: ColorTheme().primaryColor),
),
child: Center(
child: Text(
day['day'],
style: TextStyle(
fontSize: 12,
color: activeDay != index
? ColorTheme().primaryColor
: ColorTheme().whiteColor,
),
),
),
),
],
),
const SizedBox(width: 20),
],
),
);
}
}
This is my Entres model class
class Entries {
String? addedBy;
Details? details;
String? entryID;
dynamic entryLog;
String? gaadiID;
Entries({
this.addedBy = "",
this.details,
this.entryID = "",
this.entryLog,
this.gaadiID = "",
});
factory Entries.fromJson(Map<String, dynamic> json) =>
_$EntriesFromJson(json);
Map<String, dynamic> toJson() => _$EntriesToJson(this);
Entries _$EntriesFromJson(Map<String, dynamic> json) => Entries(
addedBy: json['addedBy'] as String? ?? "",
details: json['details'] == null
? null
: Details.fromJson(json['details'] as Map<String, dynamic>),
entryID: json['entryID'] as String? ?? "",
entryLog: json['entryLog'].toString(),
gaadiID: json['gaadiID'] as String? ?? "",
);
Map<String, dynamic> _$EntriesToJson(Entries instance) => <String, dynamic>{
'addedBy': instance.addedBy,
'details': instance.details,
'entryID': instance.entryID,
'entryLog': instance.entryLog.toString(),
'gaadiID': instance.gaadiID,
};
}
If you need any more info then please feel free to ask .

Related

use variables of one class in another class Flutter

# cartmodel
`class CartModel extends ChangeNotifier {
// list of items on sale
final List _shopItems = const [
// [ itemName, itemPrice, imagePath, color ]
["Avocado", "4", "assets/images/avocado.png", Colors.green],
["Banana", "2", "assets/images/banana.png", Colors.yellow],
["pollo", "12", "assets/images/chicken.png", Colors.brown],
["acqua", "1", "assets/images/water.png", Colors.blue],
["mela", "3", "assets/images/mela.png", Colors.red],
["broccoli", "3", "assets/images/broccoli.png", Colors.brown],
];
final List _faidate = const [
["Avocado", "14000", "assets/images/avocado.png", Colors.orange],
["Banana", "2", "assets/images/banana.png", Colors.blueGrey],
["pollo", "12", "assets/images/chicken.png", Colors.red],
["acqua", "1", "assets/images/water.png", Colors.blue],
];
// list of cart items
final List _cartItems = [];
get faidate => _faidate;
get cartItems => _cartItems;
get shopItems => _shopItems;
// add item to cart
void addItemToCart(int index) {
_cartItems.add(_shopItems[index]);
notifyListeners();
}
// remove item from cart
void removeItemFromCart(int index) {
_cartItems.removeAt(index);
notifyListeners();
}
// calculate total price
String calculateTotal() {
double totalPrice = 0;
for (int i = 0; i < cartItems.length; i++) {
totalPrice += double.parse(cartItems[i][1].toString());
}
return totalPrice.toStringAsFixed(2);
}
}`
cartpage
`class CartPage extends StatelessWidget {
CartPage({
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
Device.screenType == ScreenType.tablet;
return Container(
width: 100.w,
height: 90.5.h,
child: Container(
width: 100.w,
height: 20.5.h,
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
iconTheme: IconThemeData(
color: Colors.grey[800],
),
),
body: Consumer<CartModel>(
builder: (context, value, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Let's order fresh items for you
Padding(
padding: EdgeInsets.symmetric(horizontal: 24.0),
child: Text(
"Cart",
style: GoogleFonts.notoSerif(
fontSize: 36,
fontWeight: FontWeight.bold,
),
),
),
// list view of cart
Expanded(
child: Padding(
padding: const EdgeInsets.all(12.0),
child: ListView.builder(
itemCount: (value.cartItems.length),
padding: EdgeInsets.all(12),
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(12.0),
child: Container(
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.circular(8)),
child: ListTile(
leading: Image.asset(
value.cartItems[index][2],
height: 36,
),
title: Text(
value.cartItems[index][0],
style: const TextStyle(fontSize: 18),
),
subtitle: Text(
'\points ' + value.cartItems[index][1],
style: const TextStyle(fontSize: 12),
),
trailing: IconButton(
icon: const Icon(Icons.cancel),
onPressed: () => Provider.of<CartModel>(
context,
listen: false)
.removeItemFromCart(index),
),
),
),
);
},
),
),
),
// total amount + pay now
Padding(
padding: const EdgeInsets.all(25.0),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Colors.blueGrey[900]),
padding: const EdgeInsets.all(24),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'total price',
style: TextStyle(color: Colors.green[200]),
),
const SizedBox(height: 8),
// total price
Text(
'\Points ${value.calculateTotal()}',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
],
),
// pay now
Container(
),
padding: const EdgeInsets.all(1),
child: Row(
children: [
ElevatedButton(
onPressed: () {},
child: const Text('order now')),
],
),
),
],
),
),
)
],
);
},
),
),
),
);
}
}
`
# points page
`class points extends StatefulWidget {
points({Key? key,}) :super(key: key);
_pointsState createState() => _pointsState();
}
class _pointsState extends State<points> {
int _points = 0;
#override
void initState() {
super.initState();
_loadPunti();
}
_loadPunti() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
_points = (prefs.getInt('points') ?? 0);
});
}
_savePunti() async {
await FirebaseFirestore.instance.collection('xxxx').add({
'Points': _points,
});
}
other code...
hi all, I was looking for a solution for this test of a flutter shopping app and I'm having problems in being able to use the "points" variable in the cart page to be able to confirm a purchase on the "onpressed" in the cartpage, I also attached the cartmodel like this to be able to verify. Thank you all

Pass arguments from Modal Route to a post http request in Flutter

I'm passing arguments with pushNamed to a screen and I can access it inside my ListView but then I want to send some of the arguments with an ElevatedButton to my post request but I can't access it. I've tried many ways to do this can someone tell me what I'm doing wrong? Thanks in advance!
Where I use pushNamed
child: ElevatedButton(
style: ElevatedButton.styleFrom(
disabledBackgroundColor: Colors.grey),
onPressed: (() async {
Navigator.of(context).pushNamed(
SelectedAthletes.routeName,
arguments: selectedAthlete.toList());
})
The screen I'm passing the argument
class SelectedAthletes extends StatefulWidget {
const SelectedAthletes({Key? key}) : super(key: key);
static const routeName = '/selectedAthletes';
#override
State<SelectedAthletes> createState() => _SelectedAthletesState();
}
class _SelectedAthletesState extends State<SelectedAthletes> {
#override
Widget build(BuildContext context) {
final args = ModalRoute.of(context)!.settings.arguments as List<Athlete>;
return Scaffold(
backgroundColor: const Color(0Xfff7f7f5),
body: Stack(
children: [
SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ListView.builder(
shrinkWrap: true,
cacheExtent: 34,
primary: true,
physics: const ClampingScrollPhysics(),
padding: const EdgeInsets.only(
top: 10,
bottom: 56,
),
itemCount: args.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: const Icon(Icons.history_outlined,
color: Colors.black, size: 25),
title: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
'ID: ${args[index].id}',
style: const TextStyle(
color: Colors.blue, fontSize: 14),
),
const SizedBox(
width: 5,
),
Row(
children: [
Flexible(
child: Text(
'${args[index].lastName} ${args[index].firstName}',
style: const TextStyle(
color: Colors.black,
fontFamily: 'Cera',
fontWeight: FontWeight.bold,
fontSize: 18),
),
),
],
),
const SizedBox(
height: 5,
),
Row(
children: [
Text(
'(${args[index].fatherName})',
style: const TextStyle(
color: Colors.black,
fontFamily: 'Cera',
fontSize: 14),
),
const SizedBox(
width: 20,
),
Text(
'Π: ${args[index].currentMonthPresences}',
style: const TextStyle(
color: Colors.black,
fontFamily: 'Cera',
fontSize: 14),
),
const SizedBox(
width: 50,
),
],
),
],
));
},
)
],
),
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
width: double.infinity,
height: 60,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
disabledBackgroundColor: Colors.grey),
onPressed: () async {
await ApiService.insertPresences(args[index].id, args[index].firstName, args[index].lastName);
},
child: const Center(
child: Text(
'SEND',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: 18),
),
),
),
),
),
],
));
}
My post request
static Future<Athlete> insertPresences(
int athleteId, int departmentId, int teamId) async {
try {
final response = await http.post(
Uri.parse(
'http://164.92.170.94:8080/nox/api/nox/api/insert-presences'),
headers: {
'Authorization': 'Basic a2F4cmlzOjEyMzQ1',
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: jsonEncode(<String, int>{
"athleteId": athleteId,
"departmentId": departmentId,
"teamId": teamId,
}));
print('Response status: ${response.statusCode}');
print('Response body: ${response.body}');
if (response.statusCode == 201) {
return Athlete.fromJson(jsonDecode(response.body));
}
} catch (e) {
logger.e(e.toString());
}
return insertPresences(athleteId, departmentId, teamId);
}
Error in my ElevatedButton in SelectedAthletes screen
Undefined name 'index'.
Try correcting the name to one that is defined, or defining the name.
You passed a List to your second screen and you need to specify which item you want to pass to your api.
If you want pass the first item, simply use 0 instead of index like this:
onPressed: () async {
await ApiService.insertPresences(args[0].id, args[0].firstName, args[0].lastName);
},
And if you want to show a button for all items, you need to move the ElevatedButton widget to your ListView.builder widget like this:
ListView.builder(
...,
itemBuilder: (BuildContext context, int index) {
//You can use index here
return ListTile(
...,
title: Column(
children: [
...,//some other widgets
ElevatedButton(
...,
onPressed: () async {
await ApiService.insertPresences(args[index].id, args[index].firstName, args[index].lastName);
},
]
);
}
)

How to merge new field value to an existing firestore document with flutter?

I am trying to assign a delivery agent to the order of products with a dropdown containing a list of delivery agents. I am trying to get documentId and merging new field value to the documentId which already exists. But as a result, I a getting a duplicate document with the same documentId containing filed values which I want to merge. kindly help me to understand what I am doing wrong.
full code order_view.dart
import 'package:flutter/material.dart';
import 'package:loading_animation_widget/loading_animation_widget.dart';
import '../../services/firebase_database.dart';
import '../widgets/snack_bar.dart';
import 'client_address.dart';
import 'dashboard.dart';
import 'package:url_launcher/url_launcher.dart';
class OrderView extends StatefulWidget {
final Map<String, dynamic> orderData;
final List<String> deliveryAgent;
const OrderView(
{Key? key, required this.orderData, required this.deliveryAgent})
: super(key: key);
#override
State<OrderView> createState() => _OrderViewState();
}
class _OrderViewState extends State<OrderView> {
final FirebaseDatabase _database = FirebaseDatabase();
int index = 0;
num totalAmount = 0;
Map<String, dynamic> clientData = {};
getcountTotalAmount() async {
for (var item in widget.orderData["orders"]) {
totalAmount = totalAmount + (item["productPrice"] * item["amount"]);
}
clientData = (await _database.getClientData(widget.orderData["userId"]))!;
setState(() {});
}
#override
void initState() {
getcountTotalAmount();
super.initState();
}
#override
Widget build(BuildContext context) {
bool _isloading = true;
return Scaffold(
backgroundColor: Colors.grey[200],
appBar: AppBar(
elevation: 4,
title: const Text("Open Order"),
actions: [
TextButton(
onPressed: () async {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: const Text(
"Are you sure you want to cancel this order?",
textAlign: TextAlign.center,
),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text(
"No",
style:
TextStyle(color: Colors.green, fontSize: 16),
)),
TextButton(
onPressed: () async {
// Navigator.pop(context);
showDialog(
context: context,
builder: (context) {
return LoadingAnimationWidget.inkDrop(
color: Colors.orange,
size: 50,
);
});
await _database.removeOrder(
createTime: widget.orderData["createAt"],
);
Navigator.of(context).pop();
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (context) =>
const AdminDashboard()),
ModalRoute.withName(''),
);
ScaffoldMessenger.of(context).showSnackBar(
snackBar(
message: "Your order is removed",
color: Colors.deepOrange),
);
},
child: const Text(
"Yes",
style: TextStyle(color: Colors.red, fontSize: 16),
)),
],
);
});
},
child: const Text(
"Cancel Order",
style: TextStyle(
color: Colors.red,
fontWeight: FontWeight.w500,
fontSize: 15,
),
),
),
],
),
body: SizedBox(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: ListView(
physics: const BouncingScrollPhysics(),
children: [
Container(
color: Colors.white,
child: ListView.separated(
padding: const EdgeInsets.symmetric(vertical: 8),
shrinkWrap: true,
itemCount: widget.orderData["orders"].length,
physics: const BouncingScrollPhysics(),
itemBuilder: (context, index) {
return ListTile(
leading: Image.network(
widget.orderData["orders"][index]["productImage"],
),
title: Text(
widget.orderData["orders"][index]["productTitle"],
),
subtitle: SizedBox(
width: MediaQuery.of(context).size.width,
child: Row(
children: [
Text(
"₹ ${widget.orderData["orders"][index]["productPrice"]}",
style: const TextStyle(
color: Colors.green, fontSize: 12),
),
const Text(" - "),
Text(
widget.orderData["orders"][index]["productUnit"],
style: const TextStyle(fontSize: 12),
),
const Text(" x "),
Text(widget.orderData["orders"][index]["amount"]
.toString()),
const SizedBox(width: 5),
Text(
"Total ₹ ${widget.orderData["orders"][index]["productPrice"] * widget.orderData["orders"][index]["amount"]}",
style: const TextStyle(
color: Colors.green,
fontWeight: FontWeight.w600,
fontSize: 12,
),
),
],
),
),
);
},
separatorBuilder: (BuildContext context, int index) {
return const Divider();
},
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
color: Colors.white,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Delivery Slot'),
Text(
'${widget.orderData["deliverySlot"]}',
style: const TextStyle(
fontWeight: FontWeight.w500, fontSize: 16),
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
"Delivery charge: ₹${widget.orderData["taxes"]}",
),
Text(
"Total Price: ₹${totalAmount + widget.orderData["taxes"]}",
style: const TextStyle(
fontWeight: FontWeight.w500, fontSize: 16),
),
],
)
],
),
),
const SizedBox(height: 16),
Container(
width: MediaQuery.of(context).size.width,
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
decoration: const BoxDecoration(
color: Colors.white,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Ordered on: ${widget.orderData["createAt"]}',
style: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 14,
),
),
Text(
'Delivery at ${widget.orderData["addressType"]}',
),
const SizedBox(
height: 16,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.6,
child: Text(
'Address: ${widget.orderData["address"]}',
),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => ClientAddress(
lat: widget.orderData["latitude"],
lag: widget.orderData["longitude"],
)));
},
child: const Text('Open location'))
],
),
],
),
),
const SizedBox(
height: 16,
),
Container(
width: MediaQuery.of(context).size.width,
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
color: Colors.white,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"Client Details",
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 17,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
child: Text(
clientData["username"] ?? "",
style: const TextStyle(
fontWeight: FontWeight.w500,
fontSize: 15,
),
),
),
SizedBox(
child: Text(
clientData["contact"] ?? "",
style: const TextStyle(
fontWeight: FontWeight.w500,
fontSize: 15,
),
),
),
ElevatedButton.icon(
onPressed: () async {
String telephoneNumber = clientData["contact"];
Uri telephoneUrl = Uri.parse("tel:$telephoneNumber");
if (await canLaunchUrl(telephoneUrl)) {
await launchUrl(telephoneUrl);
}
},
icon: const Icon(
Icons.call,
),
label: const Text('Call'),
)
],
),
],
),
),
const SizedBox(
height: 16,
),
Container(
width: MediaQuery.of(context).size.width,
color: Colors.white,
padding: const EdgeInsets.only(bottom: 8),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
const Text(
"Delivery Agent",
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16,
),
),
DropdownButton(
items: widget.deliveryAgent
.map(
(e) => DropdownMenuItem(
value: e,
child: Text(e),
),
)
.toList(),
onChanged: (val) {
setState(() {
index =
widget.deliveryAgent.indexOf(val.toString());
});
},
isExpanded: false,
hint: Text(
widget.deliveryAgent[index],
style: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 15,
),
),
),
],
),
ElevatedButton(
onPressed: () async {
bool results;
results = await _database.updateDeliveryAgent(
orderId: widget.orderData["orderId"],
deliveryAgent: widget.deliveryAgent[index].toString(),
);
if (results) {
setState(() {
_isloading = true;
});
ScaffoldMessenger.of(context).showSnackBar(
snackBar(
message: "Delivery agent assigned",
color: Colors.green,
),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
snackBar(
message: "Error",
color: Colors.red,
),
);
}
setState(() {
_isloading = true;
});
},
child: const Text('Assign Agent')),
],
)),
],
),
),
);
}
}
firebase_database.dart
CollectionReference<Map<String, dynamic>> testorders =
FirebaseFirestore.instance.collection('testorders');
Future<bool> updateDeliveryAgent(
{required String orderId, required String deliveryAgent}) async {
try {
testorders.doc(orderId).set({
"deliveryAgent": {
"agentName": deliveryAgent,
"agentContact": '',
},
}, SetOptions(merge: true));
return true;
} catch (e) {
if (kDebugMode) {
print("error while assigning delivery agent: $e");
}
}
return false;
}
order.dart
Container(
width: MediaQuery.of(context).size.width,
color: Colors.white,
padding: const EdgeInsets.only(bottom: 8),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
const Text(
"Delivery Agent",
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16,
),
),
DropdownButton(
items: widget.deliveryAgent
.map(
(e) => DropdownMenuItem(
value: e,
child: Text(e),
),
)
.toList(),
onChanged: (val) {
setState(() {
index =
widget.deliveryAgent.indexOf(val.toString());
});
},
isExpanded: false,
hint: Text(
widget.deliveryAgent[index],
style: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 15,
),
),
),
],
),
ElevatedButton(
onPressed: () async {
bool results;
results = await _database.updateDeliveryAgent(
orderId: widget.orderData["orderId"],
deliveryAgent: widget.deliveryAgent[index].toString(),
);
if (results) {
setState(() {
_isloading = true;
});
ScaffoldMessenger.of(context).showSnackBar(
snackBar(
message: "Delivery agent assigned",
color: Colors.green,
),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
snackBar(
message: "Error",
color: Colors.red,
),
);
}
setState(() {
_isloading = true;
});
},
child: const Text('Assign Agent')),
],
)),
You cannot update a document ID once it's been defined.
See the following answer on alternative steps you can take: https://stackoverflow.com/a/52117929/9063088
There is no way you can have duplicate document IDs inside the same collection. The screenshot shows two documents sharing the same ID but for sure one of them contains one or more white spaces at the end. So there are two situations, the orderId is correct and when you call set() it adds the document with a document ID that doesn't contains white spaces, while in the database there is already one present that contains white spaces, or vice versa. When it comes to document IDs, always call .trim() so it can remove the white spaces from the beginning and from the end.
I fixed this, the main reason was firstly when I created the document with .add() where firebase auto generate the document ID, the and later while merging data in the same document ID it was creating the duplicate document ID maybe with some whitespace in it which was not visible.
But to rectify I created the data with .set() by generating the document by my own and later while merging data uses the same document ID to merge new fields in the same document ID. and finally it worked.

Flutter- click is responding to every item in listview.builder

I am trying to create a list of items in flutter with listView.builder, i am also using using elevatedButton in my itemBuilder.
i applied setState in button
i want to change value of single item only
but that is applying to every value in list
here is my code
import 'dart:developer';
import 'package:counter_button/counter_button.dart';
import 'package:flutter/material.dart';
import 'package:fooddeliveryapp/apis.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'models/restrauntmenu.dart';
class RestrauntPage extends StatefulWidget {
final String restrauntId;
final String restrauntName;
final String restrauntType;
final String restrauntApproxBill;
final String restrauntTagline;
const RestrauntPage(
{Key? key,
required this.restrauntId,
required this.restrauntType,
required this.restrauntApproxBill,
required this.restrauntTagline,
required this.restrauntName})
: super(key: key);
#override
State<RestrauntPage> createState() => _RestrauntPageState();
}
class _RestrauntPageState extends State<RestrauntPage> {
int _counterValue = 1;
bool itemAdded = false;
late Future<List<RestrauntMenu>> futureRestrauntMenu;
Future<List<RestrauntMenu>> fetchRestrauntMenu() async {
String restrauntId = widget.restrauntId;
final response = await http.get(Uri.parse(menuApi(restrauntId)));
if (response.statusCode == 200) {
final parsed = json.decode(response.body).cast<Map<String, dynamic>>();
return parsed
.map<RestrauntMenu>((json) => RestrauntMenu.fromMap(json))
.toList();
} else {
throw Exception('Failed to load album');
}
}
addToCartButton(
{required String? itemname,
required String? itemprice,
required int? itemCount,
required String? usermob,
required String? restrauntName}) {
return ElevatedButton(
clipBehavior: Clip.antiAliasWithSaveLayer,
style: ButtonStyle(
elevation: MaterialStateProperty.all(
6,
),
backgroundColor: MaterialStateProperty.all(Colors.white),
),
onPressed: () {
addToCart(
itemname: itemname,
itemprice: itemprice,
restrauntName: restrauntName,
usermob: usermob,
itemCount: itemCount);
},
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Text(
'ADD TO CART',
style: TextStyle(
color: Colors.green,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
),
);
}
counterButton() {
return Container(
decoration: const BoxDecoration(color: Colors.white),
child: CounterButton(
loading: false,
onChange: (int val) {
setState(() {
_counterValue = val;
});
},
count: _counterValue,
countColor: Colors.green,
buttonColor: Colors.black,
progressColor: Colors.black,
),
);
}
Future addToCart(
{required String? itemname,
required String? itemprice,
required int? itemCount,
required String? usermob,
required String? restrauntName}) async {
itemCount = _counterValue;
var url = addToCartApi(
itemname: itemname,
itemprice: itemprice,
itemCount: itemCount,
usermob: usermob,
restrauntName: restrauntName);
var response = await http.get(
Uri.parse(url),
);
if (response.statusCode == 200) {
setState(() {
itemAdded = true;
});
} else {
return false;
}
}
#override
void initState() {
futureRestrauntMenu = fetchRestrauntMenu();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
foregroundColor: Colors.black,
elevation: 0,
backgroundColor: Colors.white,
title: const Text('Restraunt Name'),
),
body: SingleChildScrollView(
child: SizedBox(
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.03,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.restrauntName,
style: const TextStyle(
color: Colors.black,
fontSize: 24,
fontWeight: FontWeight.w900,
),
),
// const SizedBox(height: 4),
Text(
widget.restrauntType,
style: const TextStyle(
color: Colors.black,
fontSize: 16,
fontWeight: FontWeight.normal,
),
),
Text(
widget.restrauntTagline,
style: const TextStyle(
color: Colors.black87,
fontSize: 12,
fontWeight: FontWeight.normal,
),
),
const SizedBox(
height: 6,
),
Container(
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.circular(10),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: RichText(
text: TextSpan(
children: [
const WidgetSpan(
child: Icon(
Icons.currency_rupee,
size: 16,
color: Color.fromARGB(255, 45, 174, 49),
),
),
TextSpan(
text: widget.restrauntApproxBill,
style: const TextStyle(
color: Colors.black,
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
],
),
),
),
),
],
),
const Spacer(),
Container(
decoration: BoxDecoration(
color: const Color.fromARGB(255, 49, 171, 53),
borderRadius: BorderRadius.circular(12),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: RichText(
text: const TextSpan(
children: [
TextSpan(
text: '3.6',
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
WidgetSpan(
child: Icon(
Icons.star,
color: Colors.white,
size: 18,
),
),
],
),
),
),
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.03,
),
],
),
const SizedBox(
height: 12,
),
Expanded(
child: FutureBuilder<List<RestrauntMenu>>(
future: futureRestrauntMenu,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
return ExpansionTile(
initiallyExpanded: true,
childrenPadding: const EdgeInsets.all(8),
title: Text(
snapshot.data![index].catname!,
style: const TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
),
children: [
Row(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
snapshot.data![index].itemname!,
style: const TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
),
),
const SizedBox(
height: 4,
),
Text(
snapshot.data![index].itemPrice!,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
),
),
const SizedBox(
height: 4,
),
Text(
snapshot.data![index].itemDescription!,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.normal,
),
),
],
),
const Spacer(),
Stack(
alignment: Alignment.bottomCenter,
children: [
Padding(
padding:
const EdgeInsets.only(bottom: 16),
child: ClipRRect(
borderRadius:
BorderRadius.circular(10),
child: FittedBox(
child: Container(
height: MediaQuery.of(context)
.size
.height *
0.22,
width: MediaQuery.of(context)
.size
.width *
0.4,
decoration: BoxDecoration(
color: Colors.red,
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(
'http://www.jfamoslogistics.com/images/${snapshot.data![index].itemimage!}',
),
),
),
),
),
),
),
Align(
alignment: Alignment.topRight,
child: itemAdded
? counterButton()
: addToCartButton(
itemprice: snapshot
.data![index].itemPrice,
itemname: snapshot
.data![index].itemname,
itemCount: _counterValue,
restrauntName:
widget.restrauntName,
usermob: '9354954343',
))
],
)
],
),
],
);
},
);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return const Center(
child: CircularProgressIndicator(),
);
},
),
),
],
),
),
),
);
}
}
please check and help me
please let me know if i am missing something or making some mistake in code
i have tried google everywhere but nothing works
thanks in advance
As Giseppe Colucci described, you are using a single counter variable for a list . You can follow the simple approach of using list. On State
int _counterValue = 1; will be replaced with List<int> _counterValue = []
After getting data initialize the list with default value.
if (snapshot.hasData) {
_counterValue =List.generate(snapshot.data!.length, (index) => 1); // you might prefer default value as 0 instead of 1
As for the counter button method we need to update specific index, therefore we will pass index here
counterButton(int index) {
return Container(
decoration: const BoxDecoration(color: Colors.white),
child: CounterButton(
loading: false,
onChange: (int val) {
setState(() {
_counterValue[index] = val;
});
},
count: _counterValue[index],
Now whenever we use this counterButton method we need to pass index and here we get index from listview.
child: itemAdded
? counterButton(index)
: addToCartButton(
That is because your itemAdded bool is only one, for every item in the list, you should make a map, like this:
{'id':true}
where id is the restaurant menu item, and true is whenever the item is selected or not.
If this is too hard for you, just use a simple list.

ListView.builder returns Null check operator used on a null value

I'm developing a job search app that scrapes data from Indeed using Python which is being sent back to my Flutter UI as JSON data. The JSON data is being received successfully, however, Im getting an error of Null check operator used on a null value. The error appears to be stemming from the _jobSearch widget.
The relevant error-causing widget was ListView lib/ui/home_page.dart:256
Exception caught by scheduler library
Null check operator used on a null value
Here is the code:
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:flutter_job_portal/theme/colors.dart';
import 'package:flutter_job_portal/theme/images.dart';
import 'package:flutter_job_portal/ui/bottom_menu_bar.dart';
import 'package:flutter_job_portal/ui/job_detail_page.dart';
String job = ""; //user's response will be assigned to this variable
String final_response = "";
final _formkey = GlobalKey<FormState>(); //key created to interact with the form
//function to validate and save user form
Future<void> _savingData() async {
final validation = _formkey.currentState.validate();
if (!validation) {
return;
}
_formkey.currentState.save();
}
Future<List<Job>> _getJobs() async {
final url = 'http://127.0.0.1:5000/job';
final response1 = await http.post(Uri.parse(url), body: json.encode({'job': job}));
final response2 = await http.get(Uri.parse(url));
final decoded = json.decode(response2.body);
List<Job> jobs = [];
for (var i in decoded) {
Job job = Job(i['Title'], i['Company'], i['Location'], i['Salary']);
jobs.add(job);
}
return jobs;
}
class Job {
final String title;
final String company;
final String location;
final String salary;
Job(this.title, this.company, this.location, this.salary);
}
class HomePage extends StatelessWidget {
const HomePage({Key key}) : super(key: key);
Widget _appBar(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Row(
children: [
CircleAvatar(
backgroundImage: AssetImage(Images.user1),
),
Spacer(),
IconButton(
icon: Icon(Icons.notifications_none_rounded),
onPressed: () {},
)
],
),
);
}
Widget _header(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(vertical: 12),
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Hello, Alex!",
style: TextStyle(
fontSize: 15,
color: KColors.subtitle,
fontWeight: FontWeight.w500,
)),
SizedBox(
height: 6,
),
Text("Swipe to find your future",
style: TextStyle(
fontSize: 20,
color: KColors.title,
fontWeight: FontWeight.bold)),
SizedBox(
height: 10,
),
Row(
children: [
Expanded(
child: Container(
height: 45,
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: KColors.lightGrey,
borderRadius: BorderRadius.circular(10)),
child: Form(
key: _formkey,
child: TextFormField(
decoration: InputDecoration(
hintText: 'Search job title or keywords',
),
onSaved: (value) {
job =
value; //getting data from the user form and assigning it to job
},
),
),
),
),
SizedBox(
width: 16,
),
Container(
decoration: BoxDecoration(
color: KColors.primary,
borderRadius: BorderRadius.circular(10),
),
height: 40,
child: IconButton(
color: KColors.primary,
icon: Icon(Icons.search, color: Colors.white),
onPressed: () async {
_savingData();
_getJobs();
},
),
)
],
)
],
),
);
}
Widget _recommendedSection(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 10),
margin: EdgeInsets.symmetric(vertical: 12),
height: 200,
width: MediaQuery.of(context).size.width,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Recommended",
style: TextStyle(fontWeight: FontWeight.bold, color: KColors.title),
),
SizedBox(height: 10),
Expanded(
child: ListView(
scrollDirection: Axis.horizontal,
children: [
_recommendedJob(context,
company: "Google",
img: Images.google,
title: "UX Designer",
sub: "\$45,000 Remote",
isActive: true),
_recommendedJob(context,
company: "DropBox",
img: Images.dropbox,
title: "Research Assist",
sub: "\$45,000 Remote",
isActive: false)
],
),
),
],
),
);
}
Widget _recommendedJob(
BuildContext context, {
String img,
String company,
String title,
String sub,
bool isActive = false,
}) {
return Padding(
padding: const EdgeInsets.only(right: 10),
child: GestureDetector(
onTap: () {
Navigator.push(context, JobDetailPage.getJobDetail());
},
child: AspectRatio(
aspectRatio: 1.3,
child: Container(
decoration: BoxDecoration(
color: isActive ? KColors.primary : Colors.white,
borderRadius: BorderRadius.circular(7),
),
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 40,
width: 40,
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
color: isActive ? Colors.white : KColors.lightGrey,
borderRadius: BorderRadius.circular(7),
),
child: Image.asset(img),
),
SizedBox(height: 16),
Text(
company,
style: TextStyle(
fontSize: 12,
color: isActive ? Colors.white38 : KColors.subtitle,
),
),
SizedBox(height: 6),
Text(
title,
style: TextStyle(
fontSize: 14,
color: isActive ? Colors.white : KColors.title,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 6),
Text(
sub,
style: TextStyle(
fontSize: 12,
color: isActive ? Colors.white38 : KColors.subtitle,
),
),
],
),
),
),
),
);
}
Widget _jobSearch(BuildContext context) {
return new Container(
child: FutureBuilder(
future: _getJobs(),
builder: (context, snapshot) {
if (snapshot.data == null) {
return Container(
child: Center(
child: Text('Loading...'),
));
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(snapshot.data[index].location),
);
},
);
}
},
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: KColors.background,
bottomNavigationBar: BottomMenuBar(),
body: SafeArea(
child: Container(
width: MediaQuery.of(context).size.width,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_appBar(context),
_header(context),
_recommendedSection(context),
_jobSearch(context)
],
),
),
),
),
);
}
}