How can I achieve a Timer Widget that navigates to another screen after the time expired or it restarts itself after, lets say a swiping gesture?
Flutter has a class called RestartableTimer which extends from Timer. It get's a Duration element and a callback method when the timer is set.
When you want to restart it, you can simply reset it. Here is the code snippet to go through all of this. You can just put the code to the related place.
//You need to import this
import 'package:async/async.dart';
// Duration is 5 seconds
Duration _timerDuration = new Duration(seconds: 5);
// Creating a new timer element.
RestartableTimer _timer = new RestartableTimer(_timerDuration, _startNewPage);
fun _startNewPage() {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondScreen()),
);
}
// Restarting the timer
_timer.reset();
Related
I am in a similar situation as this poster (What's the best way to create a new UI thread and call back methods on the original thread?)
I have an API object which I would like to perform lengthy calculations on, however any properties or methods of this object must be accessed on the current thread (which is the UI thread) or else I get "Accessing disposed TPS.NET DataObject" exceptions
Is there an elegant way of accomplishing this using F# async workflows or will I be stuck managing thread dispatchers as in his solution.
For reference, here is his solution to the issue:
public class Plugin
{
public void Run(Context context)
{
// Get the application's UI thread dispatcher
var dispatcher = Dispatcher.CurrentDispatcher;
// Create a dispatcher frame to push later
var frame = new DispatcherFrame();
// Create a new UI thread (using an StaTaskScheduler)
Task.Factory.StartNew(async () =>
{
var window = new MyWindow();
// The Click event handler now uses the original
// thread's dispatcher to run the slow method
window.MyButton.Click += async (o, e) =>
await dispatcher.InvokeAsync(() => context.SlowMethod());
window.ShowDialog();
// When the window is closed, end the dispatcher frame
frame.Continue = false;
}, CancellationToken.None, TaskCreationOptions.None, new StaTaskScheduler(1));
// Prevent exiting this Run method until the frame is done
Dispatcher.PushFrame(frame);
}
}
Without know the exact details I would suggest having the Click handler on the main thread and do the following:
Copy any data needed off the UI into an F# record and passes this into an async workflow
Return immediately after putting the UI into a 'loading' state
The following code is untested but should put you on the right track:
//Get the context of the UI
let context = System.Threading.SynchronizationContext.Current
//Gather any needed data from the UI into immutable F# records
//Put the UI into a 'loading' state
async {
// Do work on a different thread
do! Async.Sleep 1000
let x = 1
// Switching back to the UI
do! Async.SwitchToContext context
//Update UI
return ()
}
|> Async.Start
This link should also provide some useful information http://tomasp.net/blog/async-non-blocking-gui.aspx/
EDIT:
If you need to go back and forth between the UI thread and a background thread to gather additional information in the async workflow you can make alternating calls between do! Async.SwitchToThreadPool() and do! Async.SwitchToContext context
I am developing an app on React where countdown timers are the main component (at the same time there can be 10-20 timers on the page). From the server I get: how long the timer should go and how much is left in seconds. Then every second I recount how much is left. The source data is stored in redux, and calculated in the component local state.
These timers should show the same values for every user.
The problem is when I duplicate the tabs in the browser, the api request does not occur, respectively, the timers in a new tab are rolled back to the old state.
Updating data every second in redux seems to me not to be the best option, but I don’t see others yet.
You said that the server sends you the remaining time in seconds. So you can calculate on the client side when the countdown should end in client time. You can store that in local storage. When a new tab is opened you can use that value to initialize your timer.
It does not require the client time to be correct or in sync with the server time as all tabs share the same (possibly wrong) client time. You are only interested in the difference in seconds between the current client time and the client time you saved to correctly initialize your timer.
A solution to calculate it could roughly look like this:
// when receiving the remaining seconds in the first tab
const onReceivedRemaining = (remaining) => {
const now = new Date(); // current client time
now.setSeconds(now.getSeconds() + remaining); // timer end in client time
localStorage.set('elapsing', JSON.stringify(now));
}
// when initializing the timer in a second tab
const getInitial = () => {
const elapsing_string = localStorage.get('elapsing');
if (!elapsing_string) return null;
const now = new Date();
const elapsing = Date.parse(elapsing_string);
return (elapsing - now) / 1000; // remaining time in seconds
}
Hey everyone so I have an array private var aFishArray:Array; that is setup with the timer inside my constructor.
tFishTimer = new Timer(800);
//Listen for timer intervals/ticks
tFishTimer.addEventListener(TimerEvent.TIMER, addMainFish,false,0,true);
//Start timer object
tFishTimer.start();
then in the end game Condition I removed the timers tFishTimer.removeEventListener(TimerEvent.TIMER, addMainFish);
tFishTimer.stop();
Now this works perfectly but the problem is when I make a new timer of the same instance inside a separate function like so
private function checkFishPowerHitBucket():void
{
for (var j:int = 0; j < aFishPowerUpArray.length; j++)
{
//get current fish in j loop
var currentfPower:mcMoreFishPowerUp = aFishPowerUpArray[j];
//test if current fish is hitting bucket
if (currentfPower.hitTestObject(bucket))
{
//If we want timer to only run a certain amount of times then new Timer(1000, ??)
tFishTimer = new Timer(100, 30);
//Listen for timer intervals/ticks
tFishTimer.addEventListener(TimerEvent.TIMER, addMainFish, false, 0, true);
//Start timer object
tFishTimer.start();
}
}
}
and then in my end game condition try to remove the timer and the movie clips from entering the screen anymore it no longer happens. The fish just keep appearing on the screen. Is there anything that i can do to remove all instances of these timers when the game is over. Im thinking that by creating a new timer with the same array it cancels the command to delete it when it starts a new timer? any help would be appreciated thanks.
also here is the addMainFish(); Function
private function addMainFish(e:Event):void
{
//Create new fish object
var newFish = new mcMainFish();
//Add fish object to stage
stage.addChild(newFish);
//Add fish to fish Array
aFishArray.push(newFish);
//trace(aFishArray.length);
}
It doesn't really 'delete' the old timer, but what has happened is that when you create the new timer and store it in the same variable, you've lost the reference to the old one, so it just resides somewhere in memory, forever running (or until it hits it's limit, if it has one).
A way to solve this particular problem is to keep a reference to the old timer(s) such as in an array, then when you make a new timer, move the old one to an Timer array first so that you have access to it still.
I do not recommend this.
Having multiple timers like this will cost you in performance and possibly a fair amount of unpredictability. Also, it can lead to some tough debugging down the road as you may have trouble tracing which timer caused which thing to fail, strange instances of objects trying to access the same data, having to deal with so many objects in itself, etc.
What you could do is just have a single dedicated timer and within that timer do All of the processing on each of your objects that need it. You could also avoid a timer altogether and just use your game loop to do this logic.
This post illustrates a similar problem and the answer there is similar to what I've mentioned here.
I can't for the life of my work out why this is happening. I have in a class derived from CCLayer. I am scheduling a method call like so when initialising the class
//create an update method for keeping track of how long its been since an animation has played
[self schedule:#selector(playIdleAnimation:)];
And the method is
//an update method that will play an idle animation after a random period of idleness
-(void) playIdleAnimation:(ccTime) dt {
//if the user isn't playing an animation, increment the time since last animation variable
if ([bodySprite numberOfRunningActions] == 0) {
timeSinceLastAnimation += (float)dt;
//now check to see if we have surpassed the time set to cause an idle animation
if (timeSinceLastAnimation > (arc4random() %14) + 8) {
//reset the cooldown timer
timeSinceLastAnimation = 0;
[bodySprite stopAllActions];
//play the idle animation
//[bodySprite runAction:[CCAnimate actionWithAnimation:waitAnimation restoreOriginalFrame:NO]];
NSLog(#"PLAYING IDLE ANIMATION");
}
}
//player is currently playing animation so reset the time since last animation
else
timeSinceLastAnimation = 0;
}
But yet, when I go to run the program the console statements show the condition is being passed twice each cooldown
012-06-29 09:52:57.667 Test Game[5193:707] PLAYING IDLE ANIMATION
2012-06-29 09:52:57.701 Test Game[5193:707] PLAYING IDLE ANIMATION
2012-06-29 09:53:05.750 Test Game[5193:707] PLAYING IDLE ANIMATION
2012-06-29 09:53:05.851 Test Game[5193:707] PLAYING IDLE ANIMATION
I am trying to fix a bug where the game crashes when I finish playing the idle animation, and I'm certain this has something to do with it.
I don't see where you are unscheduling the selector. I bet that it's normal behavior to be called ever frame kicks in, and you see it being triggered twice because it takes a frame for the layer to be deallocated.
If you want a one-time method call, do this:
-(void) playIdleAnimation:(ccTime) dt {
[self unschedule:_cmd];
// rest of the code here
}
Cocos2d 2.0 has a scheduleOnce method that you can use instead.
I try to delay close a window in my App.xaml.ca :
Window splash = new Window();
splash.Show();
Timer timer = new Timer(callback, null, 2000, Timeout.Infinite);
private void callback(object stateInfo)
{
splash.Close();
}
It works fine, but the whole App is shutdowning. What am doing wrong here ?
Be sure to check that you timer callback is coming back on the main dispatcher thread. If not then you will likely be getting an exception when you try to close your window from a different thread.
Use splash.Dispatcher.CheckAccess() to make sure you are on the right thread and if not then use splash.Dispatcher.BeginInvoke((Action) () => splash.Close() to dispatch the call onto the main thread.
Check out this page for more
Here is my solution to this exact same problem:
private async void CloseWindow()
{
await ClosingTasks();
}
private async Task ClosingTasks()
{
await Task.Delay(2000);
this.Close();
}
Where you simply call CloseWindow() when you want to close the current window after the given delay of 2000 mS.
There are different shutdown modes, if that window is closed and it is the last then the application will shut down by default. So you can either see to it that there is still some window around or you can change the shutdown behaviour by setting the ShutdownMode to something that suits your needs.
e.g.
Application.Current.ShutdownMode = System.Windows.ShutdownMode.OnExplicitShutdown;