Race condition with stream listen - flutter

I have an async function like below. However content is being returned null well before the stream listening is done.
I started playing out with Future.delayed, but thought better of it and wanted to ask if there is a better approach to ensure this is async?
import 'package:googleapis/drive/v3.dart' as ga;
static Future<String> getContentsFromFile() async {
String content;
ga.Media mediaResponse = await drive.files.get(fileId);
mediaResponse.stream.listen((data) {
print("DataReceived: "+data);
content = data
}, onDone: () async {
print("Task Done");
}, onError: (error) {
print("Some Error");
});
return content;
}
Im calling the function like so..
String content = await getContentsFromFile();

EDIT: Made the example more complete, with handling of errors and partial content.
You can use Completer for this sort of control flow:
import 'dart:async';
import 'package:googleapis/drive/v3.dart' as ga;
static Future<String> getContentsFromFile() async {
Completer<String> completer = Completer();
String content = "";
ga.Media mediaResponse = await drive.files.get(fileId);
mediaResponse.stream.listen((data) {
print("DataReceived: "+data);
content += data;
}, onDone: () async {
print("Task Done");
completer.complete(content);
}, onError: (error) {
print("Some Error");
completer.completeError(error);
});
return completer.future;
}

Related

How to pass data between isolates in flutter dart

I am buiding an app were I want to run a batch operation in firestore and I want to run it in a different isolate. Here is my code for spawning the isolate:
Future<void> _startAnotherIsolate(String mediaUrl) async {
final isolate = await FlutterIsolate.spawn(isolate1,"hello"); // i need to pass 2 more
arguments
Timer(Duration(seconds: 5), () {
print("Pausing Isolate 1");
isolate.pause();
});
Timer(Duration(seconds: 10), () {
print("Resuming Isolate 1");
isolate.resume();
});
Timer(Duration(seconds: 20), () {
print("Killing Isolate 1");
isolate.kill();
});
}
My code for the isolate:
void isolate1(String data1, String data2) async {
await Firebase.initializeApp();
print("changing profile picture: $phone");
Timer.periodic(Duration(seconds: 1), (timer) => print("Timer Running From Isolate 1"));
var db = FirebaseFirestore.instance;
var batch = db.batch();
FirebaseFirestore.instance.collection("posts").doc(phone).collection("userPosts")
.get().then((querySnapshot) {
for (var document in querySnapshot.docs) {
try {
batch.update(document.reference,{'user_image': mediaUrl});
} on FormatException catch (error) {
// If a document ID is unparsable. Example "lRt931gu83iukSSLwyei" is unparsable.
// print("The document ${error.source} could not be parsed.");
return null;
}
}
return batch.commit();
});
}
I have seen This link and this link but they are not helpful
import 'dart:isolate';
class RequiredArgs {
late final SendPort sendPort;
late int id;
RequiredArgs(this.id, this.sendPort);
}
Future<void> main() async {
ReceivePort receivePort = ReceivePort();
RequiredArgs requiredArgs = RequiredArgs(1122, receivePort.sendPort);
Isolate isolate = await Isolate.spawn(download, requiredArgs);
var resp = await receivePort.first;
print(resp);
}
void download(RequiredArgs requiredArgs) {
final SendPort sendPort = requiredArgs.sendPort;
final id = requiredArgs.id;
print(id);
sendPort.send("yes");
}
We pass the value using the RequiredArgs class. Hope my answer helps.

why my circularProgressIndicator having strange behavior when async function called?

Im calling a function to get data from Excel file and upload it to my Firestore as following
floatingActionButton: FloatingActionButton(onPressed: () async {
Utils.showLoading(context);
await FireStoreServices.bulkUploadFromExcelToFireStore(
collectionName: 'test',
fileName: 'test',
sheetName: 'test');
Navigator.pop(context);
}),
the problem is my Progress loading indicator not working as expected in this case (not spinning only shows and freeze until the function complete after that its popped)
i tried to replace the awaited function 'bulkUploadFromExcelToFireStore' with Future.delayed and it worked as expected
await Future.delayed(const Duration(seconds: 3), () {});
what might be the problem ?
here is the code of bulkUploadFromExcelToFireStore function
static Future bulkUploadFromExcelToFireStore(
{required String fileName,
required String sheetName,
required String collectionName}) async {
try {
final rowsData = await Utils.readExcelFileData(
excelFilePath: fileName, sheetName: sheetName);
rowsData.removeAt(0);
for (var row in rowsData) {
firebaseFirestore.collection(collectionName).doc(row[0]).set(data, SetOptions(merge: true));
}
} catch (e) {
print('Cached ERROR MESSAGE = = = = ${e.toString()}');
}
I added some validations inside your function to check for possible failures.
It would also be interesting to validate a failure warning and terminate the Progression Indication initialization.
static Future<String> bulkUploadFromExcelToFireStore({required String fileName, required String sheetName,required String collectionName}) async {
try {
final rowsData = await Utils.readExcelFileData(excelFilePath: fileName, sheetName: sheetName);
rowsData.removeAt(0);
if(rowsData.length == 0) {
return "No Items!";
} else {
for (var row in rowsData) {
firebaseFirestore?.collection(collectionName)?.doc(row[0])?.set(data, SetOptions(merge: true));
}
return "Item allocated!";
}
} catch (e) {
return e.toString();
}
}

Flutter UI freezes on hash check

I have a program that will check a password with bcrypt library, this is quite computing intensive, so as a result the UI will be stuck for like 2 seconds. It is very annoying and I cannot figure out what to do to stop it.
I want a loader to be shown when the password is being checked.
This is my code:
class _MyWidgetState<MyWidget> extends State{
build() {
return GetPassCode(PassCodeType.ENTER,
onDone: ({context, data}) async {
unlock(state, data?['password'] ?? '', Languages.of(context));
}, goBack: () {}, data: {});
}
unlock(userState, String? password, Languages strings) async {
final user = userState.currentUser;
if (!(await user.checkPassword(password))) {
return;
}
}
context.read<LockCubit>().unlock();
}
}
you can put the caculating into a isolate.
https://api.flutter-io.cn/flutter/dart-isolate/dart-isolate-library.html
here's some example code:
class IsoMessage {
final SendPort? sendPort;
final List<String> args;
IsoMessage(this.sendPort, this.args);
}
String myCaculate(IsoMessage message) {
String result = message.args[0][0] + message.args[1][1];
message.sendPort?.send(result);
return result;
}
here's how to calling the func
var port = ReceivePort();
port.listen((message) {
print("onData: $message");
}, onDone: () {
print('iso close');
}, onError: (error) {
print('iso error: $error');
});
IsoMessage message = IsoMessage(port.sendPort,["asd", "dsa"]);
Isolate.spawn<IsoMessage>(myCaculate, message);

Flutter: 'Future.wait' multiple async functions in parallel VS 'await' one at a time. <= different results

I recently learned of the fabulous way of waiting for multiple async functions to complete using Future.wait([asyncFuncOne(), asyncFunctwo()])
However, I noticed two different outcomes when running either of these blocks of code. One awaiting each function to finish, the other using Future.wait for parallel processing. What am I doing wrong?
Method 1:
await msm.initProfileData();
await msm.initActivityFeed();
await msm.getRecentlyActiveUsers();
await msm.getRecommendedUsers();
await msm.getGroups();
await msm.getFollowing();
await msm.getFollowers();
Method 2:
await Future.wait([
msm.getFollowing(),
msm.initProfileData(),
msm.initActivityFeed(),
msm.getRecentlyActiveUsers(),
msm.getRecommendedUsers(),
msm.getGroups(),
msm.getFollowers(),
]);
in Method 1, all the async functions complete before my apps home screen appears. In Method 2 the home screen appears before all the async functions complete.
Cheers and thanks in advance.
EDIT: Additional code example.
#override
void initState() {
super.initState();
googleSignIn.onCurrentUserChanged.listen((account) {
handleSignIn(account);
}, onError: (err) {
print('Error signing in: $err');
});
googleSignIn.signInSilently(suppressErrors: false).then((account) {
handleSignIn(account);
}).catchError((err) {
setState(() => _showSignIn = true);
print('Error signing in: $err');
});
}
handleSignIn(GoogleSignInAccount account) async {
if (account != null) {
await createUserInFirestore();
setState(() {
isAuth = true;
});
} else {
setState(() {
isAuth = false;
_showSignIn = true;
});
}
}
createUserInFirestore() async {
final GoogleSignInAccount user = googleSignIn.currentUser;
DocumentSnapshot doc = await usersRef.document(user.id).get();
//...
//do stuff
//...
await someFunc1(); //Method1
// await comeFunc2(); //Method2
//do more stuff
}
someFunc1() async {
msm.asyncfunc1();
msm.asyncfunc2();
}
someFunc2() async {
await Future.wait([
msm.asyncFunc1(),
msm.asyncFunc2(),
]);
}
#override
Widget build(BuildContext context) {
return isAuth ? buildAuthScreen() : buildUnAuthScreen();
}
Using Future.wait(List<Future>) will wait for all the async operations without sequence as mentioned in the docs. While using await consecutively, it'll wait for the first await async operation to finish before running the next await async operation. If you have a prerequisite output before running the next async operation, it's better to use await async in sequence instead.

How to post observer from normal class and receive listener to widget?

I'm pretty new to Flutter and experimenting with the SDK. I am working with the flutter application which works with Socket connection. I saw lots of example which communicate with widget to widget. But, I want to add listener from Socket class to widgets. The actual scenario is, I have socket listeners in my socket manager class. Here is the rough code for better idea.
class SocketManager {
static SocketIO socketIO;
static SocketIOManager manager = SocketIOManager();
//Constructor
SocketManager(){
initSocket().then((socketIO){
addListener();
});
}
void addListener(){
socketIO.onConnect((data){
print("connected...");
});
}
}
I want to notify to my widgets when socket connected.
What kind of thing am I looking for to implement this?
Thanks in advance.
here is my class, you can follow to create yours
import 'dart:convert';
import 'package:flutter_app/global.dart';
import 'package:flutter_app/strings.dart';
import 'package:rxdart/subjects.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:socket_io_client/socket_io_client.dart' as IO;
IO.Socket kSocket;
class Sockets {
static PublishSubject socket = PublishSubject(sync: true);
static PublishSubject status = PublishSubject(sync: true);
static PublishSubject notify = PublishSubject(sync: true);
static PublishSubject chatCount = PublishSubject(sync: true);
static PublishSubject typing = PublishSubject(sync: true);
static PublishSubject login = PublishSubject(sync: false);
static PublishSubject getInfo = PublishSubject(sync: true);
static PublishSubject alreadyLogin = PublishSubject(sync: false);
static void connectSocket() async {
/* kSocket = await IO.io('${Strings.socket}', <String, dynamic>{
'transports': ['websocket', 'polling'],
});*/
SharedPreferences prefs = await SharedPreferences.getInstance();
String token = prefs.getString('userToken');
if (token != null && token != '') {
Map<String, dynamic> parsedToken = Functions.parseJwt(token);
String imza = token?.split('.')[2];
kSocket = await IO.io('${Strings.socket}', <String, dynamic>{
'transports': ['websocket', 'polling'],
'query': 'token=$imza'
});
parsedToken['Tur'] = 2;
kSocket.close();
kSocket.disconnect();
kSocket.open();
try {
kSocket.on('connect', (data) {
print('SOCKET CONNECTED');
kSocket.emit('adduser', parsedToken);
kSocket.on('getmessage', (res) {
print('GETMSG: $res');
chatCount.sink.add(res);
socket.sink.add(res);
});
kSocket.on('bildirim', (res) {
print('[BILDIRIM]: $res');
notify.sink.add(res);
});
kSocket.on('durum', (res) {
status.sink.add(res);
});
kSocket.on('disconnect', (data) {
// print('DISCONNECT: $data');
});
kSocket.on('typing', (res) {
typing.sink.add(res);
});
kSocket.on('login', (res) {
//print('Multi Login');
login.sink.add(res);
});
kSocket.on('getinfo', (res) {
//print('GETINFO: $res');
getInfo.sink.add(res);
});
kSocket.on('alreadylogin', (res) {
//print('ALREADY LOGIN: $res');
alreadyLogin.sink.add(res);
});
});
} catch (e) {
print(e);
}
} else {
print('SOCKET: token yok');
}
}
static void setInfo(Map<String, dynamic> data) {
kSocket.emit('setinfo', [data]);
}
static void setRead(String userid) {
kSocket.emit('setreaded', '$userid');
}
static void isTyping(String username, int status) {
kSocket.emit('istyping', [
{"user": int.parse(username), "durum": status}
]);
}
static void isActive(String userid) {
if (kSocket != null) {
if (kSocket.connected) {
try {
//print('${kSocket.connected}');
kSocket.emit('isactive', '$userid');
} catch (e) {
print(e);
}
}
}
}
static void disconnectSocket() async {
try {
await kSocket.disconnect();
await kSocket.close();
await kSocket.destroy();
print('SOCKET DISCONNECTED');
} catch (e) {
//print(e);
}
}
static void dispose(){
socket.close();
status.close();
//notify.close();
chatCount.close();
typing.close();
login.close();
getInfo.close();
alreadyLogin.close();
}
static void unSubNotify(){
notify.close();
}
}
Answer is here !! Here what I found while surfing on the web. Flutter-NotificationCenter. An IOS type post and receive observer. It is Very helpful to other developers who want to post observer from anywhere and want to receive it to anywhere.