-
-
Notifications
You must be signed in to change notification settings - Fork 315
Description
🐛 Bug Report
FlutterTtsPlugin.onInitListenerWithoutCallback$lambda$5
Fatal Exception: java.util.ConcurrentModificationException:
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1111)
at java.util.ArrayList$Itr.next(ArrayList.java:1064)
at com.tundralabs.fluttertts.FlutterTtsPlugin.onInitListenerWithoutCallback$lambda$5(FlutterTtsPlugin.java:32)
at android.speech.tts.TextToSpeech.lambda$dispatchOnInit$0(TextToSpeech.java:973)
at android.speech.tts.TextToSpeech.$r8$lambda$y-uaMPlobiYouiY_T7xFoJSwPww()
at android.speech.tts.TextToSpeech$$ExternalSyntheticLambda13.run(D8$$SyntheticClass)
at android.speech.tts.TextToSpeech.dispatchOnInit(TextToSpeech.java:982)
at android.speech.tts.TextToSpeech.-$$Nest$mdispatchOnInit()
at android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask.onPostExecute(TextToSpeech.java:2378)
at android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask.onPostExecute(TextToSpeech.java:2338)
at android.os.AsyncTask.finish(AsyncTask.java:771)
at android.os.AsyncTask.-$$Nest$mfinish()
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:788)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loopOnce(Looper.java:257)
at android.os.Looper.loop(Looper.java:342)
at android.app.ActivityThread.main(ActivityThread.java:9638)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:619)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:929)
Expected behavior
should be work without exception
Reproduction steps
try the widget i am using and try to read text
Configuration
class ListenToArticleButton extends StatefulWidget {
const ListenToArticleButton({
super.key,
required this.text,
required this.isTablet,
required this.flutterTts,
required this.isSoundPlaying,
this.verticalMargin,
this.hideOneNoMoreThanVoice = false,
this.title,
this.topWidget,
this.customButton,
});
final String? title;
final FlutterTts flutterTts;
final ValueNotifier isSoundPlaying;
////needed in settings to prevent user select default sound if no sounds
final bool hideOneNoMoreThanVoice;
final bool isTablet;
final double? verticalMargin;
final Function(bool playing)? customButton;
final String? text;
final Widget? topWidget;
@OverRide
State createState() => _ListenToArticleButtonState();
}
class _ListenToArticleButtonState extends State {
final ValueNotifier isLoading = ValueNotifier(false);
late bool lastIsMute;
late final AppLifecycleListener _listener;
Map<String, String>? selectedVoid;
@OverRide
void initState() {
_listener = AppLifecycleListener(
onPause: () async {
pauseByCloseApp = true;
widget.flutterTts.stop();
// await widget.flutterTts.pause();
widget.isSoundPlaying.value = false;
},
onInactive: () async {
pauseByCloseApp = true;
// await widget.flutterTts.pause();
widget.flutterTts.stop();
widget.isSoundPlaying.value = false;
},
onHide: () async {
pauseByCloseApp = true;
// await widget.flutterTts.pause();
widget.flutterTts.stop();
widget.isSoundPlaying.value = false;
},
onStateChange: (value) => print("valuedasdsads $value"),
);
lastIsMute = isMute.value;
setFlutterTtsHandler();
widget.flutterTts.getVoices.then(
(value) {
WidgetsBinding.instance.addPostFrameCallback(
(timeStamp) {
if (mounted && context.mounted) {
getDefaultVoice(context);
}
// final filtered = (value ?? [])
// .where((element) =>
// // (element is Map<String, dynamic>) &&
// (element['locale'] as String?)?.contains('ar') == true)
// .map((e) => Map<String, String>.from(e as Map));
//
// voiceList = filtered.toList();
voiceList = List.of((value ?? []).where(
(element) =>
(element is Map) &&
((element['locale'] as String?)?.contains("ar") ?? false),
))
.map(
(e) => e == null ? null : Map<String, String>.from(e),
)
.nonNulls
.toList();
if (mounted) setState(() {});
},
);
},
);
super.initState();
}
List<Map<String, String>>? voiceList;
Future getDefaultVoice(BuildContext context) async {
if (!Platform.isAndroid && mounted && context.mounted) return;
var soundCubit = context.read();
var voice = await widget.flutterTts.getDefaultVoice;
if (voice != null) {
if (soundCubit.state == null) {
soundCubit.setVoice(Map<String, String>.from(voice));
}
}
}
bool pauseByCloseApp = false;
void setFlutterTtsHandler() {
if (Platform.isAndroid) {
///limitation on android
///#66
widget.flutterTts.awaitSpeakCompletion(true);
}
widget.flutterTts.setCompletionHandler(
() {
isLoading.value = false;
if (!lastIsMute) isMute.value = false;
widget.isSoundPlaying.value = false;
},
);
widget.flutterTts.setCancelHandler(
() {
isLoading.value = false;
if (!lastIsMute) isMute.value = false;
widget.isSoundPlaying.value = false;
},
);
widget.flutterTts.setErrorHandler(
(e) {
isLoading.value = false;
if (!lastIsMute) isMute.value = false;
showMessage(e.toString(), true);
widget.isSoundPlaying.value = false;
},
);
widget.flutterTts.setStartHandler(
() {
isLoading.value = false;
if (!lastIsMute) isMute.value = true;
widget.isSoundPlaying.value = true;
},
);
widget.flutterTts.setPauseHandler(
() {
if (!lastIsMute) isMute.value = false;
widget.isSoundPlaying.value = false;
},
);
if (Platform.isAndroid) {
widget.flutterTts.setEngine('com.google.android.tts');
}
// if (Platform.isAndroid) {
// _getDefaultEngine();
// _getDefaultVoice();
// }
}
playSound(bool playing) async {
var soundCubit = context.read();
pauseByCloseApp = false;
if (playing) {
widget.flutterTts.stop();
} else {
bool isInstalled = Platform.isAndroid
? await widget.flutterTts.isLanguageInstalled("ar")
: true;
// bool isInstalled = await widget.flutterTts.isLanguageInstalled("ar");
if (isInstalled) {
await widget.flutterTts.setLanguage("ar");
isLoading.value = true;
if (soundCubit.state != null &&
(soundCubit.state?.isNotEmpty ?? false)) {
try {
await widget.flutterTts.setVoice(soundCubit.state!);
} catch (e, s) {
getIt().logFunctionError(e, s,
functionName: "listen_to_article_button/playSound/setVoice");
}
}
var text = widget.text ?? "";
if (Platform.isAndroid) {
///limitation on android
///#66
var count = text.length;
var max = await widget.flutterTts.getMaxSpeechInputLength ?? 100;
var loopCount = count ~/ max;
for (var i = 0; i <= loopCount; i++) {
if (i != loopCount) {
if (!mounted) return;
if (!pauseByCloseApp) {
await widget.flutterTts
.speak(text.substring(i * max, (i + 1) * max));
}
} else {
if (!mounted) return;
var end = (count - ((i * max)) + (i * max));
if (!pauseByCloseApp) {
await widget.flutterTts.speak(text.substring(i * max, end));
}
}
}
} else {
if (!pauseByCloseApp) {
await widget.flutterTts.speak(widget.text ?? "");
}
}
} else {
if (Platform.isAndroid) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: AppText("اللغة غير مثبتة على جهازك"),
content: AppText.style6(
"الرجاء تنزيل اللغة العربية حتى تتمكن من الاستماع إلى الأخبار"),
actionsAlignment: MainAxisAlignment.start,
actions: [
TextButton(
onPressed: () {
TtsInstaller.installVoiceData();
context.pop();
},
child: AppText("تنزيل")),
TextButton(
onPressed: () {
context.pop();
},
child: AppText("إلغاء")),
],
));
}
}
}
}
@OverRide
Widget build(BuildContext context) {
if (widget.hideOneNoMoreThanVoice) {
if ((!((voiceList?.length ?? 0) > 1)) || kDebugMode) return SizedBox();
}
final button = ValueListenableBuilder(
valueListenable: widget.isSoundPlaying,
builder: (context, playing, _) {
return AnimatedSize(
duration: Duration(milliseconds: 1000),
child: GestureDetector(
onTap: () async {
playSound(playing);
// if (StorageService.isEmailTest) {
// List voiceList = await widget.flutterTts.getVoices;
// if (playing) {
// await widget.flutterTts.stop();
// } else {
// // final d = await widget.flutterTts.getDefaultVoice;
// // print(d);
// showModalBottomSheet(
// context: context,
// builder: (context) {
// return SafeArea(
// child: SingleChildScrollView(
// child: Column(
// children: voiceList
// .where(
// (element) =>
// (element['locale'] as String?)
// ?.contains("ar") ??
// false,
// )
// .map(
// (e) => GestureDetector(
// onTap: () async {
// pauseByCloseApp = false;
// if (playing) {
// widget.flutterTts.stop();
// } else {
// await widget.flutterTts.clearVoice();
// await widget.flutterTts.setVoice(
// Map<String, String>.from(e));
// selectedVoid =
// Map<String, String>.from(e);
//
// ///TODO check if en or arabic
// // var result = await widget.flutterTts
// // .isLanguageAvailable("ar");
// // log(result.toString());
// // await widget.flutterTts.setLanguage("ar");
// // await widget.flutterTts.setLanguage("en");
// var text = widget.text ?? "";
// context.pop();
// if (Platform.isAndroid) {
// ///limitation on android
// ///#66
// var count = text.length;
// var max = 4000;
// var loopCount = count ~/ max;
//
// for (var i = 0;
// i <= loopCount;
// i++) {
// if (i != loopCount) {
// if (!mounted) return;
// if (!pauseByCloseApp)
// await widget.flutterTts.speak(
// text.substring(i * max,
// (i + 1) * max));
// } else {
// if (!mounted) return;
// var end = (count -
// ((i * max)) +
// (i * max));
// if (!pauseByCloseApp)
// await widget.flutterTts.speak(text
// .substring(i * max, end));
// }
// }
// } else {
// if (!pauseByCloseApp)
// await widget.flutterTts
// .speak(widget.text ?? "");
// }
// }
// },
// onLongPress: () {
// Clipboard.setData(
// ClipboardData(text: e.toString()));
// },
// child: Padding(
// padding: const EdgeInsets.all(8.0),
// child: AppText.style1(
// "$e",
// textAlign: TextAlign.center,
// style: TextStyle(
// color: selectedVoid.toString() ==
// Map<String, String>.from(
// e)
// .toString()
// ? Colors.red
// : null,
// fontWeight: selectedVoid == e
// ? FontWeight.bold
// : FontWeight.w400),
// ),
// ),
// ),
// )
// .toList(),
// ),
// ),
// );
// },
// );
// }
// } else {
},
child: widget.customButton != null
? widget.customButton!(playing)
: Align(
alignment: Alignment.center,
child: Container(
margin: EdgeInsets.symmetric(
vertical: widget.verticalMargin ?? 20),
padding:
EdgeInsets.symmetric(vertical: 6, horizontal: 20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: context.colorScheme.grey10),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if ((voiceList?.length ?? 0) > 1)
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () async {
bool isTablet =
HelperFunction.isTablet(context);
await showModalBottomSheet(
context: context,
showDragHandle: true,
// isScrollControlled: false,
backgroundColor: Colors.white,
constraints: BoxConstraints(
maxHeight: 0.9.sh, minWidth: 1.sw),
builder: (xtx) => BlocProvider.value(
value:
context.read<DefaultSoundCubit>(),
child: BlocBuilder<DefaultSoundCubit,
Map<String, String>?>(
builder: (context, state) {
return SizedBox(
width: 1.sw,
child: SingleChildScrollView(
child: Column(
children: [
// HeaderBottomSheet(),
AppText.style10(
"قم باختيار الصوت الافتراضي",
textAlign:
TextAlign.center,
color: Colors.black,
),
40.verticalSpace,
...?voiceList?.mapIndexed(
(index, e) {
{
var isSelected = context
.read<
DefaultSoundCubit>()
.state
.toString() ==
e.toString();
return Column(
crossAxisAlignment:
CrossAxisAlignment
.center,
children: [
GestureDetector(
onTap:
() async {
context
.read<
DefaultSoundCubit>()
.setVoice(Map<
String,
String>.from(e));
await widget
.flutterTts
.stop();
playSound(
false);
// if (isSelected) {
// searchFilters.value = List.of(searchFilters.value)
// ..removeWhere((e) => e.index == element.index);
// if (searchFilters.value.isEmpty) {
// searchFilters.value = SearchFilter.values;
// }
//
//
// }
},
child: Column(
children: [
AppText
.style6(
"Voice $index",
style: isSelected
? (isTablet
? AppTextStyles.textStyle1Tablet
: AppTextStyles.textStyle1)
: null,
textAlign:
TextAlign
.center,
// color: isSelected
// ? context
// .colorScheme
// .mainRedColor1
// : Colors
// .black,
),
],
),
),
NewsDivider(
isTablet:
isTablet,
height: 30,
)
],
);
}
},
)
],
),
),
);
},
),
),
);
widget.flutterTts.stop();
},
child: Container(
width: widget.isTablet ? 30.r : 30,
height: widget.isTablet ? 30.r : 30,
// decoration: BoxDecoration(
// shape: BoxShape.circle,
// color: context.colorScheme.mainRedColor,
// ),
margin: const EdgeInsets.only(
left: 8,
),
child:
AppImage.asset(Assets.iconsEditSound),
)),
AppText.style6(
widget.title ??
(playing
? "يتم الاستماع الى نص المقال"
: "استمع إلى نص المقال"),
color: context.colorScheme.fontColor1,
),
8.horizontalSpace,
ValueListenableBuilder(
valueListenable: isLoading,
builder: (context, _, __) {
return (isLoading.value)
? SizedBox.square(
dimension:
widget.isTablet ? 30.r : 30,
child: Center(
child: AppLoading(
dimension:
widget.isTablet ? 20.r : 20,
),
),
)
: Container(
width: widget.isTablet ? 30.r : 30,
height: widget.isTablet ? 30.r : 30,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: context
.colorScheme.mainRedColor),
child: Center(
child: AppImage.asset(playing
? Assets.iconsPause
: Assets.iconsHeadphones),
),
);
}),
],
),
),
),
),
);
});
if (widget.topWidget != null) {
return Column(
children: [
widget.topWidget!,
button,
],
);
}
return button;
}
@OverRide
void dispose() {
widget.flutterTts.stop();
// widget.flutterTts.clearVoice();
_listener.dispose();
super.dispose();
}
}
Version: 0.1.x
flutter_tts: ^4.2.3
Platform:
- 📱 iOS
- [yes ] 🤖 Android