Flutter : Avoid using `forEach` with a function literal - flutter

Hi everyone this is my whole method :
Future<void> init() async {
FirebaseAuth.instance.userChanges().listen((user) {
if (user != null) {
_loginState = ApplicationLoginState.loggedIn;
_guestBookSubscription = FirebaseFirestore.instance
.collection('guestbook')
.orderBy('timestamp', descending: true)
.limit(3)
.snapshots()
.listen((snapshot) {
_guestBookMessages = [];
snapshot.docs.forEach((document) {
_guestBookMessages.add(
GuestBookMessage(
name: document.data()['name'] as String,
message: document.data()['text'] as String,
),
);
});
notifyListeners();
});
} else {
_loginState = ApplicationLoginState.loggedOut;
_guestBookMessages = [];
_guestBookSubscription?.cancel();
}
notifyListeners();
});
}
the part that dart complains about is this one :
snapshot.docs.forEach((document) {
_guestBookMessages.add(
GuestBookMessage(
name: document.data()['name'] as String,
message: document.data()['text'] as String,
),
);
});
how can I change this method without ruining the whole functionality ?
Im just looking for a way that makes dart happy .
I appreciate your help in advance.

AVOID using forEach with a function literal.
BAD:
snapshot.docs.forEach((document) {
...
});
GOOD:
for (var document in snapshot.docs) {
// Rest of your code
}

Using like data.forEach(function);
example:
void main() async {
List<int> list = [1, 2, 3, 4, 5];
//recommended
list.forEach(showSquareNumbers);
//not recommended
list.forEach((int number) => showSquareNumbers(number));
list.forEach((int number) {
showSquareNumbers(number);
});
}
void showSquareNumbers(int data) {
print("$data * $data = ${data * data}");
}
This is my opinion.
I think the forEach seems more complicated than the for loop and the forEach can't use continue and break (return is available but not a thing happens when using return).
void test(List data) {
data.forEach((element) {
print(element);
if(element) return;
});
for (var element in data) {
print(element);
if(element) continue;
if(element) break;
if(element) return;
}
}
I think we should use the for instead of the forEach loop when your forEach loop seems like this code below because the for loop have many options more than forEach as I said.
data.forEach((element) {
print(element)
});
//or
data.forEach((element) => print(element));
I think the forEach loop is used for short code (easy to understand) and when you want to do something you don't care about the result like this code (using with Function(dynamic)).
void test(List data) {
void showInTerminal(e) {
print("data is $e");
}
data.forEach(showInTerminal);
Function(dynamic) function = showInTerminal;
data.forEach(function);
}
Make sure the data type and function(type) are the same.
//error
void test(List<Map> data) {
void showInTerminal(String e) {
print("data is $e");
}
data.forEach(showInTerminal);
}
//success
void test(List<Map> data) {
void showInTerminal(Map e) {
print("data is $e");
}
data.forEach(showInTerminal);
}
I code like this. I think it's easy to read.
void test() {
dataList.forEach(removeData);
fileList.forEach(removeFile);
}

Use await for:
await for (final document in snapshot.docs) {
// Your code...
}

Related

How to make new "Future" instance based on function parameter in Dart?

In my code, "getResponse" is executed only once. How can I fix it?
I don't want to put "getResponse" inside "retry".
import "dart:math";
Future getResponse(int sec) async {
return Future.delayed(Duration(seconds: sec), () {
int rand = Random().nextInt(10);
print(rand);
if (rand < 5) {
return "success";
} else {
throw "rejected";
}
});
}
Future retry(Future f, [int count = 0]) async {
try {
return (await f);
} catch (e) {
if (count < 5) {
print(e);
retry(f, count + 1); // I think here is wrong.
}
}
}
void main() async => await retry(getResponse(1));
Function "retry" should execute getResponse until it successed
You cannot "retry" a future. Once it's done, it's done. You can however, create a new one every time, by passing a "future factory" (a function producing the relevant future) instead of the future:
import "dart:math";
Future getResponse(int sec) async {
return Future.delayed(Duration(seconds: sec), () {
int rand = Random().nextInt(10);
print(rand);
if (rand < 5) {
return "success";
} else {
throw "rejected";
}
});
}
// pass the factory function here
Future retry(Future Function() f, [int count = 0]) async {
try {
// call the function here to get a future to await
return (await f());
} catch (e) {
if (count < 5) {
print(e);
retry(f, count + 1);
}
}
}
// here, a function returning a future, instead of the future itself is passed
void main() async => await retry(() => getResponse(1));
The comment suggesting a loop instead of recursion is probably spot on too.

how to create a stream in flutter that return a bool in every second

i am making a app. And i want to check my server state every minite and give user information
about the server. How do i do it. is stream good for it. Can some provide me a code for that.
just follow this guide
suppose your bool return value function is
Future<bool> isGpsOn() async {
return await Geolocator().isLocationServiceEnabled();
}
and this is create stream from bool value
Stream futureToStream(fn, defaultValue, Duration duration) async* {
var result;
while (true) {
try {
result = await fn();
}
catch (error) {
result = defaultValue;
}
finally {
yield result;
}
await Future.delayed(duration);
}
}
final gpsStatusStream = futureToStream(isGpsOn, false, Duration(seconds: 5));
gpsStatusStream.listen((enabled) {
print(enabled ? 'enabled' : 'disabled');
});
Use asyncMap
Stream<String> checkConnectionStream() async* {
yield* Stream.periodic(Duration(seconds: 1), (_) {
return //your function
}).asyncMap((event) async => await event);
}

Exception throw flutter

am learning api integration with bloc, these exception is been thrown when data is trying to fetch, for loadingstate i assigned a progressindicator then after that state when trying to get data,these exeption is been thrown ,pls helpenter image description here
as per the console i tried to change the data type to from double to num, still same exception
try {
_emitters.add(emitter);
await handler(event as E, emitter);
} catch (error, stackTrace) {
onError(error, stackTrace);
rethrow;
} finally {
onDone();
}
networkfile.dart
class Repository {
List<FakeStore> collections = [];
Future<List<FakeStore>?> getdata() async {
String url = 'https://fakestoreapi.com/products';
final data = await http.Client().get(Uri.parse(url));
if (data.statusCode != 200) {
return null;
} else {
Iterable values = jsonDecode(data.body);
for (var value in values) {
FakeStore fakeStore = FakeStore.fromJson(value);
collections.add(fakeStore);
}
return collections;
}
}
}
bloc.dart
class FakestoreBloc extends Bloc<FakestoreEvent, FakestoreState> {
final Repository repository;
FakestoreBloc({required this.repository}) : super(FakestoreInitialstate()) {
on<FakestoreEvent>((event, emit) async {
if (event is StorelaodEvent) {
emit(Fakestorelaodingstate());
List<FakeStore>? apiresult = await repository.getdata();
if (apiresult == null) {
emit(FAkestoreErrorstate());
} else {
emit(Fakestoreloadedstate(apiresult: apiresult));
}
}
});
}
}

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

How can I retry a Future in Dart/Flutter?

I have a method that does some async processing and want it to retry X times. How can I achieve that in Dart/Flutter?
Use this function:
typedef Future<T> FutureGenerator<T>();
Future<T> retry<T>(int retries, FutureGenerator aFuture) async {
try {
return await aFuture();
} catch (e) {
if (retries > 1) {
return retry(retries - 1, aFuture);
}
rethrow;
}
}
And to use it:
main(List<String> arguments) {
retry(2, doSometing);
}
Future doSometing() async {
print("Doing something...");
await Future.delayed(Duration(milliseconds: 500));
return "Something";
}
I added an optional delay to Daniel Oliveira's answer:
typedef Future<T> FutureGenerator<T>();
Future<T> retry<T>(int retries, FutureGenerator aFuture, {Duration delay}) async {
try {
return await aFuture();
} catch (e) {
if (retries > 1) {
if (delay != null) {
await Future.delayed(delay);
}
return retry(retries - 1, aFuture);
}
rethrow;
}
}
You can use it as follows:
retry(2, doSometing, delay: const Duration(seconds: 1));
Retry from Dart Neat is a good API and, unofficially, it's from Google:
https://pub.dev/packages/retry
This is how I implemented it:
Future retry<T>(
{Future<T> Function() function,
int numberOfRetries = 3,
Duration delayToRetry = const Duration(milliseconds: 500),
String message = ''}) async {
int retry = numberOfRetries;
List<Exception> exceptions = [];
while (retry-- > 0) {
try {
return await function();
} catch (e) {
exceptions.add(e);
}
if (message != null) print('$message: retry - ${numberOfRetries - retry}');
await Future.delayed(delayToRetry);
}
AggregatedException exception = AggregatedException(message, exceptions);
throw exception;
}
class AggregatedException implements Exception {
final String message;
AggregatedException(this.message, this.exceptions)
: lastException = exceptions.last,
numberOfExceptions = exceptions.length;
final List<Exception> exceptions;
final Exception lastException;
final int numberOfExceptions;
String toString() {
String result = '';
exceptions.forEach((e) => result += e.toString() + '\\');
return result;
}
}
This is how I use it:
try {
await retry(
function: () async {
_connection = await BluetoothConnection.toAddress(device.address);
},
message: 'Bluetooth Connect');
} catch (e) {
_log.finest('Bluetooth init failed ${e.toString()}');
}