Data is showing only after hot reload - flutter

I'm new to flutter and amplify. I'm trying to do an app using flutter and amplify as backend. I want to retrieve data from three datastore table at once and put them in to a List<Map<String, dynamic>>. I can read data from the List but the data display only when I press refresh or hot reload. I think maybe the mistake is the way I put the setstate? Could anyone tell me what I did wrong? Any help would be much appreciated!
Here is my code
late StreamSubscription _subscription;
List<Map<String, dynamic>> _orderList = [];
#override
void initState() {
super.initState();
_subscription =
Amplify.DataStore.observe(OrderItem.classType).listen((event) {
_fetchOrder();
});
_orderList = [];
_fetchOrder();
}
Future _fetchOrder() async {
List<Map<String, dynamic>> fullOrderItemList = [];
// fetch all order by user id
final user = await Amplify.Auth.getCurrentUser();
List<Order> orderDb = await Amplify.DataStore.query(Order.classType,
where: Order.USERID.eq(user.userId));
// fetch order item and user name by order id & user id
orderDb.asMap().forEach((index, order) async {
List<Map<String, String>> orderItemByOrderId = [];
User buyerInfo = (await Amplify.DataStore.query(User.classType,
where: User.ID.eq(order.userID)))[0];
List<OrderItem> orderitem = await Amplify.DataStore.query(
OrderItem.classType,
where: OrderItem.ORDERID.eq(order.id));
orderitem.asMap().forEach((index, orderitem) {
orderItemByOrderId.add({
"order item id": orderitem.id,
"item name": orderitem.item,
"price": orderitem.price.toString(),
"quantity": orderitem.quantity.toString(),
});
});
fullOrderItemList.add({
'order id': order.id,
'order date': order.orderDate,
'total price': order.totalPrice.toString(),
'buyer name': buyerInfo.name,
'buyer phone': buyerInfo.phone,
'buyer email': buyerInfo.email,
'order item': orderItemByOrderId,
});
});
setState(() {
_orderList = fullOrderItemList;
});
}

You should use await with _fetchOrder().
One way to get around this would be to make _fetchOrder() return the fullOrderItemList and use setState inside Future.then:
#override
void initState() {
super.initState();
_subscription =
Amplify.DataStore.observe(OrderItem.classType).listen((event) {
_fetchOrder().then((orderList) => setState(() {
_orderList = orderList;
});
});
_orderList = [];
_fetchOrder().then((orderList) => setState(() {
_orderList = orderList;
});
}
If this doesn't work, you can try this:
_fetchOrder().then((orderList) {
WidgetsBinding.instance!.addPostFrameCallback((_) => setState(() {
_orderList = orderList;
})}
);

Related

How to get value from Future in flutter

I'm trying to make a currency converter app in flutter.
The process I've planned was..
At the initState, get current currency data from API
Get currency data and assign it to 'currentCur'
Calculate converted currency value with 'currentCur' and display the value
But, I got an error that since 'currentCur' is Instance of 'Future<dynamic' so it can't calculated cause it is not subtype of 'num'
How can I get just value from Future in initState?
class _ConverterWidgetState extends State<ConverterWidget> {
late final TextEditingController _current;
late final currentCur;
late final currency;
fetchData() async {
try {
http.Response response = await http.get(
Uri.parse(
'https://quotation-api-cdn.dunamu.com/v1/forex/recent?codes=FRX.KRWUSD',
),
);
String jsonData = response.body;
var basePrice = jsonDecode(jsonData)[0]['basePrice'];
devtools.log(basePrice.toString());
return basePrice;
} catch (e) {
devtools.log(e.toString());
}
}
getCurrency(a) async {
return await Future.value(a);
}
#override
void initState() {
super.initState();
_current = TextEditingController(text: 1000.toString());
currentCur = fetchData();
devtools.log(currentCur.toString());
}
Specify the function is going to return a value with the "Future" keyWord
Future<num> fetchData() async {
var basePrice = 0;
try {
http.Response response = await http.get(
Uri.parse(
'https://quotation-api-cdn.dunamu.com/v1/forex/recent?codes=FRX.KRWUSD',
),
);
String jsonData = response.body;
basePrice = jsonDecode(jsonData)[0]['basePrice'];
devtools.log(basePrice.toString());
} catch (e) {
devtools.log(e.toString());
}
return basePrice;
}
void updateCurrentCur ()async{
var basePrice = await fetchData();
setState(() {
currentCur = basePrice;
});
}
#override
void initState() {
super.initState();
updateCurrentCur()
}

Why is ChangeNotifier updating endlessly

I have 2 data provider classes that extend ChangeNotifier. Within each, there's a function to fetch data and at the end of them, I use notifyListeners() to notify the screens/listeners that the data changed. However, it seems that the listeners start getting notified endlessly instead of once and that creates a loop of reloading, circle indicators that don't go away, and a frozen screen. I don't get it.
Data providers:
class UsersDataProvider extends ChangeNotifier {
UsersDataProvider() : super();
static Map<int, QueryDocumentSnapshot<Object?>> usersMap = {};
Future<void> fetchUsers() async {
final userRef = FirebaseFirestore.instance.collection('users');
final QuerySnapshot result = await userRef.get();
final docs = result.docs.asMap();
usersMap = docs;
print(usersMap.length);
notifyListeners();
}
}
class PostsDataProvider extends ChangeNotifier {
PostsDataProvider() : super();
static Map<int, QueryDocumentSnapshot<Object?>> postsMap = {};
Future<void> fetchPosts() async {
UsersDataProvider.usersMap.forEach((index, resultValue) async {
final postsRef = FirebaseFirestore.instance
.collection('users')
.doc(resultValue.id)
.collection('posts');
final QuerySnapshot postsResult = await postsRef.get();
final postDocs = postsResult.docs.asMap();
postsMap = postDocs;
print('Post map: ${postsMap.length}');
notifyListeners();
});
}
}
Add listeners and reload data:
Future<void> fetchUsersAndPosts(bool initial) async {
if (!initial) {
setState(() {
postsLoading = true;
});
usersDataProvider.fetchUsers();
postsDataProvider.fetchPosts();
}
if (initial) {
usersDataProvider.addListener(() {
print('changed');
setState(() {
fetchUsersAndPosts(false);
});
});
}
if (initial) {
postsDataProvider.addListener(() {
setState(() {
fetchUsersAndPosts(false);
});
});
}
UsersDataProvider.usersMap.forEach((index, value) async {
List<Post> posts = [];
PostsDataProvider.postsMap.forEach((index, value) {
final post = Post.fromJson(value.data() as Map<String, dynamic>);
posts.add(post);
setState(() {});
if (posts.length == PostsDataProvider.postsMap.length) {
setState(() {
postsList = posts;
postsList.sort((a, b) {
return b.date.compareTo(a.date);
});
postsLoading = false;
});
}
});
final profileInfo =
ProfileInfoObject.fromJson(value.data() as Map<String, dynamic>);
Profile profile = Profile(profileInfo, postsList.where((p) => p.uid == value.id).toList());
UserSearchResult user = (UserSearchResult(profile, value.id));
if (usersList.where((u) => u.uid == user.uid).toList().isEmpty) {
setState(() {
usersList.add(user);
});
}
});
setState(() {
postsList.sort((a, b) {
return b.date.compareTo(a.date);
});
});
}

flutter sqflite+getx use group_button to filter the array from database and render GridView.builder

I am using flutter desktop development. Use the group button filter to read data from the database and render it to GridView.builder().
At present, I can't realize the filtering function when I read the data sheet from the database[enter image description here][1]
I am using getx
final selected = 'table_operation'.obs;
final items = ['table_operation', 'merge_table', 'turn_table'];
final SqliteHelper _sqliteHelper = SqliteHelper();
final areas = [].obs;
final selectedAreaType = 0.obs;
final tables = [].obs;
// final filterTables = [].obs;
var areaModel;
final areaId = 0.obs;
void setSelected(value) {
selected.value = value;
}
void setSelectArea(int value) {
selectedAreaType.value = value;
areaModel = areas[selectedAreaType.value];
areaId.value = areaModel.id;
}
#override
onInit() {
getAreaList();
getTableList();
super.onInit();
}
Future<List<AreaModel>> getAreaList() async {
List<Map<String, dynamic>> results = await _sqliteHelper.queryObject('area');
List<AreaModel> areaList = [];
for (var r in results) {
if (r.isNotEmpty) {
areaList.add(AreaModel.fromMap(r));
}
}
areas.value = areaList.map((e) => e).toList();
return areaList;
}
Future<List<TableModel>> getTableList() async {
List<Map<String, dynamic>> results = await _sqliteHelper.queryObject('tables');
List<TableModel> tableList = [];
for (var t in results) {
tableList.add(TableModel.fromMap(t));
}
tables.value = tableList.map((e) => e).toList();
return tableList;
}
[1]: https://i.stack.imgur.com/fOGwg.png

Provider keeps on executing and data gets added to the list infinite in flutter

I am using Provider to manage the app state in flutter and use provider to call api and store it in model class and provider is called inside build method, its an inventory app.
when cart page is opened, provider to fetch cart items is called and cart items will be displayed, but the issue is the api to fetch cart items keeps on executing infinitly, in ui data gets replicated. Can anyone please help with this issue.
the code to call cart provider is
#override
Widget build(BuildContext context)
{
final FirebaseAuth _auth = FirebaseAuth.instance;
User? user = _auth.currentUser;
var phoneNumber = user!.phoneNumber;
final cartProvider = Provider.of<CartProvider>(context);
cartProvider.fetchCartByUser(user.phoneNumber.toString());
final testString = ModalRoute.of(context)!.settings.arguments as String;
return
isEmpty
? Scaffold(body: CartEmpty())
:
Scaffold
( //display cart items)
cart provider class is
class CartProvider with ChangeNotifier {
List<CartModel> _cartItems = [];
List<CartModel> get cartItems {
return [..._cartItems];
}
CollectionReference cart = FirebaseFirestore.instance.collection('cart');
Future<void> fetchCartByUser(String phoneNumber) async
{
await Server.getCartData().then((value)
{
for(var item in value)
{
_cartItems.insert
(
0,
CartModel
(
cartId: item['cartId'],
clientAddress: item['clientAddress'],
clientFullname: item['clientFullname'],
clientPhoneNumber: item['clientPhoneNumber'],
createdAt: item['createdAt'],
merchant: item['merchant'],
merchantCode: item['merchantCode'],
price: item['price'],
productCode: item['productCode'],
productImage: item['productImage'],
productName: item['productName'],
productQty: item['productQty'],
productSKU: item['productSKU'],
),
);
}
});
notifyListeners();
}
Future<void> deleteSingleCart(String cartId) async {
await cart
.doc(cartId)
.delete()
.then((value) => print("Cart Deleted"))
.catchError((error) => print("Failed to delete user: $error"));
// this.fetchCartByUser();
notifyListeners();
}
Future<void> updateProductQty(String cartId, String qty) async {
await cart.doc(cartId).update({'productQty': qty});
}
double get totalAmount {
var total = 0.0;
for (var i = 0; i < _cartItems.length; i++) {
total +=
int.parse(_cartItems[i].price) * int.parse(cartItems[i].productQty);
}
return total;
}
}
#override
void initState() {
super.initState();
WidgetsBinding.instance!.addPostFrameCallback((_) {
// Here you can fetch data login
});
}

Flutter flutter_in_app_purchases subscription FlutterInAppPurchses.instance.getSubscriptions() is not retrieving any items for IAPItem

I'm trying to implement a renewable subscription in flutter using the flutter_in_app_purchases plugin. When I click on the screen that this is declared in, it goes through the initState() function and then gets to the initPlatformState() and goes through that successfully, but when it gets to the getProducts() function, it's returning an empty item list for the List items = FlutterInappPurchase.instance.getSubscriptions([productID]); call. I've added the monthly subscription in both the App Store Connect and Google Play Store and completed the tax forms. Any help would be appreciated.
List<IAPItem> _items = [];
static const String productID = 'monthly_subscription';
#override
void initState() {
super.initState();
print("IN INIT STATE");
initPlatformState();
}
Future<void> initPlatformState() async {
print("In init platform state");
// prepare
final bool available = await InAppPurchaseConnection.instance.isAvailable();
print(available);
var close = await FlutterInappPurchase.instance.endConnection;
var result = await FlutterInappPurchase.instance.initConnection;
print('result: $result');
// 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) {
print('In not mounded');
return;
}
// refresh items for android
/*try {
String msg = await FlutterInappPurchase.instance.consumeAllItems;
print('consumeAllItems: $msg');
} catch(e){
print(e.toString());
}*/
await _getProduct();
}
Future<Null> _getProduct() async {
print("In get products");
try {
List<IAPItem> items = await FlutterInappPurchase.instance.getSubscriptions([productID]);
print("Items is: $items");
for (var item in items) {
print('${item.toString()}');
this._items.add(item);
}
setState(() {
this._items = items;
});
} catch(e) {
print(e.toString());
}
}
Here you have a working example from app in production. Disclaimer: I'm not using it anymore but the last time I did it worked fine:
class _InAppState extends State<InApp> {
StreamSubscription _purchaseUpdatedSubscription;
StreamSubscription _purchaseErrorSubscription;
StreamSubscription _conectionSubscription;
final List<String> _productLists = Platform.isAndroid
? [
'subs_premium', 'subs_user'
]
: ['subs_premium', 'subs_boss', 'subscripcion_user'];
String _platformVersion = 'Unknown';
List<IAPItem> _items = [];
List<IAPItem> _subscripions = [];
List<PurchasedItem> _purchases = [];
#override
void initState() {
super.initState();
initPlatformState();
}
#override
void dispose() {
super.dispose();
if (_conectionSubscription != null) {
_conectionSubscription.cancel();
_conectionSubscription = null;
}
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
String platformVersion;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
platformVersion = await FlutterInappPurchase.instance.platformVersion;
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
// prepare
var result = await FlutterInappPurchase.instance.initConnection;
print('result: $result');
// 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(() {
_platformVersion = platformVersion;
});
// refresh items for android
try {
String msg = await FlutterInappPurchase.instance.consumeAllItems;
print('consumeAllItems: $msg');
} catch (err) {
print('consumeAllItems error: $err');
}
_conectionSubscription = FlutterInappPurchase.connectionUpdated.listen((connected) {
print('connected: $connected');
});
_purchaseUpdatedSubscription = FlutterInappPurchase.purchaseUpdated.listen((productItem) {
print('purchase-updated: $productItem');
});
_purchaseErrorSubscription = FlutterInappPurchase.purchaseError.listen((purchaseError) {
print('purchase-error: $purchaseError');
});
final List<String> _SKUS = widget.premium ? ['subs_boss']
: ['subs_user'] ;
_getSubscriptions(_SKUS);
}
void _requestPurchase(IAPItem item) {
FlutterInappPurchase.instance.requestPurchase(item.productId);
}
Future _getProduct() async {
print('TEST 1 HERE ${_productLists.length}, ${_productLists.first.toString()}');
List<IAPItem> items = await FlutterInappPurchase.instance.getProducts(_productLists);
print('TEST 2 HERE ${items.length}');
for (var item in items) {
print('${item.toString()}');
this._items.add(item);
}
setState(() {
this._items = items;
this._purchases = [];
});
}
Future _getPurchases() async {
List<PurchasedItem> items =
await FlutterInappPurchase.instance.getAvailablePurchases();
for (var item in items) {
print('${item.toString()}');
this._purchases.add(item);
}
setState(() {
this._items = [];
this._purchases = items;
});
}
Future _getSubscriptions(_SKUS) async {
List<IAPItem> items =
await FlutterInappPurchase.instance.getSubscriptions(_SKUS);
for (var item in items) {
print('${item.toString()}');
this._subscripions.add(item);
}
setState(() {
this._items = [];
this._subscripions = items;
});
}
Future _getPurchaseHistory() async {
List<PurchasedItem> items = await FlutterInappPurchase.instance.getPurchaseHistory();
for (var item in items) {
print('${item.toString()}');
this._purchases.add(item);
}
setState(() {
this._items = [];
this._purchases = items;
});
}