Fix voice search problems.

This commit is contained in:
Erfan Rahmati
2022-05-06 20:13:26 +04:30
parent bfee22ce86
commit f5a58849bc
6 changed files with 125 additions and 116 deletions

View File

@ -33,8 +33,9 @@ class HomeNavbar extends StatelessWidget {
color: Colors.white, color: Colors.white,
size: 22.5, size: 22.5,
), ),
onPressed: () { onPressed: () async {
Get.find<MainController>().controller.jumpToTab(1); await Future.delayed(const Duration(milliseconds: 250));
Get.find<MainController>().changeIndex(1);
}, },
), ),
], ],

View File

@ -86,18 +86,17 @@ class SearchBar extends StatelessWidget {
), ),
), ),
_.fieldText == "" _.fieldText == ""
? GestureDetector( ? IconButton(
onTap: () async { splashColor: Colors.transparent,
// await _.speechToText.listen(onResult: _onSpeechResult); highlightColor: Colors.transparent,
// Get.find<SearchBarController>() onPressed: () async {
// .updateSpeechToText(listening: true);
showDialog( showDialog(
context: context, context: context,
builder: (context) { builder: (context) {
return const VoiceSearchAlertDialog(); return const VoiceSearchAlertDialog();
}); });
}, },
child: const Padding( icon: const Padding(
padding: EdgeInsets.only(right: 15, left: 5), padding: EdgeInsets.only(right: 15, left: 5),
child: Icon( child: Icon(
FontAwesomeIcons.microphoneAlt, FontAwesomeIcons.microphoneAlt,
@ -106,13 +105,15 @@ class SearchBar extends StatelessWidget {
), ),
), ),
) )
: GestureDetector( : IconButton(
onTap: () { splashColor: Colors.transparent,
highlightColor: Colors.transparent,
onPressed: () {
_.updateFieldState(tapped: false, text: ""); _.updateFieldState(tapped: false, text: "");
_.updateResult(result: null); _.updateResult(result: null);
_.controller.clear(); _.controller.clear();
}, },
child: const Padding( icon: const Padding(
padding: EdgeInsets.only(right: 15), padding: EdgeInsets.only(right: 15),
child: Icon( child: Icon(
Icons.close_rounded, Icons.close_rounded,

View File

@ -7,10 +7,6 @@ class SearchBarController extends GetxController {
bool fieldTapped = false; bool fieldTapped = false;
String fieldText = ""; String fieldText = "";
TextEditingController controller = TextEditingController(); TextEditingController controller = TextEditingController();
SpeechToText speechToText = SpeechToText();
String recognizedText = "";
bool speechEnabled = false;
bool isListening = false;
// ignore: avoid_init_to_null // ignore: avoid_init_to_null
List? result = null; List? result = null;
@ -21,13 +17,6 @@ class SearchBarController extends GetxController {
update(); update();
} }
updateSpeechToText({bool? enabled, bool? listening, String? recognized}) {
speechEnabled = enabled ?? speechEnabled;
isListening = listening ?? isListening;
recognizedText = recognized ?? recognizedText;
update();
}
updateResult({required result}) { updateResult({required result}) {
this.result = result; this.result = result;
update(); update();

View File

@ -4,110 +4,120 @@ import 'package:get/get.dart';
import 'package:movielab/constants/colors.dart'; import 'package:movielab/constants/colors.dart';
import 'package:movielab/modules/api_requester.dart'; import 'package:movielab/modules/api_requester.dart';
import 'package:movielab/pages/main/search/search_bar/search_bar_controller.dart'; import 'package:movielab/pages/main/search/search_bar/search_bar_controller.dart';
import 'package:speech_to_text/speech_recognition_result.dart'; import 'package:speech_to_text/speech_to_text.dart' as stt;
import 'package:speech_to_text/speech_to_text.dart'; import 'package:avatar_glow/avatar_glow.dart';
class VoiceSearchAlertDialog extends StatelessWidget { class VoiceSearchAlertDialog extends StatefulWidget {
const VoiceSearchAlertDialog({Key? key}) : super(key: key); const VoiceSearchAlertDialog({Key? key}) : super(key: key);
@override
State<VoiceSearchAlertDialog> createState() => _VoiceSearchAlertDialogState();
}
class _VoiceSearchAlertDialogState extends State<VoiceSearchAlertDialog> {
final stt.SpeechToText _speech = stt.SpeechToText();
bool _isListening = false;
double _confidence = 1.0;
TextEditingController controller = Get.find<SearchBarController>().controller;
String _text = "Tap to talk!";
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return StatefulBuilder(builder: (context, setState) { initState() {
TextEditingController controller = super.initState();
Get.find<SearchBarController>().controller; _listen();
double halfScreen = }
((MediaQuery.of(context).size.width.round().toDouble()) / 2) - 150;
return GetBuilder<SearchBarController>(
builder: (_) {
SpeechToText speechToText = _.speechToText;
_initSpeech() async {
_.speechEnabled = await _.speechToText.initialize();
}
_initSpeech(); double halfScreen =
Future<void> _onSpeechResult(SpeechRecognitionResult result) async { ((MediaQuery.of(context).size.width.round().toDouble()) / 2) - 150;
_.updateSpeechToText(recognized: result.recognizedWords); return Padding(
// _.controller.text = result.recognizedWords; padding: EdgeInsets.symmetric(horizontal: halfScreen),
//_.updateResult(result: []); child: AlertDialog(
//search(expression: result.recognizedWords); contentPadding: const EdgeInsets.all(5),
//await Future.delayed(const Duration(seconds: 1)); shape: RoundedRectangleBorder(
//_.updateSpeechToText(listening: false); borderRadius: BorderRadius.circular(20),
//Navigator.pop(context); ),
} content: Container(
height: 200.0,
void _stopListening() async { width: 200,
await _.speechToText.stop(); decoration: BoxDecoration(
setState(() {}); borderRadius: BorderRadius.circular(15), color: kBackgroundColor),
} child: Column(
mainAxisAlignment: MainAxisAlignment.center,
return Padding( children: [
padding: EdgeInsets.symmetric(horizontal: halfScreen), Padding(
child: AlertDialog( padding: const EdgeInsets.symmetric(vertical: 10),
contentPadding: const EdgeInsets.all(5), child: InkWell(
shape: RoundedRectangleBorder( onTap: _listen,
borderRadius: BorderRadius.circular(20), child: AvatarGlow(
), animate: _isListening,
content: Container( glowColor: kLightBlueColor,
height: 200.0, endRadius: 50.0,
width: 200, duration: const Duration(milliseconds: 2000),
decoration: BoxDecoration( repeatPauseDuration: const Duration(milliseconds: 100),
borderRadius: BorderRadius.circular(15), repeat: true,
color: kBackgroundColor), child: CircleAvatar(
child: Column( backgroundColor:
mainAxisAlignment: MainAxisAlignment.center, !_isListening ? Colors.white : kBlueColor,
children: [ radius: 35,
Padding( child: Icon(
padding: const EdgeInsets.symmetric(vertical: 20), FontAwesomeIcons.microphoneAlt,
child: InkWell( color: _isListening ? Colors.white : kBlueColor,
onTap: () async { size: 25,
if (_.isListening) {
_stopListening();
_.updateSpeechToText(listening: false);
} else {
await _.speechToText
.listen(onResult: _onSpeechResult);
_.updateSpeechToText(listening: true);
}
},
child: CircleAvatar(
backgroundColor:
!_.isListening ? Colors.white : kBlueColor,
radius: 35,
child: Icon(
FontAwesomeIcons.microphoneAlt,
color: _.isListening ? Colors.white : kBlueColor,
size: 25,
),
),
), ),
), ),
_.recognizedText != "" ),
? Text(
_.recognizedText,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w700),
)
: !_.isListening
? const Text(
"Tap to talk!",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w700),
)
: Text(
"Talk now!",
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.w700),
)
],
), ),
), ),
), Text(
); _text,
}, style: TextStyle(
color: _isListening
? kLightBlueColor.withOpacity(0.75)
: Colors.white,
fontWeight: FontWeight.w700),
)
],
),
),
),
);
}
void _listen() async {
if (!_isListening) {
bool available = await _speech.initialize(
onStatus: (val) => print('onStatus: $val'),
onError: (val) => print('onError: $val'),
); );
}); if (available) {
setState(() => _isListening = true);
setState(() => _text = "Listening...");
print('>>> Listening started...');
_speech.listen(
onResult: (val) => setState(() async {
_text = val.recognizedWords;
_confidence = val.confidence;
if (val.hasConfidenceRating && val.confidence > 0) {
SearchBarController _ = Get.find<SearchBarController>();
print('Word: $_text, Confidence: $_confidence');
_.controller.text = _text;
_.fieldText = _text;
await Future.delayed(const Duration(seconds: 1));
_.updateResult(result: []);
Navigator.pop(context);
search(expression: _text);
}
}),
);
}
} else {
setState(() => _isListening = false);
setState(() => _text = "Tap to talk!");
_speech.stop();
print('>>> Listening stopped.');
}
} }
} }

View File

@ -8,6 +8,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.8.2" version: "2.8.2"
avatar_glow:
dependency: "direct main"
description:
name: avatar_glow
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:

View File

@ -28,6 +28,7 @@ dependencies:
floating_action_bubble: ^1.1.4 floating_action_bubble: ^1.1.4
fab_circular_menu: ^1.0.2 fab_circular_menu: ^1.0.2
flutter_slidable: ^1.2.0 flutter_slidable: ^1.2.0
avatar_glow: ^2.0.2
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: