Wait for callback to complete in Dart / Flutter - flutter

Currently writing a flutter app using the flutter_tts library.
I have a list of sentences to read out, but currently having trouble waiting for the setCompletionHandler() to complete.
How can I wait for the setCompletionHandler() callback to complete before moving on to the next string? Currently, the TTS.speak() function finishes immediately with the while loop incrementing right away so it only reads out the last sentence in the list.
// code shortened for brevity
FlutterTts TTS;
TtsState ttsState = TtsState.stopped;
get isPlaying => ttsState == TtsState.playing;
get isStopped => ttsState == TtsState.stopped;
List<String> sentences = ['Hello, World', 'How are you?', 'The quick brown fox jumps over the lazy dog'];
#override
void initState() {
super.initState();
TTS = FlutterTts();
}
void readOutSentences(sentences) async {
int i = 0;
bool readCompleted = false;
while (i < sentences.length) {
readCompleted = await runSpeak(sentences[i].toString());
if (readCompleted)
i++;
}
}
Future<bool> runSpeak(String currentSentence) async {
TTS.setStartHandler(() {
setState(() {
ttsState = TtsState.playing;
});
});
TTS.setCompletionHandler(() {
setState(() {
ttsState = TtsState.stopped;
});
});
await TTS.speak(currentSentence);
return true;
}
readOutSentences(sentences);

Forgive about the setCompletionHandler)
You can use such async functions:
Future<void> _speak(String _text) async {
if (_text != null && _text.isNotEmpty) {
await flutterTts.awaitSpeakCompletion(true);
await flutterTts.speak(_text);
}
}
readAllSentencesList(List<String> allSentences) async {
for (int i=0; i<allSentences.length; i++){
await _speak(allSentences[i]);
}
}
Don't forget to use last flutter_tts library!

Set setCompletionHandler like following to speak all sentences of the list one by one.
List<String> sentences = ['Hello, World', 'How are you?', 'The quick brown fox jumps over the lazy dog']
int i = 0;
FlutterTts flutterTts = FlutterTts();
await flutterTts.speak(sentences[i]);
flutterTts.setCompletionHandler(() async {
if (i < sentences.length - 1) {
i++;
await flutterTts.speak(sentences[i]);
}
});

To wait for the callback use the await keyword:
await TTS.setCompletionHandler(() {
setState(() {
ttsState = TtsState.stopped;
});
});

Related

Flutter QR-code scanner rapidly scan issue, how to control the second scan?

Does anyone know how to adjust the QR code scanner speed? since the second scan is so quick,
source example is down below, you can download it add and run it in your Flutter project to test!
What are the differenceis are in the controller.scannedDataStream.listen() function. I posted down below:
Barcode? result;
var qrText = "";
bool isValid = false;
bool isExpired = false;
QRViewController? controller;
controller.scannedDataStream.listen((scanData) async {
log(scanData.code);
if (!scanData.code.contains('?key=')) {
setState(() {
qrText = "This is not a correct code";
isValid = false;
});
} else {
var key_hash = scanData.code.split('?key=');
log('hash: ' + key_hash[1]);
await decrypt(key_hash[1]).then((String result) async {
if (result == "") {
log('result: ' + result);
await controller.stopCamera();
setState(() {
qrText = result;
isValid = true;
isExpired = true;
});
}
else {
log('result: ' + result);
await controller.pauseCamera();
await controller.stopCamera();
setState(() {
qrText = result;
isValid = true;
isExpired = false;
});
}
});
}
});
https://pub.dev/packages/qr_code_scanner/example

Flutter in_app_purchase, show content after purchase

I take code from offical in_app_purchases documentation, purchases work correctly.
I have function that show paid content, and i need to run it after purchase done correctly, but i don't know where i need to put it, because i steel don't inderstand purchases 100% correctly.
this is my code
final InAppPurchase _inAppPurchase = InAppPurchase.instance;
final String _productID = '1d7ea644f690ffa';
bool _available = true;
List<ProductDetails> _products = [];
List<PurchaseDetails> _purchases = [];
StreamSubscription<List<PurchaseDetails>>? _subscription;
#override
void initState() {
final Stream<List<PurchaseDetails>> purchaseUpdated = _inAppPurchase.purchaseStream;
_subscription = purchaseUpdated.listen((purchaseDetailsList) {
setState(() {
_purchases.addAll(purchaseDetailsList);
_listenToPurchaseUpdated(purchaseDetailsList);
});
}, onDone: () {
_subscription!.cancel();
}, onError: (error) {
_subscription!.cancel();
});
_initialize();
super.initState();
}
#override
void dispose() {
_subscription!.cancel();
super.dispose();
}
void _initialize() async {
_available = await _inAppPurchase.isAvailable();
List<ProductDetails> products = await _getProducts(
productIds: Set<String>.from(
[_productID],
),
);
setState(() {
_products = products;
});
}
void _listenToPurchaseUpdated(List<PurchaseDetails> purchaseDetailsList) {
purchaseDetailsList.forEach((PurchaseDetails purchaseDetails) async {
switch (purchaseDetails.status) {
case PurchaseStatus.pending:
// _showPendingUI();
break;
case PurchaseStatus.purchased:
break;
case PurchaseStatus.restored:
// bool valid = await _verifyPurchase(purchaseDetails);
// if (!valid) {
// _handleInvalidPurchase(purchaseDetails);
// }
break;
case PurchaseStatus.error:
print(purchaseDetails.error!);
// _handleError(purchaseDetails.error!);
break;
default:
break;
}
if (purchaseDetails.pendingCompletePurchase) {
await _inAppPurchase.completePurchase(purchaseDetails);
CheckListModel.addPaidData(purchaseDetails.productID);
}
});
}
Future<List<ProductDetails>> _getProducts({required Set<String> productIds}) async {
ProductDetailsResponse response = await _inAppPurchase.queryProductDetails(productIds);
return response.productDetails;
}
void _subscribe({required ProductDetails product}) {
final PurchaseParam purchaseParam = PurchaseParam(productDetails: product);
_inAppPurchase.buyNonConsumable(
purchaseParam: purchaseParam,
);
}
_subscribe function start when user click on special button in ui
function that show paid contenct name is
CheckListModel.addPaidData(purchaseDetails.productID);
when function start it create file paid.paid in getApplicationDocumentsDirectory, if it doesn't exist, and add in it productID . That what must happen
Where i need to place this function?

Await and then usage issues when switching states

By default The text to speech engine uses an async call, this causes the main Dart thread to go on with whatever line of code comes next, this leads up to another setstate() call after the TTS speak function which reverts the animation state from Speaking to Idle almost instantly.
is there any way to avoid this issue?
I want it to work as follows:
setstate(() => _animation = 'Speaking')
wait until the TTS is finished speaking
setstate(() => _animation = 'Idle')
If I'm misunderstanding something, could you please point it out? in case this is how Dart works then if you have any workarounds that'd be great.
Simplified code for inspection:
void _speak(String sentence) async {
updateRiveRoot(_animation = 'Speak');
setState(() => _isSpeaking = true);
if (!_isSpeaking) {
await tts.awaitSpeakCompletion(true);
tts.speak(sentence).then((result) {
if (result != null) {
print("getting here");// not being preinted out
setState(() => _isListening = false);
setState(() => _isSpeaking = false);
updateRiveRoot(_animation = 'Idle');
}
});
} else {
setState(() => _isSpeaking = true);
}
print("isSpeaking is $_isSpeaking and animation is $_animation");
}
I have scrolled through the TTS documentation but couldn't find useful information on the problem I'm currently facing.
try to use .then,
void _speak(String sentence) async {
if (!_isSpeaking) {
setState(() => _animation = 'Speak');
setState(() => _isSpeaking = true);
tts.speak(sentence).then((result) {
if (result != null) {
_isListening = false;
_isSpeaking = false;
setState((){});
}
});
}
} else {
setState(() => _isSpeaking = false);
}
}

Flutter flutter_in_app_purchases subscription FlutterInAppPurchses.instance.getSubscriptions() is not retrieving any items for IAPItem

I'm trying to implement a renewable subscription in flutter using the flutter_in_app_purchases plugin. When I click on the screen that this is declared in, it goes through the initState() function and then gets to the initPlatformState() and goes through that successfully, but when it gets to the getProducts() function, it's returning an empty item list for the List items = FlutterInappPurchase.instance.getSubscriptions([productID]); call. I've added the monthly subscription in both the App Store Connect and Google Play Store and completed the tax forms. Any help would be appreciated.
List<IAPItem> _items = [];
static const String productID = 'monthly_subscription';
#override
void initState() {
super.initState();
print("IN INIT STATE");
initPlatformState();
}
Future<void> initPlatformState() async {
print("In init platform state");
// prepare
final bool available = await InAppPurchaseConnection.instance.isAvailable();
print(available);
var close = await FlutterInappPurchase.instance.endConnection;
var result = await FlutterInappPurchase.instance.initConnection;
print('result: $result');
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) {
print('In not mounded');
return;
}
// refresh items for android
/*try {
String msg = await FlutterInappPurchase.instance.consumeAllItems;
print('consumeAllItems: $msg');
} catch(e){
print(e.toString());
}*/
await _getProduct();
}
Future<Null> _getProduct() async {
print("In get products");
try {
List<IAPItem> items = await FlutterInappPurchase.instance.getSubscriptions([productID]);
print("Items is: $items");
for (var item in items) {
print('${item.toString()}');
this._items.add(item);
}
setState(() {
this._items = items;
});
} catch(e) {
print(e.toString());
}
}
Here you have a working example from app in production. Disclaimer: I'm not using it anymore but the last time I did it worked fine:
class _InAppState extends State<InApp> {
StreamSubscription _purchaseUpdatedSubscription;
StreamSubscription _purchaseErrorSubscription;
StreamSubscription _conectionSubscription;
final List<String> _productLists = Platform.isAndroid
? [
'subs_premium', 'subs_user'
]
: ['subs_premium', 'subs_boss', 'subscripcion_user'];
String _platformVersion = 'Unknown';
List<IAPItem> _items = [];
List<IAPItem> _subscripions = [];
List<PurchasedItem> _purchases = [];
#override
void initState() {
super.initState();
initPlatformState();
}
#override
void dispose() {
super.dispose();
if (_conectionSubscription != null) {
_conectionSubscription.cancel();
_conectionSubscription = null;
}
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
String platformVersion;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
platformVersion = await FlutterInappPurchase.instance.platformVersion;
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
// prepare
var result = await FlutterInappPurchase.instance.initConnection;
print('result: $result');
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_platformVersion = platformVersion;
});
// refresh items for android
try {
String msg = await FlutterInappPurchase.instance.consumeAllItems;
print('consumeAllItems: $msg');
} catch (err) {
print('consumeAllItems error: $err');
}
_conectionSubscription = FlutterInappPurchase.connectionUpdated.listen((connected) {
print('connected: $connected');
});
_purchaseUpdatedSubscription = FlutterInappPurchase.purchaseUpdated.listen((productItem) {
print('purchase-updated: $productItem');
});
_purchaseErrorSubscription = FlutterInappPurchase.purchaseError.listen((purchaseError) {
print('purchase-error: $purchaseError');
});
final List<String> _SKUS = widget.premium ? ['subs_boss']
: ['subs_user'] ;
_getSubscriptions(_SKUS);
}
void _requestPurchase(IAPItem item) {
FlutterInappPurchase.instance.requestPurchase(item.productId);
}
Future _getProduct() async {
print('TEST 1 HERE ${_productLists.length}, ${_productLists.first.toString()}');
List<IAPItem> items = await FlutterInappPurchase.instance.getProducts(_productLists);
print('TEST 2 HERE ${items.length}');
for (var item in items) {
print('${item.toString()}');
this._items.add(item);
}
setState(() {
this._items = items;
this._purchases = [];
});
}
Future _getPurchases() async {
List<PurchasedItem> items =
await FlutterInappPurchase.instance.getAvailablePurchases();
for (var item in items) {
print('${item.toString()}');
this._purchases.add(item);
}
setState(() {
this._items = [];
this._purchases = items;
});
}
Future _getSubscriptions(_SKUS) async {
List<IAPItem> items =
await FlutterInappPurchase.instance.getSubscriptions(_SKUS);
for (var item in items) {
print('${item.toString()}');
this._subscripions.add(item);
}
setState(() {
this._items = [];
this._subscripions = items;
});
}
Future _getPurchaseHistory() async {
List<PurchasedItem> items = await FlutterInappPurchase.instance.getPurchaseHistory();
for (var item in items) {
print('${item.toString()}');
this._purchases.add(item);
}
setState(() {
this._items = [];
this._purchases = items;
});
}

type 'future<dynamic>' is not a subtype of type 'function'

when i run my app in debug mode it shows me the error "type 'future' is not a subtype of type 'function'" all over the screen and also in the debug console. Can someone help me? I imagine it's a problem with async functions "reset","rateoGet" and "rateoSave" but i can't find any solution.
P.S. I've deleted part of the code because it was useless for this question.
int plus;
int min;
int per;
int div;
double val;
int gameswon =0;
int moves;
static int mosse=15;
String win = "gioca";
int games=0;
double rateo=1;
String mode;
int flag;
var timer=30;
#override
void initState() {
super.initState();
reset();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body:
MyButton(text: "$per" ,color: Colors.deepPurpleAccent, onTap: (){
setState(() {
val*=per;
});
if(widget.mode=="timermode" && flag==0){
timerceckresults();
}else if(widget.mode=="movesmode"){
checkResult();
}
},
MyBottomButton(text: "Reset",color: Colors.indigo,width:160, onTap: reset()),
),
}
checkResult() {
if(val == 101) {
print("hai vinto");
win="Hai Vinto";
setState(() {});
gameswon++;
Timer(Duration(seconds: 2), () {
reset();
});
} else {
print("ci sei quasi");
moves++;
mosse--;
win="$mosse moves left";
setState(() {});
if(moves>14){
print("hai perso coglione");
win="Hai Perso Coglione";
setState(() {});
Timer(Duration(seconds: 2), () {
reset();
});
}
}
}
timerceckresults(){
flag=1;
timer = 30;
Timer.periodic(Duration(seconds: 1), (t){
timer--;
setState(() {
win = "${timer.toString()}seconds left";
});
if(val==101){
timer=0;
}
if(timer == 0) {
t.cancel();
if(val == 101) {
win="Hai Vinto";
setState(() {});
gameswon++;
Timer(Duration(seconds: 2), () {
reset();
});
} else {
win="Hai Perso Coglione";
setState(() {});
Timer(Duration(seconds: 2), () {
reset();
});
}
}
});
static int randNum(x,y) {
var rng = new Random();
return rng.nextInt(y-x)+x;
}
reset() async{
timer=1;
plus = randNum(4, 9);
min = randNum(5, 19);
per = randNum(3, 9);
div = randNum(2, 5);
val = randNum(2, 11).toDouble();
moves = 0;
mosse=15;
if(widget.mode=="timermode"){
win="start playing";
}else{
win="$mosse moves left";
}
await rateoSave();
await rateoGet();
games++;
rateo=gameswon/(games-1);
await rateoSave();
flag=0;
setState(() {});
}
rateoSave() async {
SharedPreferences prefs=await SharedPreferences.getInstance();
await prefs.setInt("games",games);
await prefs.setInt("gameswon",gameswon);
}
rateoGet() async {
SharedPreferences prefs=await SharedPreferences.getInstance();
games=(prefs.getInt("games") ?? 0);
gameswon=(prefs.getInt("gameswon") ?? 0);
https://dart.dev/codelabs/async-await read this before you check the answer will help you alot
reset() async{
timer=1;
plus = randNum(4, 9);
min = randNum(5, 19);
per = randNum(3, 9);
div = randNum(2, 5);
val = randNum(2, 11).toDouble();
moves = 0;
mosse=15;
if(widget.mode=="timermode"){
win="start playing";
}else{
win="$mosse moves left";
}
await rateoSave();
await rateoGet();
games++;
rateo=gameswon/(games-1);
await rateoSave();
flag=0;
setState(() {});
}
Future<bool> rateoSave() {
SharedPreferences prefs= SharedPreferences.getInstance();
prefs.setInt("games",games);
prefs.setInt("gameswon",gameswon);
return true;
}
Future<bool> rateoGet() async {
SharedPreferences prefs= SharedPreferences.getInstance();
await games=(prefs.getInt("games") ?? 0);
await gameswon=(prefs.getInt("gameswon") ?? 0);
return true;
}
you are trying to get a variable from a method that returns a future. you need to add await just before you make the call to that function.
can you tell us in which line this error occurs ?
The most important thing to keep in mind is that if anything in your call-chain returns a Future, everything above it must deal with futures, either by returning the future itself (if no processing must be done), or await'ing and dealing with the returned value (but you'll still be returning a future).