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

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;
});
}

Related

Flutter: _TypeError

I'm trying to get datas from api and add them a list. But at this moment, I see datas i got but I can't get it out of the function. What should i do?
function
List<dynamic> xxx = [];
#override
void initState() {
super.initState();
Future<List<dynamic>> fetchCompanies(List<dynamic> datas) async {
var response = await Dio().get(CompaniesPath().url);
if (response.statusCode == HttpStatus.ok) {
Map<String, dynamic> company = jsonDecode(response.data);
for (int i = 0; i < company['Data'].length; i++) {
datas.add(company['Data'][i]);
}
//print(datas); //=> I see datas here
} else {
throw Exception();
}
return datas;
}
print(fetchCompanies(xxx));
}
When I run print(fetchCompanies(xxx)); I got "Instance of 'Future<List<dynamic>>'". How can i get data inside fetchCompanies to my xxx list?
You're trying to print future instance of List that's why you got
Instance of Future<List>
You have to wait until function finish executing.
Catch here is you can't call wait in initState() so you have to use .then method
try this:
fetchCompanies(xxx)
.then((result) {
print("result: $result");
});
It should already work fine like it is. But you probably want to call a setState to refresh the page. Try this:
#override
void initState() {
super.initState();
Future<List<dynamic>> fetchCompanies(List<dynamic> datas) async {
var response = await Dio().get(CompaniesPath().url);
if (response.statusCode == HttpStatus.ok) {
Map<String, dynamic> company = jsonDecode(response.data);
for (int i = 0; i < company['Data'].length; i++) {
datas.add(company['Data'][i]);
}
//print(datas); //=> I see datas here
setState(() {}); // added this
} else {
throw Exception();
}
return datas;
}
print(fetchCompanies(xxx));
}

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 Firestore Query snapshot- result is always null

I have a simple flutter code to retrieve some data from Firestore. the data is retireved correctly, however passing the data from the future function making the result always null. can you advise how to adapt the code to return the list?
that is the class where the actual query is happening:
class DatabaseManager {
final CollectionReference BusinessProfilesCollection =
FirebaseFirestore.instance.collection("BusinessProfilesCollection");
Future GetBusinessProfilesCollection() async {
List businessprofileslist = [];
try {
await BusinessProfilesCollection.get().then((QuerySnapshot) {
QuerySnapshot.docs.forEach((element) {
businessprofileslist.add(element.data());
print(businessprofileslist[0]);
});
});
} catch (e) {
print(e.toString());
return null;
}
}
}
here is the page where I am calling the function: (however the result is always null)
class _ProfilesListPageState extends State<ProfilesListPage> {
List businessprofileslist = [];
#override
void initState() {
super.initState();
fetchBusinessProfilesList();
}
fetchBusinessProfilesList() async {
dynamic result = await DatabaseManager().GetBusinessProfilesCollection();
print(result.toString());
if (result == null) {
print('enable to retieve');
} else {
print('success');
setState(() {
businessprofileslist = result;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold();
}
}
You're not returning anything from GetBusinessProfilesCollection but null, so the result seems somewhat expected.
I guess you want to do:
class DatabaseManager {
final CollectionReference BusinessProfilesCollection =
FirebaseFirestore.instance.collection("BusinessProfilesCollection");
Future GetBusinessProfilesCollection() async {
List businessprofileslist = [];
try {
var QuerySnapshot = await BusinessProfilesCollection.get();
querySnapshot.docs.forEach((element) {
businessprofileslist.add(element.data());
});
return businessprofileslist;
} catch (e) {
print(e.toString());
return null;
}
}
}
Btw: returning null when the load fails, is just going to lead to a null pointer exception when you then do print(result.toString());. So I recommend not catching the error and just letting it bubble up. With that your code can be simplified to:
class DatabaseManager {
final CollectionReference BusinessProfilesCollection =
FirebaseFirestore.instance.collection("BusinessProfilesCollection");
Future GetBusinessProfilesCollection() async {
var QuerySnapshot = await BusinessProfilesCollection.get();
return querySnapshot.docs.map((element) => element.data());
}
}
You just need to return the list
return businessprofileslist;
CODE :
class DatabaseManager {
final CollectionReference BusinessProfilesCollection =
FirebaseFirestore.instance.collection("BusinessProfilesCollection");
Future GetBusinessProfilesCollection() async {
List businessprofileslist = [];
try {
await BusinessProfilesCollection.get().then((QuerySnapshot) {
QuerySnapshot.docs.forEach((element) {
businessprofileslist.add(element.data());
print(businessprofileslist[0]);
});
// you just need to return the list here after filling it up
return businessprofileslist;
});
} catch (e) {
print(e.toString());
return null;
}
}
}
Code with a little improvement:
class DatabaseManager {
final CollectionReference BusinessProfilesCollection =
FirebaseFirestore.instance.collection("BusinessProfilesCollection");
Future GetBusinessProfilesCollection() async {
await BusinessProfilesCollection.get().then((QuerySnapshot) {
QuerySnapshot.docs.map((doc) => doc.data()).toList();
});
}
}
Try that with calling the function in feching
fetchBusinessProfilesList()
async {
dynamic result ;
await DatabaseManager().GetBusinessProfilesCollection().then((value){
result=value;
print(result.toString());
if (result == null) {
print('enable to retieve');
} else {
print('success');
setState(() {
businessprofileslist = result;
});
}
});
}

Flutter in_app_purchase, show content after purchase

I take code from offical in_app_purchases documentation, purchases work correctly.
I have function that show paid content, and i need to run it after purchase done correctly, but i don't know where i need to put it, because i steel don't inderstand purchases 100% correctly.
this is my code
final InAppPurchase _inAppPurchase = InAppPurchase.instance;
final String _productID = '1d7ea644f690ffa';
bool _available = true;
List<ProductDetails> _products = [];
List<PurchaseDetails> _purchases = [];
StreamSubscription<List<PurchaseDetails>>? _subscription;
#override
void initState() {
final Stream<List<PurchaseDetails>> purchaseUpdated = _inAppPurchase.purchaseStream;
_subscription = purchaseUpdated.listen((purchaseDetailsList) {
setState(() {
_purchases.addAll(purchaseDetailsList);
_listenToPurchaseUpdated(purchaseDetailsList);
});
}, onDone: () {
_subscription!.cancel();
}, onError: (error) {
_subscription!.cancel();
});
_initialize();
super.initState();
}
#override
void dispose() {
_subscription!.cancel();
super.dispose();
}
void _initialize() async {
_available = await _inAppPurchase.isAvailable();
List<ProductDetails> products = await _getProducts(
productIds: Set<String>.from(
[_productID],
),
);
setState(() {
_products = products;
});
}
void _listenToPurchaseUpdated(List<PurchaseDetails> purchaseDetailsList) {
purchaseDetailsList.forEach((PurchaseDetails purchaseDetails) async {
switch (purchaseDetails.status) {
case PurchaseStatus.pending:
// _showPendingUI();
break;
case PurchaseStatus.purchased:
break;
case PurchaseStatus.restored:
// bool valid = await _verifyPurchase(purchaseDetails);
// if (!valid) {
// _handleInvalidPurchase(purchaseDetails);
// }
break;
case PurchaseStatus.error:
print(purchaseDetails.error!);
// _handleError(purchaseDetails.error!);
break;
default:
break;
}
if (purchaseDetails.pendingCompletePurchase) {
await _inAppPurchase.completePurchase(purchaseDetails);
CheckListModel.addPaidData(purchaseDetails.productID);
}
});
}
Future<List<ProductDetails>> _getProducts({required Set<String> productIds}) async {
ProductDetailsResponse response = await _inAppPurchase.queryProductDetails(productIds);
return response.productDetails;
}
void _subscribe({required ProductDetails product}) {
final PurchaseParam purchaseParam = PurchaseParam(productDetails: product);
_inAppPurchase.buyNonConsumable(
purchaseParam: purchaseParam,
);
}
_subscribe function start when user click on special button in ui
function that show paid contenct name is
CheckListModel.addPaidData(purchaseDetails.productID);
when function start it create file paid.paid in getApplicationDocumentsDirectory, if it doesn't exist, and add in it productID . That what must happen
Where i need to place this function?

How to update a list in initState() flutter after the api call?

So I have a Stateful Widget which has a List variable I want to update from the API call. My issue is that the List is empty even after I do the fetchItems() in the initState().
How can I then update the itemsList with the content of the fetchItems function?
Isnt the function suppose to update itemsList if I use setState().
class _ItemsWidgetState extends State<ItemsWidget> {
List<ItemsModel> itemsList = [];
void initState(){
fetchItems();
}
fetchItems() async {
final response = await http.get(url);
if (response.statusCode == 200) {
final fetchedItems = json.decode(response.body);
for (var item in fetchedItems) {
ItemsModel item = ItemsModel.fromJson(item);
setState(() {
itemsList.add(item);
});
}
} else {
throw Exception('Failed to load items');
}
}
Avoid calling setState inside loops, call it after your task has done.
Always call super.initState and mark initState as overrided
class _ItemsWidgetState extends State<ItemsWidget> {
List<ItemsModel> itemsList = [];
#override
void initState(){
super.initState();
fetchItems();
}
fetchItems() async {
final response = await http.get(url);
if (response.statusCode == 200) {
final fetchedItems = json.decode(response.body);
for (var item in fetchedItems) {
ItemsModel item = ItemsModel.fromJson(item);
/// Remove from setState
itemsList.add(item);
}
/// Tells to Flutter that now something has changed
setState(() {});
} else {
throw Exception('Failed to load items');
}
}
First check fetchedItems is a list type.
class _ItemsWidgetState extends State<ItemsWidget> {
List<ItemsModel> itemsList = [];
void initState(){
fetchItems();
}
fetchItems() async {
final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
final fetchedItems = jsonDecode(response.body);
for (var item in fetchedItems) {
ItemsModel item = ItemsModel.fromJson(item);
setState(() {
itemsList.add(item);
});
}
} else {
throw Exception('Failed to load items');
}
}