mirror of
https://github.com/ErfanRht/MovieLab.git
synced 2025-08-06 03:19:43 +08:00
Fix voice search problems.
This commit is contained in:
@ -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);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -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,
|
||||||
|
@ -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();
|
||||||
|
@ -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.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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:
|
||||||
|
@ -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:
|
||||||
|
Reference in New Issue
Block a user