skipping data entries in a for loop in dart - flutter

a for loop runs to enter the data from an API call into a class the calls work fine but suddenly skip a few entries or enter previous entries into the list causing data to be lost.
for( int i = 0; i < pinarray.length; i++) {
String value = pinarray[i];
//print(value);
final response = await http.get(Uri.parse('http://api/api/getPin/?id=$value'));
final pinsData = json.decode(response.body) as Map<String, dynamic>;
print(pinsData);
//the problem is here
viewPins.add(Pin(
id: pinsData['id'],
location: pinsData['location'],
description: pinsData['description'],
qrcode: pinsData['qrcode'],
ttl: pinsData['ttl'],
tier: pinsData['tier'],
type: pinsData['type'],
user: pinsData['user'],
images: pinsData['images'],
viewable_users: pinsData['viewable_users']));
//print(i);
print(viewPins[i].id);
pincount = i;
}
the print(viewPins) shows that the wrong data is fetched but the data is still correct before it is entered into the list, there was a setState also in the code but was removed to see if it caused the problem but the issue remained.

Related

How to add if else condition to data in Realtime Database in Flutter?

Currently, I am working on a Flutter project, which is a tricycle booking system. Right now, I want to implement the functionality of having life points for every account. I have thought of this for some time and I have decided to store a value in the real-time database in firebase, it is where I will be decrementing the life points every time a user cancels the booking. What I want to do right now, is to check if the value stored in lifePoints is equal to a certain value, if yes, then I will be placing some functions and restrictions in there. So, how do i get the data from realtime database using flutter and add conditions to it?
Any help will be much appreciated. Thank you!
If you want to read data from Firebase, the documentation on reading data is a great starting point.
From there comes this great example of reading data once:
final ref = FirebaseDatabase.instance.ref();
final snapshot = await ref.child('users/$userId').get();
if (snapshot.exists) {
print(snapshot.value);
} else {
print('No data available.');
}
And this example for listening for data, which provides both the current value right away, and then continues to listen for updates:
DatabaseReference starCountRef =
FirebaseDatabase.instance.ref('posts/$postId/starCount');
starCountRef.onValue.listen((DatabaseEvent event) {
final data = event.snapshot.value;
updateStarCount(data);
});
If you want to increment/decrement a value in the database, have a look at this example from the documentation on atomic increments/decrements:
void addStar(uid, key) async {
Map<String, Object?> updates = {};
updates["posts/$key/stars/$uid"] = true;
updates["posts/$key/starCount"] = ServerValue.increment(1);
updates["user-posts/$key/stars/$uid"] = true;
updates["user-posts/$key/starCount"] = ServerValue.increment(1);
return FirebaseDatabase.instance.ref().update(updates);
}
Or if you want to perform a more complex update of a value based on its current value, you'll want to use a transaction:
DatabaseReference postRef =
FirebaseDatabase.instance.ref("posts/foo-bar-123");
TransactionResult result = await postRef.runTransaction((Object? post) {
// Ensure a post at the ref exists.
if (post == null) {
return Transaction.abort();
}
Map<String, dynamic> _post = Map<String, dynamic>.from(post as Map);
if (_post["stars"] is Map && _post["stars"][uid] != null) {
_post["starCount"] = (_post["starCount"] ?? 1) - 1;
_post["stars"][uid] = null;
} else {
_post["starCount"] = (_post["starCount"] ?? 0) + 1;
if (!_post.containsKey("stars")) {
_post["stars"] = {};
}
_post["stars"][uid] = true;
}
// Return the new data.
return Transaction.success(_post);
});
As you might notice these are all code snippets from the documentation, all from the same page even. I recommend spending some time studying that documentation, and then trying to apply these to your own use-case. If you then run into problems while implementing the use-case, post a question with the minimal code that reproduces where you got stuck and we can probably help further.

Firestore taking too much time to update from flutter app

I want to update a field of a particular document in firestore while listening to any change in its value. So basically, when the field 'call' is found to be 1, we will update it again back to 0 after printing a message. However, it takes too much time to update. On the other hand, updating the same field using python is way quicker. Please advise me what should be done.
FirebaseFirestore.instance
.collection('joystick')
.snapshots()
.listen((QuerySnapshot querySnapshot) {
var firestoreList = querySnapshot.docs;
var data = firestoreList.first.data();
var callval = firestoreList.first.get('call');
print("call value while listening ${callval}");
if(callval == 1){
print("call value is 1 ");
//makecall();
FirebaseFirestore.instance.collection('joystick').doc('data').update({'call':0});
call = firestoreList.first.get('call');
print("Next call value is ${call}");
if(call == 0){
print("updated!");
callval = 0;
}
}
}).onError((e) => print(e));
Document writing takes as long as it takes and depends on network connection speed. But you can speed up uploading data by compressing them. Dart has a useful tool as GZip. GZip can compress String data up to 99% You can decode/encode data in many ways even HttpClient can autoUncompress it :).
import 'dart:io';
// Send compressed data.
Future<void> sendCompressed() async {
final gzip = GZipCodec();
// You can convert any data to JSON string with `dart:convert`.
const String jsonToSend = '{"field": "Some JSON to send to the server."}';
// Original Data.
final List<int> original = utf8.encode(jsonToSend);
// Compress data.
final List<int> compressed = gzip.encode(original);
// Send compressed to db.
print(compressed);
}
// Get compressed data.
Future<void> getCompressed() async {
final gzip = GZipCodec();
// Get compressed data from the data base.
final List<int> compressed = await http.get('https://data.com');
// Decompress
final List<int> decompress = gzip.decode(compressed);
// Decode back to String (JSON)
final String decoded = utf8.decode(decompress);
// Do what you want with decoded data.
print(decoded);
}

Simply accessing call logs in flutter (?)

I need to access the call log of android mobile phone in my project and I don't have that much experience in Flutter. Found a package named 'call_log' but don't know how to implement. I would very much appreciate any help here.
Here is the readme file of 'call_log' package:
// IMPORT PACKAGE
import 'package:call_log/call_log.dart';
// GET WHOLE CALL LOG
Iterable<CallLogEntry> entries = await CallLog.get();
// QUERY CALL LOG (ALL PARAMS ARE OPTIONAL)
var now = DateTime.now();
int from = now.subtract(Duration(days: 60)).millisecondsSinceEpoch;
int to = now.subtract(Duration(days: 30)).millisecondsSinceEpoch;
Iterable<CallLogEntry> entries = await CallLog.query(
dateFrom: from,
dateTo: to,
durationFrom: 0,
durationTo: 60,
name: 'John Doe',
number: '901700000',
type: CallType.incoming,
);
What I could understand from your question is, you are unable to work with the output of CallLog.get() here.
After adding the package to pubspec.yaml file dependencies and importing it, you can call the get() function using the following line of code -
Iterable<CallLogEntry> entries = await CallLog.get();
It returns an Iterable of type CallLogEntry. An iterable is simply a collection of values, or "elements", that can be accessed sequentially.
The output gets stored in entries which can then be iterated over to access the values such as -
void _callLogs() async {
Iterable<CallLogEntry> entries = await CallLog.get();
for (var item in entries) {
print(item.name);
}
}
The above code snippet would print the names of all CallLog entries. Try replacing item.name with item.number, item.duration, item.callType.
Also, do not forget to add the following line to AndroidManifest.xml
<uses-permission android:name="android.permission.READ_CALL_LOG" />
Instead of CallLog.get(), you can also use CallLog.query() to specify constraints on the response/output as mentioned in the question itself.

With Flutter/Dart How should I query data that doesn't build a widget?

I'm working on an app and I'm relatively new to this scene, but I'm running into some troubles when I'm looking to simply query some data.
I'm calling a function after someone logs in. The login function only sends the necessary information and I want to do a secondary ping to my server to get a little more user information, more than just verifying the login info.
getUser (userid) async{
List<Map<String, dynamic>> user=[] ;
var client = new http.Client();
try {
var req = await client.post(urlPath+'mobileGetUser', body: {'thisUserID': userid'});
var jsonResponse = convert.jsonDecode(req.body);
//print('Here: '+jsonResponse.toString());
var id = jsonResponse['id'] ?? '';
var joinDate = jsonResponse['joinDate'] ?? '';
var userEmail = jsonResponse['userEmail'] ?? '';
var displayName = jsonResponse['displayName'] ?? '';
var timezone = jsonResponse['timezone'] ?? '';
var verified = jsonResponse['verified'] ?? '';
user = [{'id': id}];
user = [{'joinDate': joinDate}];
user = [{'userEmail': userEmail}];
user = [{'displayName': displayName}];
user = [{'timezone': timezone}];
user = [{'verified': verified}];
return user
} finally {
client.close();
}
}
I'm used to working in PHP. In that language, I'd pass the JSON object or associative array back to the calling function and accessing the individual fields would be as simple as
$displayName = $user['displayName'];
But this isn't PHP. List types are a little strange to me still.
From my calling function, I try to test this with:
thisUser = getUser(userid);
print('Successful Login: Display name: '+thisUser.toString());
And I get a message of:
Successful Login: Display name: Instance of 'Future<dynamic>'
How can I access this data? I've tried a few ways to get it. Also, is there a better way to create my List? I'm definitely going through a few steps that feel unnecessary. I'm honestly like to just pass it the entire JSONresponse. I'm just a bit out of my depth.
You could do this:
thisUser = await getUser(userid);
print('after'); // this will print after getUser() finishes
If you don't await for getUser() to finish, then you'll receive a Future.
Another way would be to use that Future and add a listener to it, like this:
getUser(userid).then((value) {
print('after'); // this will print after getUser() finishes
thisUser = value;
});
print('before'); // this will print before getUser() finishes
This is assuming you aren't using this on widgets. Otherwise you could use FutureBuilder to detect when the Future finishes and show something.

Flutter, getting database records and then internet json

I have a simple table from which I'm fetching a list of records. Once I get the records, then I have to get information online for each of the records. The code to do this is as follows:
class UserStationList {
List<UserStationListItem> _userStations = [];
final StreamController<HomeViewState> stateController;
UserStationList({#required this.stateController});
Future fetchUserStations() async {
stateController.add(HomeViewState.Busy);
//Fetch stations from table.
List<Map<String, dynamic>> stations =
await UserStationDatabase.instance.queryAllRows();
//If there are no stations, return and tell the screen to display the no data message.
if (stations.length == 0) {
stateController.add(HomeViewState.NoData);
return;
}
//Loop through each of the stations in the list and build the collection.
stations.forEach((station) async {
UserStationListItem newItem =
await _getPurpleAirSiteData(station['_id'], station['stationid']);
_userStations.add(newItem);
});
//When done, let the screen know.
stateController.add(HomeViewState.DataRetrieved);
}
Future<UserStationListItem> _getPurpleAirSiteData(
int id, int stationId) async {
var response = await http.get('$kURL$stationId');
var data = json.decode(response.body);
return UserStationListItem(
id: id, stationId: stationId, stationName: data['results'][0]['Label']);
}
}
The problem that I am running into involves the futures. I am processing the loop in a forEach and calling into the _getPurpleAirSiteData function for each. Within that function I have to await on the http.get to bring in the data. The stateController.add(HomeViewState.DataRetrieved) function is being called and the function exits long before the loop is completed. This is resulting in the data not being available when the StreamBuilder that I have receiving the data is run.
How can I set this up so that the loop runs completely before calling stateController.add?
I would change this part of code to a list of Futures and await-ing on it.
//Loop through each of the stations in the list and build the collection.
stations.forEach((station) async {
UserStationListItem newItem =
await _getPurpleAirSiteData(station['_id'], station['stationid']);
_userStations.add(newItem);
});
To:
List<Future<UserStationListItem>> listOfFutures = [];
stations.forEach((station) {
listOfFutures.add(_getPurpleAirSiteData(station['_id'], station['stationid']));
});
var stationItems = await Future.wait(listOfFutures);
stationItems.forEach((userStationListItem) {
_userStations.add(userStationListItem);
});
What I am essentially doing creating a list of Futures with your server request. Then await on it which returns a list of item result Maintaining index, which in turn ensures that requests are completed before you hit statecontroller.add. You also gain a performance gain since all request are not going one by one and instead asynchronously. Then you just iterate through the future result and add it to your item list.