Estou ficando louco com speech_to_text
.
No meu aplicativo tenho vários formulários, cada um com vários arquivos TextFormFields
. Para cada entrada devo anexar um microfone que me permita transcrever o que é dito. Eu tinha implementado tudo com basic SpeechToText
, mas deu alguns problemas devido às diversas inicializações. Em seguida, modifiquei tudo mudando SpeechToTextProvider
e me inspirando neste código disponibilizado pelo plugin .
No entanto, não posso garantir que o texto falado seja transcrito palavra por palavra na entrada. Isso SpeechToText
é tratado através do onResult
parâmetro do evento listen, mas não está disponível no provedor!
Alguém pode me ajudar? Mil obrigados!
Aqui está o código de um formulário, para simplificar com apenas uma entrada:
class WeekForm extends StatefulWidget {
const WeekForm({required this.week, super.key});
final Week week;
@override
State<WeekForm> createState() => _WeekFormState();
}
class _WeekFormState extends State<WeekForm> {
Week get week => widget.week;
final _formKey = GlobalKey<FormState>(debugLabel: "WeekForm");
final TextEditingController _learnedController = TextEditingController();
String _lastTextChange = '';
void _setLastTextChange(inputKey, lastText) {
setState(() {
_lastTextChange = lastText;
});
}
void _saveLearn(week, appState) async {
if (_formKey.currentState!.validate()) {
week.learned = _learnedController.text;
// save data to Firebase
appState.setWeek(week);
}
}
@override
void initState() {
///fetched data from firebase showing on controller
super.initState();
_learnedController.text = week.learned ?? "";
_setLastTextChange("learned", _learnedController.text);
}
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// Add TextFormFields and ElevatedButton here.
Row(
children: [
Expanded(
child: TextFormField(
controller: _learnedController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
),
onChanged: (value) {
_setLastTextChange("learned", value);
},
),
),
SpeechProvider(
key: const ValueKey("learned"),
controller: _learnedController,
lastInputChange: _lastTextChange,
setLastInputChange: _setLastTextChange,
),
],
),
Consumer<ApplicationState>(
builder: (context, appState, _) => Align(
alignment: Alignment.bottomRight,
child: ElevatedButton(
onPressed: () => _saveLearn(week, appState),
child: const Text("Save"),
),
),
),
],
),
);
}
}
E aqui está o código para chamar SpeechToTextProvider
:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:speech_to_text/speech_to_text_provider.dart';
class SpeechProvider extends StatefulWidget {
const SpeechProvider({
super.key,
required this.controller,
required this.lastInputChange,
required this.setLastInputChange,
});
final TextEditingController controller;
final String lastInputChange;
final void Function(String, String) setLastInputChange;
@override
SpeechProviderState createState() => SpeechProviderState();
}
class SpeechProviderState extends State<SpeechProvider> {
String lastWords = '';
String lastStatus = '';
late SpeechToTextProvider speechProvider;
void startListening() {
if (speechProvider.isAvailable && speechProvider.isNotListening) {
lastWords = '';
speechProvider.listen(
// I want something like this!!! but this is available only in basic SpeechToText
onResult: resultListener,
);
}
}
void stopListening() {
if (speechProvider.isListening) {
speechProvider.stop();
widget.setLastInputChange((widget.key as ValueKey<dynamic>).value, widget.controller.text);
setState(() {});
}
}
/// This callback is invoked each time new recognition results are
/// available after `listen` is called.
void resultListener() {
var result = speechProvider.lastResult;
if (result!.finalResult) {
widget.setLastInputChange((widget.key as ValueKey<dynamic>).value, widget.controller.text);
} else {
setState(() {
lastWords = result.recognizedWords;
});
widget.controller.text = '${widget.lastInputChange} $lastWords';
}
}
@override
Widget build(BuildContext context) {
speechProvider = Provider.of<SpeechToTextProvider>(context);
if (speechProvider.isNotAvailable) {
return const Center(
child: Text('Speech recognition not available, no permission or not available on the device.'),
);
}
return MicrophoneWidget(speechProvider.isListening, startListening, stopListening, key: UniqueKey());
}
}
class MicrophoneWidget extends StatelessWidget {
const MicrophoneWidget(this.isListening, this.startListening, this.stopListening, {super.key});
final bool isListening;
final void Function() startListening;
final void Function() stopListening;
@override
Widget build(BuildContext context) {
return TapRegion(
onTapOutside: (tap) => isListening ? stopListening() : null,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: SizedBox(
child: FloatingActionButton(
onPressed: isListening ? stopListening : startListening,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(80.0)),
),
child: Icon(isListening ? Icons.mic : Icons.mic_off),
),
),
),
);
}
}
Com base na sua descrição, acho que você deseja encontrar uma maneira de obter um
SpeechRecognitionResult
sempre que houver um novo.SpeechToTextProvider
expõe umstream
getter que você pode ouvir e acessar o resultado do reconhecimento dos eventos que ele emite: