|
|
@ -1,6 +1,14 @@
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
|
|
|
import 'package:fluttertoast/fluttertoast.dart';
|
|
|
|
|
|
|
|
import 'package:provider/provider.dart';
|
|
|
|
|
|
|
|
import 'package:speech_to_text/speech_recognition_error.dart';
|
|
|
|
|
|
|
|
import 'package:speech_to_text/speech_recognition_result.dart';
|
|
|
|
|
|
|
|
import 'package:speech_to_text/speech_to_text.dart';
|
|
|
|
|
|
|
|
import 'package:test_sa/controllers/providers/settings/setting_provider.dart';
|
|
|
|
import 'package:test_sa/extensions/context_extension.dart';
|
|
|
|
import 'package:test_sa/extensions/context_extension.dart';
|
|
|
|
import 'package:test_sa/extensions/int_extensions.dart';
|
|
|
|
import 'package:test_sa/extensions/int_extensions.dart';
|
|
|
|
|
|
|
|
import 'package:test_sa/extensions/text_extensions.dart';
|
|
|
|
|
|
|
|
import 'package:test_sa/extensions/widget_extensions.dart';
|
|
|
|
import 'package:test_sa/new_views/app_style/app_color.dart';
|
|
|
|
import 'package:test_sa/new_views/app_style/app_color.dart';
|
|
|
|
import 'package:test_sa/new_views/app_style/app_text_style.dart';
|
|
|
|
import 'package:test_sa/new_views/app_style/app_text_style.dart';
|
|
|
|
|
|
|
|
|
|
|
@ -32,6 +40,7 @@ class AppTextFormField extends StatefulWidget {
|
|
|
|
final EdgeInsets? contentPadding;
|
|
|
|
final EdgeInsets? contentPadding;
|
|
|
|
final bool showWithoutDecoration;
|
|
|
|
final bool showWithoutDecoration;
|
|
|
|
final VoidCallback? onTap;
|
|
|
|
final VoidCallback? onTap;
|
|
|
|
|
|
|
|
final bool showSpeechToText;
|
|
|
|
|
|
|
|
|
|
|
|
const AppTextFormField({
|
|
|
|
const AppTextFormField({
|
|
|
|
Key? key,
|
|
|
|
Key? key,
|
|
|
@ -47,6 +56,7 @@ class AppTextFormField extends StatefulWidget {
|
|
|
|
this.textInputType = TextInputType.text,
|
|
|
|
this.textInputType = TextInputType.text,
|
|
|
|
this.initialValue, // Provide default value
|
|
|
|
this.initialValue, // Provide default value
|
|
|
|
this.enable = true,
|
|
|
|
this.enable = true,
|
|
|
|
|
|
|
|
this.showSpeechToText = false,
|
|
|
|
this.style,
|
|
|
|
this.style,
|
|
|
|
this.contentPadding,
|
|
|
|
this.contentPadding,
|
|
|
|
this.textAlign,
|
|
|
|
this.textAlign,
|
|
|
@ -69,24 +79,92 @@ class AppTextFormField extends StatefulWidget {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class _AppTextFormFieldState extends State<AppTextFormField> {
|
|
|
|
class _AppTextFormFieldState extends State<AppTextFormField> {
|
|
|
|
|
|
|
|
bool _speechEnabled = false;
|
|
|
|
|
|
|
|
final SpeechToText _speechToText = SpeechToText();
|
|
|
|
|
|
|
|
SettingProvider? _settingProvider;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
late FocusNode _focusNode;
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
void initState() {
|
|
|
|
if (widget.initialValue != null) {
|
|
|
|
if (widget.initialValue != null) {
|
|
|
|
widget.controller?.text = widget.initialValue!;
|
|
|
|
widget.controller?.text = widget.initialValue!;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_focusNode = widget.node ?? FocusNode();
|
|
|
|
|
|
|
|
_initSpeech();
|
|
|
|
super.initState();
|
|
|
|
super.initState();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void _initSpeech() async {
|
|
|
|
|
|
|
|
_speechEnabled = await _speechToText.initialize(
|
|
|
|
|
|
|
|
onError: (SpeechRecognitionError error) async {
|
|
|
|
|
|
|
|
Fluttertoast.showToast(msg: "failed to convert text to speech");
|
|
|
|
|
|
|
|
setState(() {});
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void _startListening() async {
|
|
|
|
|
|
|
|
_speechEnabled = _speechToText.isAvailable;
|
|
|
|
|
|
|
|
if (_speechToText.isListening) {
|
|
|
|
|
|
|
|
Fluttertoast.showToast(msg: "Currently in use");
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!_speechEnabled) return;
|
|
|
|
|
|
|
|
await _speechToText.listen(
|
|
|
|
|
|
|
|
onResult: (SpeechRecognitionResult result) {
|
|
|
|
|
|
|
|
widget.controller?.text = result.recognizedWords;
|
|
|
|
|
|
|
|
if (widget.onChange != null) widget.onChange!(result.recognizedWords);
|
|
|
|
|
|
|
|
widget.controller?.text = result.recognizedWords;
|
|
|
|
|
|
|
|
if (widget.onSaved != null) widget.onSaved!(result.recognizedWords);
|
|
|
|
|
|
|
|
_focusNode.unfocus();
|
|
|
|
|
|
|
|
setState(() {});
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
localeId: _settingProvider!.speechToText);
|
|
|
|
|
|
|
|
setState(() {});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void _stopListening() async {
|
|
|
|
|
|
|
|
await _speechToText.stop();
|
|
|
|
|
|
|
|
_focusNode.unfocus();
|
|
|
|
|
|
|
|
setState(() {});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
|
|
|
_settingProvider ??= Provider.of(context, listen: false);
|
|
|
|
final border = UnderlineInputBorder(
|
|
|
|
final border = UnderlineInputBorder(
|
|
|
|
borderSide: BorderSide.none,
|
|
|
|
borderSide: BorderSide.none,
|
|
|
|
borderRadius: BorderRadius.circular(10),
|
|
|
|
borderRadius: BorderRadius.circular(10),
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Widget speechToTextWidget = PopupMenuButton<String>(
|
|
|
|
|
|
|
|
child: SizedBox(
|
|
|
|
|
|
|
|
height: 24.toScreenHeight,
|
|
|
|
|
|
|
|
width: 24.toScreenWidth,
|
|
|
|
|
|
|
|
child: "speech_to_text".toSvgAsset(color: context.isDark ? AppColor.neutral10 : AppColor.neutral20, height: 24, width: 24),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
onSelected: (String selectedLanguage) {
|
|
|
|
|
|
|
|
_settingProvider!.setSpeechToText(selectedLanguage);
|
|
|
|
|
|
|
|
_startListening();
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
itemBuilder: (BuildContext context) {
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
|
|
|
const PopupMenuItem<String>(
|
|
|
|
|
|
|
|
value: 'ar',
|
|
|
|
|
|
|
|
child: Text('Arabic'),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
const PopupMenuItem<String>(
|
|
|
|
|
|
|
|
value: 'en',
|
|
|
|
|
|
|
|
child: Text('English'),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
final textField = TextFormField(
|
|
|
|
final textField = TextFormField(
|
|
|
|
focusNode: widget.node,
|
|
|
|
focusNode: _focusNode,
|
|
|
|
enabled: widget.enable,
|
|
|
|
enabled: widget.enable,
|
|
|
|
onSaved: (text) {
|
|
|
|
onSaved: (text) {
|
|
|
|
if (widget.onSaved != null && text != null) widget.onSaved!(text);
|
|
|
|
if (widget.onSaved != null && text != null) widget.onSaved!(text);
|
|
|
@ -110,10 +188,11 @@ class _AppTextFormFieldState extends State<AppTextFormField> {
|
|
|
|
focusedBorder: border,
|
|
|
|
focusedBorder: border,
|
|
|
|
enabledBorder: border,
|
|
|
|
enabledBorder: border,
|
|
|
|
errorBorder: border,
|
|
|
|
errorBorder: border,
|
|
|
|
|
|
|
|
|
|
|
|
contentPadding: widget.contentPadding ?? EdgeInsets.symmetric(vertical: 12.toScreenHeight, horizontal: 16.toScreenWidth),
|
|
|
|
contentPadding: widget.contentPadding ?? EdgeInsets.symmetric(vertical: 12.toScreenHeight, horizontal: 16.toScreenWidth),
|
|
|
|
constraints: const BoxConstraints(),
|
|
|
|
constraints: const BoxConstraints(),
|
|
|
|
suffixIconConstraints: const BoxConstraints(minWidth: 0),
|
|
|
|
suffixIconConstraints: const BoxConstraints(
|
|
|
|
|
|
|
|
minWidth: 0,
|
|
|
|
|
|
|
|
),
|
|
|
|
filled: true,
|
|
|
|
filled: true,
|
|
|
|
fillColor: widget.backgroundColor ??
|
|
|
|
fillColor: widget.backgroundColor ??
|
|
|
|
(context.isDark && !widget.enable
|
|
|
|
(context.isDark && !widget.enable
|
|
|
@ -124,7 +203,7 @@ class _AppTextFormFieldState extends State<AppTextFormField> {
|
|
|
|
errorStyle: AppTextStyle.tiny.copyWith(color: context.isDark ? AppColor.red50 : AppColor.red60),
|
|
|
|
errorStyle: AppTextStyle.tiny.copyWith(color: context.isDark ? AppColor.red50 : AppColor.red60),
|
|
|
|
floatingLabelStyle: AppTextStyle.body1.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? null : AppColor.neutral20),
|
|
|
|
floatingLabelStyle: AppTextStyle.body1.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? null : AppColor.neutral20),
|
|
|
|
hintText: widget.hintText ?? "",
|
|
|
|
hintText: widget.hintText ?? "",
|
|
|
|
labelText: widget.labelText ?? "",
|
|
|
|
labelText: (widget.showSpeechToText && _speechToText.isListening) ? "Listening..." : widget.labelText ?? "",
|
|
|
|
labelStyle: widget.labelStyle,
|
|
|
|
labelStyle: widget.labelStyle,
|
|
|
|
prefixIcon: widget.prefixIcon ??
|
|
|
|
prefixIcon: widget.prefixIcon ??
|
|
|
|
(widget.prefixIconData == null
|
|
|
|
(widget.prefixIconData == null
|
|
|
@ -134,17 +213,36 @@ class _AppTextFormFieldState extends State<AppTextFormField> {
|
|
|
|
size: widget.prefixIconSize == null ? 20.toScreenWidth : (widget.prefixIconSize! - 10).toScreenWidth,
|
|
|
|
size: widget.prefixIconSize == null ? 20.toScreenWidth : (widget.prefixIconSize! - 10).toScreenWidth,
|
|
|
|
color: AppColor.neutral70,
|
|
|
|
color: AppColor.neutral70,
|
|
|
|
)),
|
|
|
|
)),
|
|
|
|
suffixIcon: widget.suffixIcon,
|
|
|
|
suffixIcon: widget.suffixIcon ??
|
|
|
|
|
|
|
|
(widget.showSpeechToText
|
|
|
|
|
|
|
|
? (_speechToText.isListening
|
|
|
|
|
|
|
|
? SizedBox(
|
|
|
|
|
|
|
|
height: 24.toScreenHeight,
|
|
|
|
|
|
|
|
width: 24.toScreenWidth,
|
|
|
|
|
|
|
|
child: const Icon(
|
|
|
|
|
|
|
|
Icons.fiber_manual_record,
|
|
|
|
|
|
|
|
color: Colors.red,
|
|
|
|
|
|
|
|
)).onPress(() {
|
|
|
|
|
|
|
|
// _focusNode.unfocus();
|
|
|
|
|
|
|
|
_stopListening();
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
: speechToTextWidget)
|
|
|
|
|
|
|
|
.paddingOnly(end: 12, bottom: 50)
|
|
|
|
|
|
|
|
: null),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// Widget myWidget = Row(
|
|
|
|
|
|
|
|
// children: [textField.expanded, if (widget.showSpeechToText) speechToTextWidget],
|
|
|
|
|
|
|
|
// );
|
|
|
|
|
|
|
|
|
|
|
|
if (widget.showWithoutDecoration) return textField;
|
|
|
|
if (widget.showWithoutDecoration) return textField;
|
|
|
|
return Container(
|
|
|
|
return Container(
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
borderRadius: BorderRadius.circular(10),
|
|
|
|
borderRadius: BorderRadius.circular(10),
|
|
|
|
boxShadow: widget.showShadow ? [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10)] : null,
|
|
|
|
boxShadow: widget.showShadow ? [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10)] : null,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
child: textField,
|
|
|
|
child: textField);
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|