Skip to content

FlutterTtsPlugin.onInitListenerWithoutCallback$lambda$5 #613

@abdalyosfi

Description

@abdalyosfi

🐛 Bug Report

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:253)
at android.speech.tts.TextToSpeech.lambda$dispatchOnInit$0(TextToSpeech.java:934)
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:943)
at android.speech.tts.TextToSpeech.-$$Nest$mdispatchOnInit()
at android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask.onPostExecute(TextToSpeech.java:2324)
at android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask.onPostExecute(TextToSpeech.java:2284)
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:249)
at android.os.Looper.loop(Looper.java:337)
at android.app.ActivityThread.main(ActivityThread.java:9604)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:615)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)

Expected behavior

should be works wihtout error

Reproduction steps

try it in huawei device

Image

Configuration


class ListenToArticleButton extends StatefulWidget {
  const ListenToArticleButton({
    super.key,
    required this.text,
    required this.isTablet,
    required this.isSoundPlaying,
    this.verticalMargin,
    this.hideOneNoMoreThanVoice = false,
    this.title,
    this.topWidget,
    this.customButton,
  });

  final String? title;
  final ValueNotifier<bool> 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<ListenToArticleButton> createState() => _ListenToArticleButtonState();
}

class _ListenToArticleButtonState extends State<ListenToArticleButton> {
  final ValueNotifier<bool> isLoading = ValueNotifier(false);
  late bool lastIsMute;
  late final AppLifecycleListener _listener;
  Map<String, String>? selectedVoid;
  late FlutterTts fluttertts;

  @override
  void initState() {
    fluttertts = getIt<FlutterTts>();
    _listener = AppLifecycleListener(
      onPause: () async {
        pauseByCloseApp = true;
        fluttertts.stop();

        // await fluttertts.pause();
        widget.isSoundPlaying.value = false;
      },
      onInactive: () async {
        pauseByCloseApp = true;
        // await fluttertts.pause();
        fluttertts.stop();
        widget.isSoundPlaying.value = false;
      },
      onHide: () async {
        pauseByCloseApp = true;
        // await fluttertts.pause();
        fluttertts.stop();

        widget.isSoundPlaying.value = false;
      },
      onStateChange: (value) => print("valuedasdsads $value"),
    );
    lastIsMute = isMute.value;
    setFlutterTtsHandler();
    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<void> getDefaultVoice(BuildContext context) async {
    if (!Platform.isAndroid && mounted && context.mounted) return;
    var soundCubit = context.read<DefaultSoundCubit>();

    var voice = await fluttertts.getDefaultVoice;
    if (voice != null) {
      if (soundCubit.state == null) {
        soundCubit.setVoice(Map<String, String>.from(voice));
      }
    }
  }

  bool pauseByCloseApp = false;

  void setFlutterTtsHandler() async {
    if (Platform.isAndroid) {
      ///limitation on android
      ///https://github.com/dlutton/flutter_tts/issues/66
      fluttertts.awaitSpeakCompletion(true);
    }
    fluttertts.setCompletionHandler(
      () {
        isLoading.value = false;
        if (!lastIsMute) isMute.value = false;
        widget.isSoundPlaying.value = false;
      },
    );
    fluttertts.setCancelHandler(
      () {
        isLoading.value = false;
        if (!lastIsMute) isMute.value = false;
        widget.isSoundPlaying.value = false;
      },
    );
    fluttertts.setErrorHandler(
      (e) {
        isLoading.value = false;
        if (!lastIsMute) isMute.value = false;
        showMessage(e.toString(), true);
        widget.isSoundPlaying.value = false;
      },
    );
    fluttertts.setStartHandler(
      () {
        isLoading.value = false;

        if (!lastIsMute) isMute.value = true;
        widget.isSoundPlaying.value = true;
      },
    );
    fluttertts.setPauseHandler(
      () {
        if (!lastIsMute) isMute.value = false;
        widget.isSoundPlaying.value = false;
      },
    );

    if (Platform.isAndroid) {
      try {
        if (hasServiceGoogle) {
          await fluttertts.setEngine('com.google.android.tts');
        }
      } catch (e, st) {
        getIt<CrashlyticsService>()
            .logFunctionError(e, st, functionName: "fluttertts/setEngine");
      }
    }
    // if (Platform.isAndroid) {
    //   _getDefaultEngine();
    //   _getDefaultVoice();
    // }
  }

  playSound(bool playing) async {
    getIt<InAppPipCubit>().pause();
    var soundCubit = context.read<DefaultSoundCubit>();
    pauseByCloseApp = false;
    if (playing) {
      fluttertts.stop();
    } else {
      bool isInstalled = Platform.isAndroid
          ? await fluttertts.isLanguageInstalled("ar")
          : true;
      // bool isInstalled = await fluttertts.isLanguageInstalled("ar");
      if (isInstalled) {
        await fluttertts.setLanguage("ar");
        isLoading.value = true;
        if (soundCubit.state != null &&
            (soundCubit.state?.isNotEmpty ?? false)) {
          try {
            await fluttertts.setVoice(soundCubit.state!);
          } catch (e, s) {
            getIt<CrashlyticsService>().logFunctionError(e, s,
                functionName: "listen_to_article_button/playSound/setVoice");
          }
        }
        var text = widget.text ?? "";
        if (Platform.isAndroid) {
          ///limitation on android
          ///https://github.com/dlutton/flutter_tts/issues/66
          var count = text.length;
          var max = await fluttertts.getMaxSpeechInputLength ?? 100;
          var loopCount = count ~/ max;

          for (var i = 0; i <= loopCount; i++) {
            if (i != loopCount) {
              if (!mounted) return;
              if (!pauseByCloseApp) {
                await fluttertts.speak(text.substring(i * max, (i + 1) * max));
              }
            } else {
              if (!mounted) return;
              var end = (count - ((i * max)) + (i * max));
              if (!pauseByCloseApp) {
                await fluttertts.speak(text.substring(i * max, end));
              }
            }
          }
        } else {
          if (!pauseByCloseApp) {
            await fluttertts.speak(widget.text ?? "");
          }
        }
      } else {
        if (Platform.isAndroid) {
          if (mounted) {
            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("إلغاء")),
                      ],
                    ));
          }
        }
      }
    }
    getIt<AnalyticsService>().logCustomEvent("listen_to_article", {});
  }

  @override
  Widget build(BuildContext context) {
    if (widget.text?.isEmpty ?? true) return SizedBox.shrink();
    if (widget.hideOneNoMoreThanVoice) {
      if ((!((voiceList?.length ?? 0) > 1)) || kDebugMode) return SizedBox();
    }
    final button = AppOfflineTracker(
      refresh: (_) {},
      child: (isConnected) => FutureBuilder(
          future: Platform.isAndroid
              ? fluttertts.isLanguageInstalled("ar")
              : Future.value(true),
          builder: (context, snapshot) {
            if (snapshot.connectionState == ConnectionState.done &&
                snapshot.data == false &&
                !isConnected) return SizedBox.shrink();
            return ValueListenableBuilder(
                valueListenable: widget.isSoundPlaying,
                builder: (context, playing, _) {
                  return AnimatedSize(
                    duration: Duration(milliseconds: 1000),
                    child: GestureDetector(
                      onTap: () async {
                        playSound(playing);
                        // if (StorageService.isEmailTest) {
                        //   List<dynamic> voiceList = await fluttertts.getVoices;
                        //   if (playing) {
                        //     await fluttertts.stop();
                        //   } else {
                        //     // final d = await 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) {
                        //                           fluttertts.stop();
                        //                         } else {
                        //                           await fluttertts.clearVoice();
                        //                           await fluttertts.setVoice(
                        //                               Map<String, String>.from(e));
                        //                           selectedVoid =
                        //                               Map<String, String>.from(e);
                        //
                        //                           ///TODO check if en or arabic
                        //                           // var result = await fluttertts
                        //                           //     .isLanguageAvailable("ar");
                        //                           // log(result.toString());
                        //                           // await fluttertts.setLanguage("ar");
                        //                           // await fluttertts.setLanguage("en");
                        //                           var text = widget.text ?? "";
                        //                           context.pop();
                        //                           if (Platform.isAndroid) {
                        //                             ///limitation on android
                        //                             ///https://github.com/dlutton/flutter_tts/issues/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 fluttertts.speak(
                        //                                       text.substring(i * max,
                        //                                           (i + 1) * max));
                        //                               } else {
                        //                                 if (!mounted) return;
                        //                                 var end = (count -
                        //                                     ((i * max)) +
                        //                                     (i * max));
                        //                                 if (!pauseByCloseApp)
                        //                                   await fluttertts.speak(text
                        //                                       .substring(i * max, end));
                        //                               }
                        //                             }
                        //                           } else {
                        //                             if (!pauseByCloseApp)
                        //                               await 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 {
                                            var soundCubit = context
                                                .read<DefaultSoundCubit>();
                                            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) {
                                                return BlocProvider.value(
                                                  value: soundCubit,
                                                  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 = soundCubit
                                                                            .state
                                                                            .toString() ==
                                                                        e.toString();
                                                                    return Column(
                                                                      crossAxisAlignment:
                                                                          CrossAxisAlignment
                                                                              .center,
                                                                      children: [
                                                                        GestureDetector(
                                                                          onTap:
                                                                              () async {
                                                                            soundCubit.setVoice(Map<String,
                                                                                String>.from(e));
                                                                            await 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,
                                                                        )
                                                                      ],
                                                                    );
                                                                  }
                                                                },
                                                              )
                                                            ],
                                                          ),
                                                        ),
                                                      );
                                                    },
                                                  ),
                                                );
                                              },
                                            );
                                            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() {
    fluttertts.stop();
    // fluttertts.clearVoice();
    _listener.dispose();
    super.dispose();
  }
}

Version:
flutter_tts: ^4.2.3
Platform:

  • 📱 iOS
  • 🤖 Android

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions