My problem:
I need to add a delay for about 100 milliseconds because the inherited animation of the TextFormField when it gets focused shows the keyboard at almost the same time the building animation of itself is running (and also because I want to apply some animations in between and/or then chain the keyboard to it).
I would like to add a small delay there.
What did I have tried so far:
I have been doing a lot things to reproduce this behavior but with not much success, I tried several strategies from flutter community since adding delays, listen to the keyboard using several packages or listening the context bottom viewPadding, I have attached listeners to the onTap of the TextFormField class and it is not the best outcome.
I also have tried with this strategy, but is not good either (sometimes there's a race between the native and the instruction that flutter has internally to show it):
// flutter hook
useEffect(() {
void focusListener() {
// FOCUS:
if (focusNode.hasFocus) {
// #1. Using SystemChannels.textInput:
// The problem with this approach is that flutter ignores it,
// it gets never hidden.
// ... skipped by brevity ...
SystemChannels.textInput.invokeMethod('TextInput.hide')
// ... skipped by brevity ...
// #2. Delay in hide & show events:
// This approach hide the keyboard but its always visible at the beginning
// for a really short amount of time, that's like a blink
// and it doesn't look nice, but this kind of
// solve the issue.
// ... skipped by brevity ...
final channel = SystemChannels.textInput;
Future.delayed(Duration(seconds: 0), () {
channel.invokeMethod('TextInput.hide').whenComplete(() {
Future.delayed(Duration(milliseconds: 400), () {
channel.invokeMethod('TextInput.show');
});
});
});
// ... skipped by brevity ...
// #3. Delay in show event:
// This approach is similar to the previous but the order
// from the channel 'TextInput.hide' is skipped
// because of the same reason of the first approach.
// ... skipped by brevity ...
channel.invokeMethod('TextInput.hide').whenComplete(() {
Future.delayed(Duration(milliseconds: 400), () {
channel.invokeMethod('TextInput.show');
});
});
// ... skipped by brevity ...
}
// VALIDATION:
autovalidate.value = focusNode.hasFocus;
}
focusNode.addListener(focusListener);
return () => focusNode.removeListener(focusListener);
}, [focusNode.hasFocus]);
Any idea?
Related
I am trying to get a switch widget to turn off at a specific time of the day.
I have read a lot of the documentations like these
https://stackoverflow.com/questions/15848214/does-dart-have-a-scheduler
https://pub.dev/packages/cron
https://pub.dev/packages/scheduled_timer
All of these which can only allow me to set duration instead of a specific time.
Thus, my only approach right now is by setting up the timer when the switch is turned on.
e.g. 8hrs then it turns off.
Problem: If the user turned on the switch late, the time that it turns off will also be delayed.
So is there an actual way to set an event at a specific time + works even after we onstop/terminate the application?
You can try to do something like this:
I'll simplify the specific time into :
...
var setTime = DateTime.utc(2022, 7, 11, 8, 48, 0).toLocal();
StreamSubscription? subscription;
...
Then you can assign a periodic stream listener:
...
// periodic to run every second (you can change to minutes/hours/others too)
var stream = Stream.periodic(const Duration(seconds: 1), (count) {
//return true if the time now is after set time
return DateTime.now().isAfter(setTime);
});
//stream subscription
subscription = stream.listen((result) {
// if true, insert function and cancel listen subscription
if(result){
print('turn off');
subscription!.cancel();
}
// else if not yet, run this function
else {
print(result);
}
});
...
However, running a Dart code in a background process is more difficult, here are some references you can try:
https://medium.com/flutter/executing-dart-in-the-background-with-flutter-plugins-and-geofencing-2b3e40a1a124
https://pub.dev/packages/flutter_background_service
I hope it helps, feel free to comment if it doesn't work, I'll try my best to help.
After some time I figured it out.
Format
cron.schedule(Schedule.parse('00 00 * * *'), () async {
print("This code runs at 12am everyday")
});
More Examples
cron.schedule(Schedule.parse('15 * * * *'), () async {
print("This code runs every 15 minutes")
});
To customize a scheduler for your project, read this
I've made an aplication with vala where at some point I have to process a lot of files. I've created a window to choose a folder and then I get the paths of files and make some proces on them.
I've added a progress bar to this window to show how many files have been processed but for some reason it remains always empty.
Code about window:
this.files_window = new Gtk.Window();
this.files_window.window_position = Gtk.WindowPosition.CENTER;
this.files_window.destroy.connect (Gtk.main_quit);
// VBox:
Gtk.Box vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 5);
this.files_window.add (vbox);
// Buttons to open and close
Gtk.Button cancel = new Gtk.Button.with_label ("Cancel");
Gtk.Button select = new Gtk.Button.with_label ("Select");
vbox.add (select);
vbox.add (cancel);
// proogress bar
this.progress_bar = new Gtk.ProgressBar();
vbox.add(this.progress_bar);
// conect select to method do_stuff
select.clicked.connect (do_stuff);
this.files_window.show_all ();
As you can see, I connect the button "select" to the method "do_stuff" where I get the paths of selected files and make some process.
I update correctlly the fraction of the progres bar because I've added some prints to know if the value is correct and it is. It's just that the windows is not refreshing, possibly because all the work it is doing with the process of the files. Here is the code about do_stuff() method:
// some proces to get paths of files in the list sfiles
double fraction = 0.0;
this.progress_bar.set_fraction (fraction);
int processed_files = 0;
foreach (string sfile in sfiles) {
do_some_proces_to_file(sfile);
processed_files += 1;
fraction = (double)processed_files/(double)sfiles.length;
this.progress_bar.set_fraction (fraction);
stdout.printf("Real fraction: %f\n", this.progress_bar.get_fraction());
}
The printf shows that the value of the progres bar is being updated but in the window the bar is always empty.
Am I missing something? Is it the correct way to do the progres bar? Should I made another thread to do the stuff?
As #nemequ says, your code is blocking the main loop thread (which handles both user input and scheduling/drawing widget updates), hence it the progress bar is not updated until the method completes.
Using a thread is one way solve the problem, however using threads can lead to a lot of bugs however since it can be difficult to make even simple interactions between threads safe.
An async method avoids this by interleaving the code with the other work being done by the main loop. An async version of your do_stuff() would be pretty straight-forward to write, simply declare it async and put a yield in the for loop somewhere:
public async void do_stuff() {
...
foreach (string sfile in sfiles) {
// all of this is as before
do_some_proces_to_file(sfile);
processed_files += 1;
fraction = (double)processed_files/(double)sfiles.length;
this.progress_bar.set_fraction (fraction);
// Schedule the method to resume when idle, then
// yield control back to the caller
Idle.add(do_stuff.callback);
yield;
}
}
You can then kick it off from your click handler by calling: do_stuff.begin().
Unless there is some relevant code you're not showing, you're blocking the main loop. One option would be to do everything in a thread, and use an idle callback to update the UI. The basic idea is something like:
new GLib.Thread<void*>("file-processor", () => {
foreach (string sfile in sfiles) {
/* do stuff */
GLib.Idle.add(() => {
/* Update progress */
return false;
});
}
return null;
});
Depending on your application you may need to add a mutex to avoid race conditions. You may also need to add some logic for canceling the operation.
A better option might be to use a GLib.ThreadPool. You'd still want to update the UI from an idle callback, but this would allow each task to execute in parallel, which could provide a significant speed-up.
If I were you I'd probably wrap it all up in an async function to keep the API tidy, but you don't really have to.
I upgraded the kendo library to the 2014Q1 framework which had a few nice features that they were adding, however when I did that it broke any widget (grid, tabStrip, select lists, etc.) from rendering at all. I tracked it down to the layout/view not being able to activate the widget without being wrapped in a setTimeout set to 0. Am I missing something key here or did I build this thing in an invalid way?
http://jsfiddle.net/upmFf/
The basic idea of the problem I am having is below (remove the comments and it works):
var router = new kendo.Router();
var mainLayout = new kendo.Layout($('#mainLayout').html());
var view = new kendo.View('sample', {
wrap: false,
model: kendo.observable({}),
init: function() {
// setTimeout(function(){
$("#datepicker").kendoDatePicker();
// }, 0);
}
});
mainLayout.render('#container');
router.route('/', function() {
mainLayout.showIn('#app', view);
});
router.start();
Admittedly, I don't fully understand it, but hope this helps.
Basically when you try to init the #datepicker, the view elements have not been inserted into the DOM yet. You can put a breakpoint inside the init function, when it hits, check the DOM and you will see that the #app is an empty div, and #datepicker does not exist yet (at least not on the DOM).
kendo.Layout.showIn seems to need to exit in order for the view to finish rendering, but when it initializes the view's elements, it thinks the render is done and init is triggered incorrectly ahead of time. The setTimeout works because it runs the kendoDatePicker initialization asynch, the view is able to finish rendering before the timeout function.
Workarounds...
Trigger the view rendering from the view object itself:
var view = new kendo.View('sample', {
init: function() {
$("#datepicker").kendoDatePicker();
}
});
router.route('/', function() {
view.render('#app');
});
Select and find the datepicker from the view object itself:
var view = new kendo.View('sample', {
init: function() {
view.element.find("#datepicker").kendoDatePicker();
}
});
router.route('/', function() {
mainLayout.showIn('#app', view);
});
Near the bottom of this thread is where I got the idea for the 2nd option. Maybe someone else can come around and give a better explanation of whats going on.
Assume button A in an HTML5 webapp built with jQuery Mobile.
If someone taps button A, we call foo(). Foo() should get called once even if the user double taps button A.
We tried using event.preventDefault(), but that didn't stop the second tap from invoking foo(). event.stopImmediatePropagation() might work, but it also stops other methods further up the stack and may not lead to clean code maintenance.
Other suggestions? Maintaining a tracking variable seems like an awfully ugly solution and is undesirable.
You can set a flag and check if it's OK to run the foo() function or unbind the event for the time you don't want the user to be able to use it and then re-bind the event handler after a delay (just a couple options).
Here's what I would do. I would use a timeout to exclude the subsequent events:
$(document).delegate('#my-page-id', 'pageinit', function () {
//setup a flag to determine if it's OK to run the event handler
var okFlag = true;
//bind event handler to the element in question for the `click` event
$('#my-button-id').bind('click', function () {
//check to see if the flag is set to `true`, do nothing if it's not
if (okFlag) {
//set the flag to `false` so the event handler will be disabled until the timeout resolves
okFlag = false;
//set a timeout to set the flag back to `true` which enables the event handler once again
//you can change the delay for the timeout to whatever you may need, note that units are in milliseconds
setTimeout(function () {
okFlag = true;
}, 300);
//and now, finally, run your original event handler
foo();
}
});
});
I've created a sample here http://jsfiddle.net/kiliman/kH924/
If you're using <a data-role="button"> type buttons, there is no 'disabled' status, but you can add the appropriate class to give it the disabled look.
In your event handler, check to see if the button has the ui-disabled class, and if so, you can return right away. If it doesn't, add the ui-disabled class, then call foo()
If you want to re-enable the button, simply remove the class.
$(function() {
$('#page').bind('pageinit', function(e, data) {
// initialize page
$('#dofoo').click(function() {
var $btn = $(this),
isDisabled = $btn.hasClass('ui-disabled');
if (isDisabled) {
e.preventDefault();
return;
}
$btn.addClass('ui-disabled');
foo();
});
});
function foo() {
alert('I did foo');
}
});
Perhaps my question deviates from the simplicity of itself: Given I .trigger() an event, how can I ensure that code following said .trigger() will not execute until the entire event handler function has completed, including all animations, delays, et al., therein?
I hope I'm missing something here; I'm setting up a UI with a bunch of custom events. Some of the events are really just aggregates of other events; for instance:
// ...
'cb-ui.hide': function(event){
// do stuff to hide
},
'cb-ui.close': function(event){
$(this).trigger('cb-ui.hide');
// more stuff for close
},
// ...
Given there is an animation in the cb-ui.hide event, like .fadeOut(1500), it appears (in my testing) that the remaining // more stuff for close doesn't wait for the animation to complete in the triggered event. I was thinking (previous to referencing the docs) that .trigger() would likely have an optional callback argument much like the animation methods:
$(this).trigger('cb-ui.hide', function(event){
// more stuff for close
});
But this doesn't appear to be the case. Since event triggers are not blocking (or don't appear to be at least), what can I do to force the desired functionality, while keeping with the event handler/trigger implementation that I've been building off of?
More specifically:
$('[data-cb-ui-class="window"]').live({
'cb-ui.hide': function(event){
$(this).find('[data-cb-ui-class="content"]').animate({
opacity: 0
}, 1000);
},
'cb-ui.show': function(event){
$(this).find('[data-cb-ui-class="content"]').animate({
opacity: 1
}, 1000);
}
'cb-ui.close': function(event){
$(this).trigger('cb-ui.hide');
$(this).find('[data-cb-ui-class="content"]').animate({
height: 'hide' // happening simultaneously to the animation of 'cb-ui.hide'
// expected to happen in tandem; one after the other
}, 1000);
},
'cb-ui.update': function(event, html){
// none of this is working as expected; the expected being, the 'cb-ui.hide'
// event is triggered (thus fading the [...-content] out) the HTML content is
// updated, then the [...-content] is faded back in from 'cb-ui.show'
// instead its just a mess that results in it fading out
$(this).trigger('cb-ui.hide');
$(this).find('[data-cb-ui-class="content"]').html(html);
$(this).trigger('cb-ui-show');
}
});
$('#foo').trigger('cb-ui.update', ['<p>Hello world!</p>']); // #foo is bound
This example animation should take ~2 seconds, but appears to be taking 1; both animations are occurring simultaneous to each other, rather than in logical order.
Not sure if I understand your question right, but does this make sense?
You can just pass another function to be run after the animation is done.
'cb-ui.hide': function(event, callback){
$('.lol').fadeTo(0,function() {
// fire callback
})
},
'cb-ui.close': function(event){
cb-ui.hide(e,function() {
// other stuff
});
},