how to handle a loop in flutter? need can be canceled? - flutter

I want to use flutter fulfilled a mqtt client. the client need send message to serve for loop.
I used "while" keyword for loop. but the flutter UI is pending during the while function.
if use isolate to work it, sending the cancel flag failed.
Does there anybody have the experience on it?
----------code------
onpressed -----> Future Function ----> use while loop---->the app pending cannot press anywhere---> the loop stop, the app recovery
I want start a mqtt client to send mqtt message for loop.
but when pressed the function, the while loop pended the app.
Expected results: the loop works in an async work, and the app can work rightly.
Actual results: the app is pending.
###UI
children: [
SizedBox(width: 200),
MaterialButton(
child: Text('发送'),
onPressed: () {
BtnsendMsg();
},
####onpressed function
mc = new MqttClient(false);
BtnsendMsg() async {
mc.MsgSend(clientid, topic, msgname, '3');
print("back");
}
####loop function
class MqttClient {
bool isStop;
MqttClient(this.isStop);
Future MsgSend(clientid, topic, msgname, interval) async {
isStop = false;
var cc = await clientGet(clientid);
var msg = await msgGet(msgname);
String host = "1.1.1.1";
String msgdata = "1111";
if (cc != null) {
host = cc.host!;
}
if (msg != null) {
msgdata = msg.msgdata!;
}
Future future = Future(() {
while (isStop == false) {
SendMsgOnce(host, clientid, topic, msgdata);
sleep(Duration(seconds: 3));
}
});
sleep(Duration(seconds: 30));
isStop = true;
}

This is because you are putting load on main thread by not using async and await while sending request to the server. Do the following changes to your code then it should get work.
class MqttClient {
bool isStop;
MqttClient(this.isStop);
Future MsgSend(clientid, topic, msgname, interval) async {
isStop = false;
var cc = await clientGet(clientid);
var msg = await msgGet(msgname);
String host = "1.1.1.1";
String msgdata = "1111";
if (cc != null) {
host = cc.host!;
}
if (msg != null) {
msgdata = msg.msgdata!;
}
Future future = Future(() async {
while (isStop == false) {
await SendMsgOnce(host, clientid, topic, msgdata);
sleep(Duration(seconds: 3));
}
});
sleep(Duration(seconds: 30));
isStop = true;
}
In your on pressed function you are using async but now awaiting for that
mc = new MqttClient(false);
BtnsendMsg() async {
await mc.MsgSend(clientid, topic, msgname, '3');
print("back");
}

Related

How to call API every specific duration waiting for change in response?

I have this code:
var wasViewed = await _check_if_was_viewed(value: value);
if (!wasViewed) {
var period = const Duration(seconds: 5);
Timer.periodic(period, (t) async {
wasViewed = await _check_if_was_viewed(value: value);
});
}
print("finished");
Where _check_if_was_viewed receive an id to search in DB from API, then return bool if the first state has been modified.
Future<bool> _check_if_was_viewed({value}) async {
var response = await APIService.getData(
database: database,
table: table,
id: idColumn,
idValue: value);
var status = response["data"]["status"]) != 0;
return status;
}
The code is working because call the API every 5 seconds but the block
Timer.periodic(period, (t) async {
wasViewed = await _check_if_was_viewed(value: value);
});
creates multiples instaces of Timer, I tried to fixed with
class formHelpState extends State<formHelp> {
Timer? timer;
…
…
var wasViewed = await _check_if_was_viewed(value: value);
if (!wasViewed) {
var period = const Duration(seconds: 5);
timer = Timer.periodic(period, (t) async {
wasViewed = await _check_if_was_viewed(value: value);
});
}
print("finished");
But in this way the callback function doesn't execute.
That I try is:
Call api every X seconds (ok)
if status of id was changed execute other event (not ok)

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.

how to enable ear speaker instead of loud speaker in flutter

I had done with calling peer to peer in flutter but there is a problem how can I enable ear speaker instead of the loudspeaker in a flutter. Please help me to do this,
while establishing calling from one device to another device it only enables the loudspeaker instead of ear speaker. Thanks in advance.
Future<void> joinRoom(String roomId, RTCVideoRenderer remoteVideo) async {
FirebaseFirestore db = FirebaseFirestore.instance;
DocumentReference roomRef = db.collection('rooms').doc('$roomId');
var roomSnapshot = await roomRef.get();
log('Got room ${roomSnapshot.exists}');
if (roomSnapshot.exists) {
log('Create PeerConnection with configuration: $configuration');
peerConnection = await createPeerConnection(configuration);
registerPeerConnectionListeners();
localStream?.getTracks()?.forEach((track) {
peerConnection?.addTrack(track, localStream);
});
// Code for collecting ICE candidates below
var calleeCandidatesCollection = roomRef.collection('calleeCandidates');
peerConnection.onIceCandidate = (RTCIceCandidate candidate) {
if (candidate == null) {
log('onIceCandidate: complete!');
return;
}
log('onIceCandidate: ${candidate.toMap()}');
calleeCandidatesCollection.add(candidate.toMap());
};
// Code for collecting ICE candidate above
peerConnection?.onTrack = (RTCTrackEvent event) {
log('Got remote track: ${event.streams[0]}');
event.streams[0].getTracks().forEach((track) {
log('Add a track to the remoteStream: $track');
remoteStream?.addTrack(track);
});
};
// Code for creating SDP answer below
var data = roomSnapshot.data();
log('Got offer $data');
var offer = data['offer'];
await peerConnection?.setRemoteDescription(
RTCSessionDescription(offer['sdp'], offer['type']),
);
var answer = await peerConnection.createAnswer();
log('Created Answer $answer');
await peerConnection.setLocalDescription(answer);
Map<String, dynamic> roomWithAnswer = {
'answer': {'type': answer.type, 'sdp': answer.sdp}
};
await roomRef.update(roomWithAnswer);
// Finished creating SDP answer
// Listening for remote ICE candidates below
roomRef.collection('callerCandidates').snapshots().listen((snapshot) {
snapshot.docChanges.forEach((document) {
var data = document.doc.data();
// log(data);
log('Got new remote ICE candidate: $data');
peerConnection.addCandidate(
RTCIceCandidate(
data['candidate'],
data['sdpMid'],
data['sdpMLineIndex'],
),
);
});
});
}
}
Future<void> openUserMedia(RTCVideoRenderer localVideo,
RTCVideoRenderer remoteVideo, BuildContext contextt) async {
context = contextt;
chatProvider = Provider.of<ChatProvider>(context, listen: false);
var stream = await navigator.mediaDevices.getUserMedia({'audio': true});
localVideo.srcObject = stream;
localStream = stream;
remoteVideo.srcObject = await createLocalMediaStream('key');
}

How to synchronize a call from the asynchronous function in dart

I am working on my first app in Flutter, I have a bit of experience with Java and js, but I never worked with flutter before so sorry if my question will seem ridiculous to you.
The app is the voice assistant chatbot, and it is supposed to perform text to speech on each new message that customer receives, my problem is that since I am using firebase messaging all of the requests that I receive are in the asynchronous call, but I need to synchronize the access to the text to speech service otherwise I run into problem of having one text interrupt another.
This is what my code looks like at the moment:
Firebase messaging:
onMessage: (Map<String, dynamic> message) {
return this.handleBotMessage(appState, message);
},
Method that desides how to handle each particular message:
Future handleBotMessage(
Store<AppState> store,
Map<String, dynamic> dataJson,
) {
#logic that convert the message into json and extracts the message type
if (type == MessageType.CHAT_MESSAGE) {
return handleChatMessage(store, subtype, messageMap);
}
}
The method that handles text messages:
Future<dynamic> handleChatMessage(
Store<AppState> store,
MessageSubtype subtype,
Map<String, dynamic> message,
) {
#Text to speach is build as a singleton and this always returns the same instance
TextToSpeech tts = TextToSpeech();
if (subtype == MessageSubtype.TEXT) {
TextMessage textMessage = TextMessage.fromJson(message);
return tts
.speak(textMessage.text)
.then((result) => store.dispatch(NewBotMessageAction(textMessage)));
} else if (subtype == MessageSubtype.QUICK_REPLY) {
QuickReplyMessage qrMessage = QuickReplyMessage.fromJson(message);
return tts
.speak(qrMessage.text)
.then((result) => store.dispatch(NewQrOptionsAction(qrMessage)));
} else {
throw new Exception('Unexpected message subtype!');
}
}
The method that actually performs the text to speech
Future<dynamic> speak(String text) async {
return flutterTts.speak(text).then((resp) {
ttsRunning = false;
print(resp);
return resp;
}, onError: (obj, st) {
ttsRunning = false;
print(obj);
print(st.toString());
});
}
Text to speech initialization
Future init() async {
await flutterTts.setLanguage("en-US");
var res = await flutterTts.isLanguageAvailable("en-US");
print(res);
return res;
}
https://pub.dev/packages/flutter_tts
Ok, I have found the solution, the issue was as frank06 pointed out with the fact that flutter tts completes the future immediately rather than after the whole phrase was spoken.
So here is my solution, it is not perfect, but it works:
Completer completer;
Future<dynamic> speak(String text) {
print('Started speeking');
print(new DateTime.now().toIso8601String());
if (TextToSpeech.lastRequest == null) {
lastRequest = _executeSpeech(text);
} else {
lastRequest = lastRequest.then((resp) {
return _executeSpeech(text);
});
}
return lastRequest;
}
Future<dynamic> _executeSpeech(String text) {
completer = Completer();
flutterTts.speak(text).then((resp) {
ttsRunning = false;
print(resp);
return resp;
}, onError: (obj, st) {
ttsRunning = false;
print(obj);
print(st.toString());
});
return completer.future;
}
flutterTts.setCompletionHandler(() {
print('Finished speeking');
print(new DateTime.now().toIso8601String());
ttsState = TtsState.stopped;
completer.complete(ttsState);
});
flutterTts.setErrorHandler((msg) {
ttsState = TtsState.stopped;
completer.complete(ttsState);
});
If you don't want new messages interrupting those being spoken, you can queue them up. This way the new messages will wait for the current message to finish. Check out this approach:
Queue of Future in dart

Flutter method blocking, need to isolate

When I click a button and run some set state items and then call the send function, it waits until the send function is done before the set state items take effect. I have tried to make the called function async with await on the item that takes so much time BASE64.encode of image and/or video, but still it waits.
Looking for a way to not have this function block, someone mentioned isolate, but have no idea how to work that in and examples show how to work it in the entire application not just a long running function.
onPressed: () async {
setState(() {
submitting = true;
_imageFile = null;
TextInputAction.done;
});
await _sendReply();
},
Above is what I run on a ImageButton. The _sendReply is below.
_sendReply() async {
if (_newreplycontroller.text.isNotEmpty || myimagefile != null) {
//_doShowSubmitting();
DateTime dateSubmit = new DateTime.now();
if (myimagefile != null) {
if (isImage) {
List<int> imageBytes = myimagefile.readAsBytesSync();
myimage = await BASE64.encode(imageBytes);
myvideo = 'NONE';
}
if (isVideo) {
List<int> imageBytes = myvidfile.readAsBytesSync();
myvideo = await BASE64.encode(imageBytes);
myimage = 'NONE';
}
} else {
myimage = 'NONE';
myvideo = 'NONE';
}
var mymessage = _newreplycontroller.text;
ChatServerMessage mychat = new ChatServerMessage(
widget.mychat.msgkey,
'message',
widget.mychat.refid,
widget.mychat.referralname,
replysub,
oid,
oname,
pid,
pname,
sender,
sendname,
receiver,
receivename,
mymessage,
dateSubmit.toString(),
widget.mychat.grpid.toString(),
widget.mychat.prid.toString(),
myfcmtoken,
myimage,
myvideo,
myext);
_doSendReply(mychat);
} else {
}
}
From debugging I know all the time is spent on the BASE64.encode. Any ideas would be great.