folks.....how could I use Speech To Text Provider so I can use it multiple times in multiple widgets? I read speech to text could only be started once per app session....
I know I need to use it as a provider class but I don´t know how..
I tried speech to text but It just doesn´t work for me since I need to use it in multiple widgets and I want to be able to use the callbacks "onstatus" and "onerror"
I don´t know what else to say since it is asking me to type a certain number of letters
initialize speech to text in the root/first class.
final SpeechToText speech = SpeechToText();
late SpeechToTextProvider speechProvider;
in the initState, initialize the speechProvider and pass the speech instance to it.
speechProvider = SpeechToTextProvider(speech);
await speechProvider.initialize(); // do it in any other method and call it from initState.
in the root widget, add the ChangeNotifierProvider and pass the SpeechToTextProvider class.
ChangeNotifierProvider<SpeechToTextProvider>.value(
value: speechProvider,
child: ///
now you can use speechToTextProvider anyWhere in your project.
now in order to use it anywhere in your project you have to initilaize the speechToTextProvider in the class where you want to use it.
late SpeechToTextProvider speechToTextProvider;
speechToTextProvider = Provider.of<SpeechToTextProvider>(context); // do it in a didChangeDependency or any other method and call it from initState.
now in order to start listening
speechToTextProvider.listen(
pauseFor: Duration(seconds: 5),
localeId: 'en-us',
listenFor: Duration(seconds: 5));
StreamSubscription<SpeechRecognitionEvent> subscription =
speechToTextProvider.stream.listen(
(event) {
// on every change it update
if (event.eventType ==
SpeechRecognitionEventType.partialRecognitionEvent) {
if (mounted) {
_setState!(
() {
toDisplayText =
speechToTextProvider.lastResult!.recognizedWords; // the textField or Text Widget Will be updated
},
);
}
}
//on error
else if (event.eventType == SpeechRecognitionEventType.errorEvent) {
///on error if some error occurs then close the dilog box here . or stop the listner
}
}
//on done
else if (event.eventType ==
SpeechRecognitionEventType.finalRecognitionEvent) {
//when the user stop speaking.
}
},
);
}
and in that way you can use it in any class or widget. You have to repeat the process of start listening in other class.
Related
For a simple Email login with OTP code I have a structure as follows.
View
await _signUpCntrl.signUp(email, password);
Controller
_showOtpDialog(email);
_showOtpDialog func
return Get.dialog(
AlertDialog(
So the thing is _showOtpDialog function is inside a controller file. ie. /Controllers/controller_file.dart
I want do something like a blocListener, call the _showOtpDialog from a screen(view) file on signup success. (also relocate the _showOtpDialog to a view file)
Using GetX I have to use one of the builders either obs or getbuilder. Which is I think not a good approach to show a dialog box.
On internet it says Workers are the alternative to BlocListener. However Workers function resides on Controller file and with that the dialog is still being called on the controller file.
As OTP dialog will have its own state and a controller I wanted to put it inside a /view/viewfile.dart
How do I obtain this?
I tried using StateMixin but when I call Get.dialog() it throw an error.
visitChildElements() called during build
Unlike BLoC there's no BlocListener or BlocConsumer in GetX.
Instead GetX has RxWorkers. You can store your response object in a Rx variable:
class SomeController extends GetxController{
final response= Rxn<SomeResponse>();
Future<void> someMethod()async{
response.value = await someApiCall();
}
}
And then right before the return of your widget's build method:
class SomeWidget extends StatelessWidget{
final controller = Get.put(SomeController());
#override
Widget build(BuildContext context){
ever(controller.response, (SomeResponse res){
if(res.success){
return Get.dialog(SuccessDialog()); //Or snackbar, or navigate to another page
}
....
});
return UI();
}
First thing, you will need to enhance the quality of your question by making things more clearly. Add the code block and the number list, highlight those and making emphasize texts are bold. Use the code block instead of quote.
Seconds things, Depends on the state management you are using, we will have different approaches:
Bloc (As you already added to the question tag). By using this state management, you controller ( business logic handler) will act like the view model in the MVVM architecture. In terms of that, You will need to emit a state (e.g: Sent success event). Afterward, the UI will listen to the changes and update it value according to the event you have emitted. See this Bloc example
GetX (As your code and question pointed out): GetX will acts a little bit different. you have multiple ways to implement this:
Using callbacks (passed at the start when calling the send otp function)
Declare a general dialog for your application ( this is the most used when it comes to realization) and calling show Dialog from Bloc
Using Rx. You will define a Reactive Variable for e.g final success = RxBool(true). Then the view will listen and update whenever the success changes.
controller.dart
class MyController extends GetxController {
final success = RxBool(false);
void sendOtp() async {
final result = await repository.sendOTP();
success.update((val) => {true});
}
}
view.dart
class MyUI extends GetView<MyController> {
#override
Widget build(BuildContext context) {
ever(controller.success, (bool success) {
// This will update things whenever success is updated
if (success) {
Get.dialog(AlertDialog());
}
});
return Container();
}
}
I am wondering if it is possible to watch for a value change inside an initState which then simply calls a function?
I basically need to start and stop a timer given the status of a class i am observing with Provider (listen: true) and was hoping there was some callback functionality that i could trigger instead of build() being called each time?
For example something like..
void initState() {
super.initState();
Provider.of<MyService>(context, listen: true).serviceRunning => {
//do stuff (start/stop my local timer)
if(serviceRunning) {
serviceRunning()
} else {
serviceStopped()
}
}
}
void serviceRunning() {
//start local timer and other bits
}
void serviceStopped() {
//stop local timer and other bits
}
Widget build(BuildContext context) {
..
}
I don't recall Provider being able to do this, so would appreciate any suggestions on how to achieve this. As mentioned above, i am just trying to save having build() get called unnecessarily.
Not a provider user here (bloc fan) but you should be able to do what you want with a ValueNotifier exposed by your service. And in your initState you would add a listener to the notifier.
And I believe you can avoid build call by using context.select<T, R>(R cb(T value)), which allows a widget to listen to only a small part of T.
Extract of the provider doc:
Widget build(BuildContext context) {
final name = context.select((Person p) => p.name);
return Text(name);
}
But I'm not sure that this is necessary.
If you find a cleaner solution please share it, it's always nice learning what works in other part of the flutter community.
I have this ViewModel and a Riverpod provider for it:
final signInViewModelProvider = Provider.autoDispose<SignInViewModel>((ref) {
final vm = SignInViewModel();
ref.onDispose(() {
vm.cleanUp();
});
return vm;
});
class SignInViewModel extends VpViewModelNew {
FormGroup get form => _form;
String get emailKey => _emailKey;
String get passwordKey => _passwordKey;
final String _emailKey = UserSignInFieldKeys.email;
final String _passwordKey = UserSignInFieldKeys.password;
final FormGroup _form = FormGroup({
UserSignInFieldKeys.email:
FormControl<String>(validators: [Validators.required]),
UserSignInFieldKeys.password:
FormControl<String>(validators: [Validators.required])
});
void cleanUp() {
print('cleaning up');
}
void onSubmitPressed(BuildContext context) {
// _saveRegistrationLocallyUseCase.invoke(
// form.control(_self.emailKey).value as String ?? '',
// form.control(_self.passwordKey).value as String ?? '');
}
}
abstract class VpViewModelNew {
VpViewModelNew() {
if (onCreate != null) {
onCreate();
print('creating');
}
}
void onCreate() {}
}
When I navigate to the page that has the signInViewModelProvider, it prints to the console:
flutter: signInPage building
flutter: creating
flutter: cleaning up
Then popping the page from the stack with Navigator.pop() prints nothing.
Then navigating to the page again prints the same 3 lines in the same order.
I expected onDispose to be called after Navigator.pop(), and not when navigating to the page that reads the provider. Why is onDispose being called directly after creation, and not when using Navigator.pop() (when I expected the provider to be disposed of since no other views reference it)?
Edit: I access the provider with final viewModel = context.read<SignInViewModel>(signInViewModelProvider);
I don't need to listen since I don't need to rebuild the page on
change. Is consumer less performant for this?
No, the performance is meaningless, even if it's listening it's not really affecting the performance because as a Provider there is no way to notify (which is not the case with a state notifier or change notifier)
Also if you don't care to listen after the value has been read The auto dispose understand no one is watching it and it disposes, it's better to use context.read when using tap or gestures that modify something
(I realize this is late to the party but maybe it'll help somebody)
The Riverpod docs come out pretty strongly against using read for the reason you said, i.e. performance/rebuilding concerns.
Basically you should always use watch except:
If you want your custom callback function called when it updates (use listen)
If the actual reading is happening asynchronously or in response to user action (like in an onPressed): this is the only time to use read.
If you're having issues with your widgets rebuilding too often, Riverpod has some ways to deal with that that don't involve using read.
I have a TextFormField. Usually you can use the selection toolbar to copy/paste/select all and so on using long tap/double tap.
I want to overwrite the Paste Event. It shouldn't simple insert the current clipboard data but open a popup with several options to insert.
Is it possible to catch and overwrite the Paste event in any way? I saw something like handlePaste() for SelectionControls, but I don't know how to add this to my TextFormField.
Thanks in advance!
AFAIK, you can't exactly 'intercept' the standard toolbar. However, what you can do is to prevent the standard toolbar and make your own.
You can use wrap the textfield/textformfield under IgnorePointer. It will hide any tap gestures on the text field. Below is the code snippet.
IgnorePointer(
child: TextField(
focusNode: _textfieldFocusNode,
controller: _controller,
),
)
Now,you can wrap this IgnorePointer under GestureDetector and show your own menu. Like this :
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
FocusScope.of(context).requestFocus(_textfieldFocusNode);
},
onLongPress: () {
showMenu(____
}
)
This produces the result below and the sample implementation code is here
Found a way to override paste event. I'm not sure, that it is a right way, but it works.
In every TextField you have selectionControls, that provides a way to show and handle toolbar controls.
So, to catch paste event first:
create your own version of selection controls, for example
class AppCupertinoTextSelectionControls extends CupertinoTextSelectionControls {
AppCupertinoTextSelectionControls({
required this.onPaste,
});
ValueChanged<TextSelectionDelegate> onPaste;
#override
Future<void> handlePaste(final TextSelectionDelegate delegate) {
onPaste(delegate);
return super.handlePaste(delegate);
}
}
class AppMaterialTextSelectionControls extends MaterialTextSelectionControls {
AppMaterialTextSelectionControls({
required this.onPaste,
});
ValueChanged<TextSelectionDelegate> onPaste;
#override
Future<void> handlePaste(final TextSelectionDelegate delegate) {
onPaste(delegate);
return super.handlePaste(delegate);
}
}
then, initialise it in your state (for example in StatefulWidget it can looks like that, see below). To study how it used in TextField please see source here
TextSelectionControls? _selectionControls;
#override
void initState() {
if (widget.onPaste != null) {
if (Platform.isIOS) {
_selectionControls = AppCupertinoTextSelectionControls(
onPaste: widget.onPaste!,
);
} else {
_selectionControls = AppMaterialTextSelectionControls(
onPaste: widget.onPaste!,
);
}
}
super.initState();
}
Use callback for onPaste with a type ValueChanged<TextSelectionDelegate> and you can use the same code the Flutter team used to get Clipboard data:
Future<void> onPastePhone(final TextSelectionDelegate? delegate) async {
final TextSelection selection = phoneController.selection;
if (!selection.isValid) {
return;
}
// Snapshot the input before using `await`.
// See https://github.com/flutter/flutter/issues/11427
final ClipboardData? data = await Clipboard.getData(Clipboard.kTextPlain);
final text = data?.text ?? '';
if (text.isEmpty) {
return;
}
}
Then use selection controls in your TextField.
TextFormField(
selectionControls: _selectionControls,
)
Hope it helps.
I search for this problem. I think there is no proper way to solve this problem. I read about the Textfield class and found two solutions for it.
if you check TextField widget you can find that it will use EditableText to show its simple Text input. EditableText has a selectionControls property. this property is used to render the selection toolbar. also, I found that material and Cupertino have different implementation of it.
1st Solution: you can create your own custom TextField that will use EditableText and pass your custom selectionControl to your widget. I think this gonna be a very hard job to do. create your own implementation of the widget, handling animations, and...
2nd Solution: You can simply copy all related files of TextField in a new file and update it as you want. for this solution, I create a repo in GitHub. you can checkout source code to understand how you can show a dialog in the paste option. and this is how the code should work.
note: I just simply update paste function of the Material implementation of selectionControls. if you want you can also update the Cupertino selectionControls too.
note: also I added documents in everywhere I change the code.
I have an app with a ModalProgresHUD on most pages. Usually I can pass a fucntion to, for example, onTap, to any widget in this tree, to turn this spinner on/off.
But sometimes this seems difficult and I'd like to access the fields and/or setState on a State somewhere else, up the WidgetTree.
One option seems to be to move all the logic into the top Widget, and pass handlers down to access these methods, but that feels cludgy.
class StatefullPage ..... {
String _someImportantField;
set someImportantField(String newValue) {
_someImportantField = newValue;
if(mounted) setState((){});
}
...
}
class StateOfSomethingElse ... {
Future doSomeWorkThatAffectsTheParent() async {
await something.then((String newResult) {
State.of(context).someImportantField = newResult;// HOW TO DO THIS
}
...
}
What kind of state management do you use? It looks that you are simply using setState and there's no problem with that. But to do the sort of think you want i think it's better to use something like provider or redux to handle the state of your app.
Using Provider for example, you can provide a value on the top of your tree and can get this value anywhere on your widget tree to do your logic.