How to perform Firebase Mutable Transaction Data with Flutter? - firebase

I have Swift code where I reduce or increase an int value for certain conditions. How can I replicate this with Flutter? Here is my Swift code..
// 2. Decrease Value -= from NumberOnes
let decreaseRef = self.ref.child("NumberOnes/\(myfav1)/Value")
decreaseRef.runTransactionBlock { (currentData: FIRMutableData) -> FIRTransactionResult in
if var data = currentData.value as? Int
{
var count = data
count -= 1
data = count
currentData.value = data
return FIRTransactionResult.success(withValue: currentData)
}
return FIRTransactionResult.success(withValue: currentData)
}
Also, if there's documentation/tutorials for this out there, please point me to it.
* UPDATE *
Here is my Flutter code...
fb.child('UserVideo/${userid}/Vid1').onValue.listen((Event event){
if (event.snapshot != null){
final vidID = event.snapshot.value["videoID"];
fb.child('NumberOnes/${vidID}/Value').onValue.listen((Event newEvent){
int vidValue = newEvent.snapshot.value;
vidValue;
print(vidValue);
//vidValue = value;
final value = vidValue--;
fb.child('NumberOnes/${vidID}').update({
'Value': value,
});
});
The problem with my Flutter code is that it doesn't stop decreasing. Is there a way around this?

Here is my solution. I worked it out from this answer here... Flutter Firebase update will not stop updating node?
Basically, I'm isolating the value once, manipulating it, then updating node with new info. I think this is much less efficient than the runTransactionBlock from the Firebase Swift SDK that brings back snapshot value as MutableData. If anyone finds a work around for this in the future please add answer.
if (vidRank == 1) {
var event = await fb.child('UserVideo/${userid}/Vid1').once();
if (event.snapshot != null){
var vid1id = event.snapshot.value['videoID'].toString();
var onesEvent = await fb.child('NumberOnes/${vid1id}').once();
if (onesEvent.snapshot != null){
var onesValue = (onesEvent.snapshot.value['Value'] as int);
final vidValue = onesValue - 1;
print("Inside ${vidValue}");
fb.child('NumberOnes/${vid1id}').update({
'Value': vidValue
});
}
}
}

Related

How to use runTransaction with firebase_database 9.0.3

Can anyone give where to look, or what I am missing, for how to use runTransaction in 9.0.3? (Flutter, Firebase realtime DB)
I updated firebase_database ^7.0.0 to ^9.0.3. Had to change runTransaction(MutableData mutable) to runTransaction(Object object), and the Object is null for Map data. Does return a String when point to single node of a String.
For something to work I placed + ‘/sap’ at end of .ref(…), and it gave ‘DROID’
Code Updated for Comment below:
bool bCanWeStartGame = false;
bool bIfiOS = false;
bIfiOS = Theme.of(referencedData.mngGamesPageMaterialContext)
.platform == TargetPlatform.iOS;
DatabaseReference playersRef = referencedData.database.ref(
TABLENAME_STARTS + '/' + referencedData.strFBGameUniqueIDKey);
if(bIfiOS){
//apparently iPhone needs to 'open line to DB record' to work
playersRef.once()
.then((DatabaseEvent event) {//dont need to do anything??
});
}
TransactionResult txResult;
//while loop to repeat runTransaction until solved
int iWhileLoopCheck = 0; //so whileLoop does not go forever
bool bTransactionCommittedOrGetOutAnyWay = false;
while(!bTransactionCommittedOrGetOutAnyWay
&& (iWhileLoopCheck<30)){
iWhileLoopCheck++;
txResult = await playersRef
.runTransaction(
(Object post) {
if (post == null) {
if(bIfiOS){sleep(Duration(milliseconds: 3));}
return Transaction.abort(); //out, but will retry transaction
}
Map<String, dynamic> _post = Map<String, dynamic>.from(post as Map);
bTransactionCommittedOrGetOutAnyWay = true; //whichever of
//options below take effect, dont want to continue while loop
if (_post[STARTS_TABLE_ALL_PLAYERS] !=
startingGames[iIndexInStartingGames].allplayers) {
//someone has joined since trying to start
bCanWeStartGame = false;
//Transaction.abort() as dont want to change data
return Transaction.abort();
} else {
//Nobody has joined, so can Start. Tell others Game is starting
_post['stg'] = true;
bCanWeStartGame = true;
return Transaction.success(_post);
}
});
} //while runtransaction doesnot get at data```

Flutter firestore pagination doesn't start after a document,

I am trying to implement a pagination feature on my list and I've been following this official documentation https://firebase.google.com/docs/firestore/query-data/query-cursors
Here is a main code:
Future<List<Review>> fetchReviewPaginated(int limit) async {
var ref = _firestore.collection('reviews').orderBy('creationDate', descending: true);
if (_isFetchingUser) return null;
_isFetchingUser = true;
if (cachedReview == null) {
var snapshot = await ref.limit(limit).get();
lastPage = snapshot.docs.last;
cachedReview = snapshot.docs.map((e) => Review.fromMap(e.data())).toList();
} else {
var snapshot = await ref.startAfter(lastPage["creationDate"]).limit(limit).get();
lastPage = snapshot.docs.last;
cachedReview.addAll(snapshot.docs.map((e) => Review.fromMap(e.data())).toList());
}
if (cachedReview.length < limit) hasNext = false;
_isFetchingUser = false;
notifyListeners();
return cachedReview;
}
My widget will call fetchReviewPaginated(5) and display what is in cachedReviews. If the cachedReviews is null, then it will fetch 5 of reviews, then it will startAfter what is in the last of the cachedReviews and get another 5 documents after that point.
Problem is that this function always returns the same 5 items and doesn't truly start after what I specify,
I cannot test/try your code at this moment but I think this is because startAfter(lastPage["creationDate"]) is not correct.
You need to pass a list of values to startAfter() (a list with a unique item in your case). Since lastPage is a DocumentSnapshot you therefore need to use get() or data(), like startAfter([lastPage.get("creationDate")]).
Note that you could instead use the startAfterDocument() method to which you pass the DocumentSnapshot itself. It is somehow more adapted to your case, since you have only one sorting.

How to get data from Firebase Realtime Database and put it into a list using Flutter?

I'm new in mobile development and flutter, and I was trying to get data from firebase realtime database using flutter to use it in a list.
Here's the database that i'm trying to retrieve data from.
I wanna get all "timespent" putted into the database, in one list then return a string showing the total time (which is the sum of all "timespent" data).
String sumTimers() {
String sumstring;
int sumseconds = 0;
List<String> hms;
List<String> list;
List<int> listint;
FirebaseDatabase.instance
.reference()
.child("Timers")
.once()
.then((DataSnapshot snapshot) {
Map<dynamic, dynamic> values = snapshot.value;
values.forEach((key, values) {
list.add(values["timespent"]);
});
});
int c = 0;
for (var x in list) {
hms = x.split(":");
listint[c] = int.parse(hms[0]) * 60 * 60 +
int.parse(hms[1]) * 60 +
int.parse(hms[2]);
c = c + 1;
sumseconds = sumseconds + listint[c];
}
sumstring = secondstoString(sumseconds);
return sumstring;}
String secondstoString(int tseconds) {
String timoo;
if (tseconds < 60) {
timoo = "00:00:" + tseconds.toString();
tseconds = tseconds - 1;
} else if (tseconds < 3600) {
int m = tseconds ~/ 60;
int s = tseconds - (60 * m);
timoo = "00:" + m.toString() + ":" + s.toString();
tseconds = tseconds - 1;
} else {
int h = tseconds ~/ 3600;
int t = tseconds - (3600 * h);
int m = t ~/ 60;
int s = t - (60 * m);
timoo = h.toString() + ":" + m.toString() + ":" + s.toString();
tseconds = tseconds - 1;
}
return timoo;
}
every time I debug my code I get this error:
Which means that the list is still null and nothing was added to it and I don't understand why.
What am I doing wrong ?
The issue you're running into here is that in the first part of the code you're saying, "Hey, Firebase, please retrieve this data and then when you receive it, put it into this variable called list." But then you're immediately turning around and trying to process list, even before Firebase has had a chance to fill it up. So it's null at the point when you're trying to iterate on it.
There are a few ways of fixing this, but I suppose the simplest would simply be to add your list parsing logic into the then clause of your Firebase call. A little something like this...
Future<String> sumTimers() async {
FirebaseDatabase.instance
.reference()
.child("Timers")
.once()
.then((DataSnapshot snapshot) {
Map<dynamic, dynamic> values = snapshot.value;
values.forEach((key, values) {
list.add(values["timespent"]);
});
int c = 0;
for (var x in list) {
hms = x.split(":");
... etc ...
}
return sumstring;
});
Note that in doing so, you're going to need to mark your function as async and declare that it returns a Future that resolves to String. If that previous sentence sounds like mumbo-jumbo, I highly recommend watching this video on Dart Futures, which explains things nicely.
This DataSnapshot not yet become list :
Map<dynamic, dynamic> values = snapshot.value;
This will do :
Map<dynamic,dynamic> curentvalue = snapshot.value;
List mylist = curentvalue.values.tolist();
mylist.forEach((key, values) {
list.add(values["timespent"]);
});

TransactionResult.Success does not update my mutableData (Firebase)

I have copied the code from Save Data.
which is like this:
void addScoreToLeaders(string name, int score ,string
key,Dictionary<string,object> childUpdates){
reference.Child ("leaders").KeepSynced (true);
reference.Child ("leaders").RunTransaction(mutableData =>{
List<Dictionary<string,object>> leaders = mutableData.Value as
List<Dictionary<string,object>>;
if(leaders == null){
leaders = new List<Dictionary<string,object>>();
} else if(mutableData.ChildrenCount >= MAX_SCORE){
int minScore = int.MaxValue;
Dictionary<string,object> minValue = null;
foreach(var child in leaders){
if(!(child is Dictionary<string,object>)) continue;
int childScore = (int)((Dictionary<string,object>)child)
["score"];
if(childScore < minScore){
minScore = childScore;
minValue = child;
}
}
if(minScore > score){
return TransactionResult.Abort ();
}
leaders.Remove (minValue);
}
//Add the new high score
Dictionary<string ,object> newScoreMap = new
Dictionary<string,object> ();
LeaderBoardEntry entry = new LeaderBoardEntry (name, score);
newScoreMap = entry.ToDictionary ();
leaders.Add (newScoreMap);
mutableData.Value = leaders;
return TransactionResult.Success (mutableData);
});
}
okay, there's two things not happens correctly :
TransactionResult.Success(mutableData) does not store the new data at the location
Return mutableData null for every first time i call the method
[Solved] after examining the code and several attempts of testing found the solution .
line number five (5) of the code causing problem which is :
List<Dictionary<string,object>> leaders = mutableData.Value as
List<Dictionary<string,object>>;
replace with :
Dictionary<string,object> leaders = mutableData.Value as
Dictionary<string,object>;
because mutableData.Value returned the data contained in this instance as native types, and i saved the data like Dictionary<.string,object>;

Chargify how can get total transactions of between two dates?

Currently, i am getting only 20 transactions per pages and can be extends only 200 but i need all transaction between that dates no paging.
Or if it possible to get count of transactions.?
How can i archive?
Thanks
I would use the following to retrieve all transactions for a subscription:
bool isFinished = false;
int counter = 1;
var results = new Dictionary<int, ITransaction>();
while (!isFinished)
{
// Get results
var transactions = chargify.GetTransactionsForSubscription(activeSubscription.SubscriptionID, counter++, 20);
// Check condition
if (transactions.Count == 0) { isFinished = true; continue; }
// Merge results
transactions.ToList().ForEach(x => results.Add(x.Key, x.Value));
}
That should get all the transactions and merge them all into the single dictionary. :) If need to use dates, then just switch the data retrieval line to something like this:
var transactions = chargify.GetTransactionsForSubscription(activeSubscription.SubscriptionID, counter++, 20, null, int.MinValue, int.MinValue, DateTime.Today, DateTime.Now); (just switch to your dates).

Resources