Looping through Firestore is ignoring async await? - flutter

So I have an app where I have images stored on Firebase Storage and I want to loop through a few records in my Firestore and create Containers() for each of them to be used in a PageView. However, when looping through it seems like the AWAIT clues are ignored somehow?
This is what I got:
loadDashboard() async {
// Load data about breed in focus
Map<String, dynamic> _documentData;
_isLoadingArticles = true;
_dashboardArticles.clear();
int _articleCount = 0;
await _firestore
.collection('DashboardArticle')
.where("active", isEqualTo: true)
.get()
.then((QuerySnapshot querySnapshot) {
querySnapshot.docs.forEach((doc) {
_articleCount++;
downloadURL(doc["image"]);
print(doc["title"]);
_dashboardArticles.add(
Padding(
padding: EdgeInsets.fromLTRB(5, 0, 5, 0),
child: Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(15),
child: Image(
image: NetworkImage(
_articleImgUrl,
),
alignment: Alignment.center,
width: double.infinity,
fit: BoxFit.cover,
),
),
Padding(
padding: EdgeInsets.fromLTRB(20, 10, 0, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 25),
Text(
doc["title"],
style: TextStyle(
shadows: [
Shadow(
blurRadius: 10.0,
color: Colors.black,
offset: Offset(1.0, 1.0),
),
],
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold),
),
SizedBox(height: 5),
Text(
doc["tagline"],
style: TextStyle(
color: Colors.white,
fontSize: 16,
shadows: [
Shadow(
blurRadius: 10.0,
color: Colors.black,
offset: Offset(1.0, 1.0),
),
],
),
)
],
),
),
],
),
),
);
setState(() {
_isLoadingArticles = false;
_totalArticleDots = _articleCount;
});
});
}).catchError((e) => print("error fetching data: $e"));
}
Future<String> downloadURL(String image) async {
await firebase_storage.FirebaseStorage.instance
.ref('dashboard_article_images/$image')
.getDownloadURL()
.then((value) => {
setState(() {
_articleImgUrl = value;
print(_articleImgUrl);
})
})
.catchError((e) => {
setState(() {
_articleImgUrl = "";
})
});
return "";
}
_articleImgUrl is a variable outside that I set as I'm not sure how to get the String value from the Future in any other way.
Each PRINT shows the data in correct order, for example:
flutter: Pooch in focus: Corgi
flutter: https://firebasestorage.googleapis.com:443/v0/b/.../o/article_images%2F4f3... (I truncated here)
When I se breakpoints it seems that _articleImgUrl has no value at the time the _dashboardArticles.add(...) is executed, so it feels like the awaits are skipped when looping somehow?
Invalid argument(s): No host specified in URI file:///
Can anyone provide insights? Thanks!

Yes your await is ignored because you are not awaiting your method downloadURL, also I would not recommend to use querySnapshot.docs.forEach as you won't be able to manage asynchronous operations correctly. You should use for in instead, also you should not use await and then at the same time.
Here is some fix you could apply to your code:
final querySnapshot = await _firestore
.collection('DashboardArticle')
.where("active", isEqualTo: true)
.get().catchError((e) => print("error fetching data: $e"));
for (final doc in querySnapshot.docs) {
_articleCount++;
await downloadURL(doc['image']);
print(doc["title"]);
// rest of your loop ...
}
By doing so you ensure all of your asynchronous operations are correctly awaited.

Related

getting duplicate data in stream builder flutter

I hope you guys are happy and healthy
I am facing a problem in flutter
i will be very thankful to you if any of you help me resolving this issue
I am trying to fetch data from api
and show it in alphabetic list with the help of stream builder
but i am getting the same data multiple of time
let me show you the code
here i am trying to fetch data from api and convert it into list
Future<List<DisplayClients>> getClientList() async {
String email = widget.email;
final response = await http.get(
Uri.parse(
api),
);
if (response.statusCode == 200) {
final parsed = json.decode(response.body).cast<Map<String, dynamic>>();
var list = parsed
.map<DisplayClients>(
(json) => DisplayClients.fromMap(json),
)
.toList();
print('clientList');
print(list.length);
for (var i = 0; i < list.length; i++) {
clientList.add(AlphabetSearchModel(
title: list[i].name,
subtitle: list[i].email,
phoneNumber: list[i].phoneNumber));
}
print(clientList);
return parsed
.map<DisplayClients>(
(json) => DisplayClients.fromMap(json),
)
.toList();
} else {
throw Exception('Failed to load album');
}
}
here i am showing i am using data in streamBuilder
StreamBuilder<List<DisplayClients>>(
stream: getClientsStream(),
builder: (context, snapshot) {
if (snapshot.hasData) {
// var list = s
print('got data');
print(snapshot.data!.length);
return SizedBox(
height: MediaQuery.of(context).size.height * 0.7,
child: AlphabetSearchView.modelList(
list: clientList,
buildItem: (context, index, item) {
return Padding(
padding:
const EdgeInsets.symmetric(vertical: 6),
child: Card(
// margin: EdgeInsets.all(12),
elevation: 3,
color: Constants.purpleLight,
shadowColor: Colors.white,
child: Row(children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
colors: [
Constants.red,
Constants.orange,
],
begin: Alignment.bottomLeft,
end: Alignment.topRight)),
child: CircleAvatar(
radius: 40,
child: Icon(
Icons.person,
color: Colors.white,
size: 40,
),
backgroundColor:
Colors.transparent),
),
),
SizedBox(
width: 10,
),
CustomText(
text: item.title,
color: Colors.white,
size: 18,
weight: FontWeight.w400,
),
SizedBox(
width: 10,
),
CircleAvatar(
backgroundColor: Constants.orange,
radius: 4,
),
SizedBox(
width: 10,
),
CustomText(
text: item.subtitle,
color: Colors.white70,
size: 14,
weight: FontWeight.w400,
),
SizedBox(
width: 10,
),
CircleAvatar(
backgroundColor: Constants.orange,
radius: 4,
),
SizedBox(
width: 10,
),
CustomText(
text: item.phoneNumber,
color: Colors.white70,
size: 14,
weight: FontWeight.w400,
),
Spacer(),
Padding(
padding: const EdgeInsets.all(12),
child: primaryButton(
verticalPadding: 0,
context: context,
text: 'Deal with ${item.title}',
onTap: () {
Navigator.push(context,
MaterialPageRoute(
builder: (context) {
return SeperateClient(
clientName: item.title,
email: widget.email,
clientEmail:
item.subtitle!);
}));
}),
)
])));
},
)
);
}
return Container();
},
)
i am calling getClientList() method in initSate
here is code for getClientStream()
Stream<List<DisplayClients>> getClientsStream() async* {
while (true) {
await Future.delayed(Duration(milliseconds: 1));
yield await getClientList();
}
}
this code is returning multiple data's
please let me know where i am making mistakes
i will be thankful to you
Thanks :)
Try the following code:
Stream<List<DisplayClients>> getClientsStream() async* {
await Future.delayed(const Duration(milliseconds: 1));
String email = widget.email;
final response = await http.get(
Uri.parse(api),
);
if (response.statusCode == 200) {
final parsed = json.decode(response.body).cast<Map<String, dynamic>>();
var list = parsed
.map<DisplayClients>(
(json) => DisplayClients.fromMap(json),
)
.toList();
print('clientList');
print(list.length);
for (var i = 0; i < list.length; i++) {
clientList.add(AlphabetSearchModel(title: list[i].name, subtitle: list[i].email, phoneNumber: list[i].phoneNumber));
}
print(clientList);
yield* parsed
.map<DisplayClients>(
(json) => DisplayClients.fromMap(json),
)
.toList();
} else {
throw Exception('Failed to load album');
}
}

saving a value from a document snapshot from firestore

in the following code i want to save the String data that i get from firestore inside the .then() method into another variable. but once its outside the method i lose the value.
String memberName = '';
member.user.get().then(
(DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists) {
memberName = documentSnapshot.get('Full_Name');
print(memberName) //'John Smith'
}
},
);
print(memberName) //''
posting all the code for reference.
Widget buildGroupMemberTile(GroupMember member, bool loggedUserIsAdmin) {
final memberDoc = FirebaseFirestore.instance
.collection('groups')
.doc(widget.group.id)
.collection("members")
.doc(member.email);
print(getMemberName(member));
String memberName = '';
member.user.get().then(
(DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists) {
memberName = documentSnapshot.get('Full_Name');
print(memberName) //'John Smith'
}
},
);
print(memberName) //''
return ListTile(
onTap: (() {
if (loggedUserIsAdmin) {
if (member.role == 'admin') {
memberDoc.update({'role': 'member'});
} else if (member.role == 'member') {
memberDoc.update({'role': 'admin'});
}
}
}),
title: Center(
child: Padding(
padding: EdgeInsets.fromLTRB(0, 5, 0, 5),
child: Container(
width: 450,
decoration: BoxDecoration(
color: Color.fromARGB(255, 65, 61, 82),
borderRadius: BorderRadius.all(Radius.circular(12))),
child: Padding(
padding: const EdgeInsets.fromLTRB(40, 20, 40, 20),
child: Column(
children: [
Text(
memberName,
style: GoogleFonts.poppins(
color: ThemeColors.whiteTextColor,
fontSize: FontSize.large,
fontWeight: FontWeight.w400,
),
),
],
),
),
),
),
));
}
Im guessing that I'm referencing documentSnapshot.get('Full_Name') and not copying its the value to memberName. How can i keep the value?
try to get it like this without the then, and call the data() first to get the Map<String, dynamic> of the docuemnt:
DocumentSnapshot documentSnapshot = await member.user.get();
if (documentSnapshot.exists) {
memberName = (documentSnapshot.data() as Map<String, dynamic>)["Full_Name"];
print(memberName);
}
Note: you need to mark the method where this is implemented to Future with async keyword
Looks like you need to put the entire member.user.get() call into a async future method & place an await keyword just before the call.
Then you need to use a FutureBuilder around the widget that will be displaying the name to ensure the name has a value before it is rendered.

Rendering Filtered List in Flutter

I'm having trouble displaying a filtered list in my widget. It works up to the point of printing the filtered data in the list as per the query that is passed but not when that exact data needs to be displayed. I believe I will have to update the list with the filtered data every time I type in a query but I just cannot figure out how and where I need to use setState to update that. My code and the outputs are as follows:
Initially, the entire list gets rendered but the moment I type in a query string, the list is supposed to get modified with only the data that matched the query. This is not something that's happening at the moment. The list tends to remain as it is.
However, when I print the filtered data, it seems to work just fine(_searchResult printed in the searchData method below).
[
{product_id: 8, restaurant_name: Mocambo, restaurant_id: 6, product_name: Kaju Paneer, product_description: Tasty yummy paneer gravy dish, product_image: /public/assets/product/lgml5L03-19-41.jpg, product_selling_price: 320},
{product_id: 5, restaurant_name: City Club, restaurant_id: 1, product_name: Palak Paneer, product_description: Tasty silky gravy with goodness of palak, product_image: /public/assets/product/C6pGz101-42-17.jpg, product_selling_price: 180},
{product_id: 4, restaurant_name: City Club, restaurant_id: 1, product_name: Shahi Paneer, product_description: Tasty Paneer main course dish, product_image: /public/assets/product/vgI1dR01-29-18.jpg, product_selling_price: 240}
]
The code:
The method that filters. (Please note that the filtering is performed after the data is fetched from the server. For my convenience, I decided to convert it into a list)
class PopularDishesProvider with ChangeNotifier {
Map<String, dynamic> _dishes = {};
final List<dynamic> _searchDish = [];
List<dynamic> _searchResult = [];
List<dynamic> get searchDish {
return [..._searchDish];
}
List<dynamic> get searchResult {
return [..._searchResult];
}
Future<void> searchData(String query) async {
final url = Uri.parse(baseUrl + 'api/all_products');
final response = await http.get(url);
PopularDishes popularDishes = popularDishesFromJson(response.body); //This method converts the response into Dart model
_dishes = popularDishes.toJson();
_dishes['data'].forEach((value) => _searchDish.add(value));
_searchResult = _searchDish.where((element) {
final name = element['product_name'].toLowerCase();
final searchQuery = query.toLowerCase();
return name.contains(searchQuery);
}).toList();
print(_searchResult);
notifyListeners();
}
}
The widget where this is supposed to be rendered:
class SearchState extends State<Search> {
final _controller = TextEditingController();
bool value = true;
String query = '';
List<dynamic> search = [];
PopularDishesProvider popular = PopularDishesProvider();
#override
void initState() { //This is to make the API Call for the first time
// TODO: implement initState
Provider.of<PopularDishesProvider>(context, listen: false)
.searchData('');
});
super.initState();
}
#override
Widget build(BuildContext context) {
final height = MediaQuery.of(context).size.height;
final width = MediaQuery.of(context).size.width;
final textScale = MediaQuery.of(context).textScaleFactor * 1.2;
final searchProvider = Provider.of<PopularDishesProvider>(context).searchResult;
PopularDishesProvider popular = PopularDishesProvider();
// TODO: implement build
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
elevation: 5,
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
// backgroundColor: Colors.green,
titleSpacing: 0,
toolbarHeight: 100,
title: Column(
children: [
Container(
width: double.infinity,
height: 40,
.......
.......
.......
),
Stack(
children: [
Container(
height: 60,
width: double.infinity,
// color: Colors.red,
padding: const EdgeInsets.only(top: 8, left: 2),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Icon(
Icons.search,
size: 30,
color: Colors.grey,
),
Expanded(
child: Center(
child: Container(
margin:
const EdgeInsets.only(bottom: 6, right: 4),
padding: const EdgeInsets.only(left: 6),
height: 45,
width: width * 0.7,
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(
Radius.circular(14)),
border:
Border.all(color: Colors.grey, width: 2)),
child: Row(
children: [
Flexible(
flex: 9,
fit: FlexFit.tight,
child: Center(
child: TextField(
controller: _controller,
onChanged: (value) async {
setState(() {
query = value;
});
await popular.searchData(value);
},
autofocus: true,
cursorColor: Colors.grey,
style: const TextStyle(
color: Colors.grey, fontSize: 18),
decoration: const InputDecoration(
border: InputBorder.none,
hintText:
'Search By Restaurant or Food',
hintStyle:
TextStyle(color: Colors.grey),
),
),
)),
Flexible(
flex: 1,
fit: FlexFit.tight,
child: InkWell(
onTap: () => Navigator.of(context).pop(),
child: const Icon(Icons.close,
color: Colors.grey),
),
)
],
),
),
),
),
],
),
),
],
)
],
)),
body: Column(
children: [
Expanded(
child: Container(
width: double.infinity,
color: Colors.red,
child: ListView.builder(
itemBuilder: (context, index) => ListTile(
title: Text(searchProvider [index]['product_name'])),
itemCount: searchProvider.length,
),
)
)
],
),
);
}
}
Can someone please help out?

How can change my hardcoded data of a card carousel to dynamic data from api response -Flutter

Good day. Please I am have a challenge on how to change my hardcoded data to dynamic from the api. The response to the API returns success but populating the card carousel is the challenge I am having. I keep on getting error "type 'List' is not a subtype of type 'List<Map<String, String>>'"'
This my Api request:
SharedPreferences _prefs;
void getCategories() async {
try {
_prefs = await SharedPreferences.getInstance();
var _categoryService = CategoryService();
var result =
await _categoryService.getCategories(_prefs.getString('token'));
var categories = json.decode(result.body);
print('categories');
print(categories);
List<Map<String, String>> foodCategories = categories;
} catch (e) {
throw Exception();
}
}
This is my list variable with hard coded data:
final List<Map<String, String>> foodCategories = [
{
'name': 'Rice Planting',
'image': 'images/Icon-001.png',
},
{
'name': 'Harvesting',
'image': 'images/Icon-002.png',
},
{
'name': 'Machineries',
'image': 'images/Icon-003.png',
},
{
'name': 'Rice Products',
'image': 'images/Icon-004.png',
}
];
And this is my screen:
Container(
height: 105,
margin: const EdgeInsets.only(
top: 20.0,
bottom: 25.0,
),
child: ListView.builder(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.only(
left: 20.0,
),
itemCount: this.foodOptions.length,
itemBuilder: (context, index) {
Map<String, String> option = this.foodOptions[index];
return Container(
margin: const EdgeInsets.only(right: 35.0),
child: Column(
children: <Widget>[
Container(
width: 70,
height: 70,
margin: const EdgeInsets.only(bottom: 10.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(5.0),
),
image: DecorationImage(
image: AssetImage(
option['image'],
),
),
boxShadow: [
BoxShadow(
blurRadius: 10.0,
color: Colors.grey[300],
offset: Offset(6.0, 6.0),
)
],
),
),
Text(
option['name'],
style: TextStyle(fontSize: 17.0),
),
],
),
);
}),
),
My basic challenge is how to switch this hardcoded data of **foodCategories ** to
var categories = json.decode(result.body); which is from the API. any suggestion I will highly appreciate. The Api returns a json with image and category name
this is the UI picture
Declare this List in your screen widget:
final List<Map<String, String>> foodCategories = [
{
'name': 'Rice Planting',
'image': 'images/Icon-001.png',
},
{
'name': 'Harvesting',
'image': 'images/Icon-002.png',
},
{
'name': 'Machineries',
'image': 'images/Icon-003.png',
},
{
'name': 'Rice Products',
'image': 'images/Icon-004.png',
}
];
Add this method to your screen widget:
void getCategories() async {
try {
_prefs = await SharedPreferences.getInstance();
var _categoryService = CategoryService();
var result =
await _categoryService.getCategories(_prefs.getString('token'));
var categories = json.decode(result.body);
print('categories');
print(categories);
setState(() {
foodCategories = categories;
});
} catch (e) {
throw Exception();
}
}
and call the method in initState:
#override
void initState() {
super.initState();
getCategories();
}
You can use a state management tool for this use case, like Provider, on flutter.dev there is a nice explanation on how to use it Simple app state management.
Basically you wrap the api call inside a provider class and every time data comes from the api it notify the consumers that are using the provider.
Steps:
1 - Add the provider dependency to your pubspec.yaml.
name: my_name
description: Blah blah blah.
# ...
dependencies:
flutter:
sdk: flutter
provider: ^6.0.0
dev_dependencies:
# ...
2 - Create a provider for your desire state, in this case the list of categories
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../category_service.dart';
class CategoriesProvider with ChangeNotifier {
List<Map<String, String>> foodCategories = [];
bool loading = false;
var _prefs;
void getCategories() async {
try {
loading = true;
_prefs = await SharedPreferences.getInstance();
var _categoryService = CategoryService();
var result =
await _categoryService.getCategories(_prefs.getString('token'));
var categories = json.decode(result.body);
print('categories');
print(categories);
List<Map<String, String>> foodCategories = categories;
loading = false;
notifyListeners();
} catch (e) {
throw Exception();
}
}
}
3 - Create an instance of CategoriesProvider and add it to the widget tree near to the desired carrossel widget, you can read more about where to insert the provider on this link https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => CategoriesProvider (),
child: const MyApp(),
),
);
}
4 - In the stateful widget you need to create a instance of the CategoriesProvider to retrieve the data from the api.
class _MyApp extends State<MyApp> {
late CategoriesProvider categoriesProvider;
#override
void initState() {
categoriesProvider = Provider.of(context, listen: false);
categoriesProvider.getCategories();
super.initState();
}
5 - On the screen widget you can wrap your widget with a Consumer widget to retrieve the data from the provider, replacing on your example the this.foodOptions with the categories defined early on the provider class.
Consumer<CategoriesProvider>(
builder: (context, provider, _) => Container(
height: 105,
margin: const EdgeInsets.only(
top: 20.0,
bottom: 25.0,
),
child: ListView.builder(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.only(
left: 20.0,
),
itemCount: provider.foodCategories.length,
itemBuilder: (context, index) {
Map<String, String> option = provider.foodCategories[index];
return Container(
margin: const EdgeInsets.only(right: 35.0),
child: Column(
children: <Widget>[
Container(
width: 70,
height: 70,
margin: const EdgeInsets.only(bottom: 10.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(5.0),
),
image: DecorationImage(
image: AssetImage(
option['image'],
),
),
boxShadow: [
BoxShadow(
blurRadius: 10.0,
color: Colors.grey[300],
offset: Offset(6.0, 6.0),
)
],
),
),
Text(
option['name'],
style: TextStyle(fontSize: 17.0),
),
],
),
);
}),
),
),

I am having trouble iterating through an array of objects

I am trying to go through an array of objects, I stored them in my SharedPreferences where I go the data from firebase and add the quantity for each object, now I only want to display the title, price, and quantity of the product in the cart. I was able to pull all the values belonging to the product to the cart screen but how to loop through the nested values in the cart screen is the problem. please can anyone help me still learning more on flutter?
Cart screen
#override
Widget build(BuildContext context) {
SharedPreferences prefs = SharedPreferences.getInstance() as SharedPreferences;
var cart = prefs.getStringList('userCart');
return Row(
children: [
SizedBox(
width: getProportionateScreenWidth(88),
child: AspectRatio(
aspectRatio: 0.88,
child: Container(
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
color: Color(0XFFF5F6F9),
borderRadius: BorderRadius.circular(15),
),
child: Image.network(cart![0]),
// child: Image.network(cart.product.images[0]),
),
),
),
SizedBox(
width: getProportionateScreenWidth(20),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
cart.first,
// cart.product.title,
style: TextStyle(fontSize: 16, color: Colors.black),
maxLines: 2,
),
const SizedBox(
height: 10,
),
Text.rich(
TextSpan(
text: "\$${cart.product.price}",
style: TextStyle(
color: kPrimaryColor,
),
children: [
TextSpan(
text: " x${cart.numOfItem}",
style: TextStyle(
color: kTextColor,
),
),
],
),
),
],
)
],
);
}
Storing the data from firebase and adding quantity
Future<void> checkItemInCart(
Product product, int quantity, BuildContext context) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
// convert to map
var product_str = product.toMap();
// combine product with quantity
String productWithQuantity =
product_str.toString() + '-quantity-' + quantity.toString();
// check if project exists first
List<String> userCartPref = (prefs.getStringList('userCart') ?? <String>[]);
['Product-quantity-2'];
/*
update {
check if found
}
*/
List<String> temp = (prefs.getStringList('userCart') ?? <String>[]);
// add f
//userCartPref ['iphone 1','laptop 3'];
// temp ['laptop 3'];
var foundInCart = false;
for (var i = 0; i < userCartPref.length; i++) {
var item = userCartPref[i];
var items = item.split('-quantity-'); //list [product,quantity]
var old_product = items[0];
var old_qty = items[1];
if (old_product.contains(product.pid)) {
foundInCart = true;
// product exists
// delete the current item
temp.removeAt(i);
// set pref to temp
prefs.setStringList('userCart', temp);
// sum the quantity 2 1
String finalQuantity = (quantity + int.parse(old_qty)).toString();
// create string for pref with the updated quantity
String updatedProductWithQuantity =
product_str.toString() + '-quantity-' + finalQuantity;
//add item with the updated quantity iphone 2
addItemToCart(updatedProductWithQuantity, context);
showSnackBar(context, "Quantity has been updated successfully");
break;
}
}
if (userCartPref.length == 0 || foundInCart == false) {
addItemToCart(productWithQuantity, context);
showSnackBar(context, "Product added successfully to cart");
}
await getProducts();
}
Future<void> addItemToCart(String product, BuildContext context) async {
// await clearPref();
print("inside");
SharedPreferences prefs = await SharedPreferences.getInstance();
List<String> tempCartList = (prefs.getStringList('userCart') ?? <String>[]);
// print(tempCartList);
tempCartList.add(product);
prefs.setStringList('userCart', tempCartList);
}
Future<void> getProducts() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
List<String> tempCartList =
(preferences.getStringList('userCart') ?? <String>[]);
for (var i = 0; i < tempCartList.length; i++) {
var item = tempCartList[i];
var items = item.split('-quantity-');
var product_ = items[0];
var quantity_ = items[1];
}
}
you can use ListView.builder or GridView.builder to iterate over the array and render them on screen. So if I use ListView.builder, my code in cart screen would look like:
return ListView.builder(
itemCount: cart.length, //length of cart
itemBuilder: (context, index) {
return Row(
children: [
SizedBox(
width: getProportionateScreenWidth(88),
child: AspectRatio(
aspectRatio: 0.88,
child: Container(
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
color: Color(0XFFF5F6F9),
borderRadius: BorderRadius.circular(15),
),
child: Image.network(cart![index]),
// child: Image.network(cart.product.images[0]),
),
),
),
SizedBox(
width: getProportionateScreenWidth(20),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
cart[index].product.title,
// cart.product.title,
style: TextStyle(fontSize: 16, color: Colors.black),
maxLines: 2,
),
const SizedBox(
height: 10,
),
Text.rich(
TextSpan(
text: "\$${cart[index].product.price}",
style: TextStyle(
color: kPrimaryColor,
),
children: [
TextSpan(
text: " x${cart[index].numOfItem}",
style: TextStyle(
color: kTextColor,
),
),
],
),
),
],
)
],
);
},
);