dipsose() Being called at same time as initState() - flutter

I'm porting a offline Flutter game to online and the only difference in the code is that the new online code where the problem occurs has a active StreamSubscription being listened to. I can't find anything online other than 1 issue saying this is a bug.. any ideas on why dipose() is being called on the initial build?
class _OnlineSequenceState extends State<OnlineSequence> with SingleTickerProviderStateMixin {
//variable initialization would be here
#override
void initState(){
super.initState();
//setup gameRef
gameRef = FirebaseDatabase.instance.reference().child('sequence').child(widget.gameID);
//glow animation init
_setupGlowAnimtation();
//init sounds
_initSounds();
//setup player and card objects and create a stream
_setupGame();
}
#override
void dispose(){
print('disposing'); //prints on the widget being built for the first time
gameDataStream.cancel();
_animation.removeListener(() {});
_animationController.stop();
pool.dispose();
super.dispose();
}
_setupGlowAnimtation() {
_animationController = AnimationController(vsync: this, duration: Duration(milliseconds: 1300));
_animationController.repeat(reverse: true);
_animation = Tween(begin: 1.0, end: 6.4).animate(_animationController)
..addListener(() {
setState(() {});
});
}
_initSounds() async {
drawSound = await pool.load(await rootBundle.load('lib/assets/sounds/draw.wav'));
playCardSound = await (pool.load(await rootBundle.load('lib/assets/sounds/playcard.wav')));
shuffleSound = await (pool.load(await rootBundle.load('lib/assets/sounds/shuffle.wav')));
pool.play(shuffleSound);
}
...

The error turned out to actually be in the prior lobby screen. The host of the game would navigate to in-game screen twice instead of once causing weird dispose() behavior. Once I fixed the double navigation the issue went away.

Related

Flutter - video_player listener being called very slowly

I want to get the current position of the video being played in real-time. I was thinking of using a listener, but if I do:
_controller.addListener(() => print(_controller.value.position.inMilliseconds))
It only prints the value every 500 milliseconds. This is too slow, the video is updating every 33ms or even more often. Anyone knows why is this happening and what's the proper way of achieving what I want?
P.S. I was able to achieve what I wanted by starting an AnimationController when the video starts, but this seems like a hack.
The reason for the delay is that VideoPlayerController is notifying listeners every 500 milliseconds. You can use a Timer to periodically get position of video player. Here is a sample code to do that
class VideoPlayerScreen extends StatefulWidget {
#override
VideoPlayerState createState() => VideoPlayerState();
}
class VideoPlayerState extends State<VideoPlayerScreen> {
Timer _timer;
VideoPlayerController videoPlayerController;
void startTimer() {
_timer = Timer.periodic(const Duration(milliseconds: 100), (Timer timer) async {
print(await videoPlayerController.position);
});
}
#override
void dispose() {
_timer?.cancel();
videoPlayerController?.dispose();
super.dispose();
}
}

A VideoPlayerController was used after being disposed. Once you have called dispose() on a VideoPlayerController, it can no longer be used

I am using chewie player to play a list of videos in pageview.builder. when i scroll my page below it works fine,but when i scroll my page to view top video it shows me this error.How can i re-initialize the videoplayercontroller.
problem is you have to destroy the container that is using the video player and setting .dipose() after its destroyed. So your kill function should be something like this:
void killVidPlayer() {
yourContainer = Container();
setState(() {
});
_controller.dispose();
_controller = null;
}
So i followed what this guy said here and it worked github
void initState() {
super.initState(); //Super should be called at the very beginning of init
_controller = VideoPlayerController.network(
'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerFun.mp4',
);
_chewieController = ChewieController(
videoPlayerController: _controller,
aspectRatio: 3 / 2,
autoPlay: true,
looping: true,
);
}
#override
void dispose() {
_controller.dispose();
_chewieController.dispose();
super.dispose(); //Super should be called at the very end of dispose
}
//Let me know if this doesn't solve your issue.

How to run a timer in initState flutter?

I've created an app using flutter and I want to get start a timer when the user navigates to a screen to calculate how long will he stay there.
I want if he has completed three minutes on the screen to print anything.
I don't have any ideas about the timer or how to use it.
please help me regarding achieving that.
thanks all.
You can do something like that.
Timer _timer;
#override
void initState() {
_timer = Timer(Duration(milliseconds: 500), () {
// SOMETHING
});
super.initState();
}
#override
void dispose() {
if (_timer != null) {
_timer.cancel();
_timer = null;
}
super.dispose();
}
Please have a look of this:
import 'dart:async';
main() {
const twentyMillis = const Duration(milliseconds:20);
new Timer(twentyMillis, () => print('hi!'));
}
Also look Timer class link

Async/Await not working as expect. How can I start a timer exactly 4 seconds after a sound has started playing in Flutter?

When my app's game screen appears, an audio file starts playing that says "Ready? 3...2...1...GO" and then plays music.
I'd like the game timer to start at the same time that it says "GO", which is exactly 4 seconds into the audio.
I got pretty close by starting the audio file in initState() and then using "Future.delayed(const Duration(seconds: 4), () => startTimer());" but the problem is that the first time a user plays, it take a bit of time for the audio to load, so then the timer starts before the audio says "GO".
I tried to solve this by making an startGameSequence() function that uses async and await, but it isn't working. What am I doing wrong?
Here's what I've got so far:
import 'package:audioplayers/audio_cache.dart';
import 'dart:async';
class GameScreen extends StatefulWidget {
#override
_GameScreenState createState() => _GameScreenState();
}
class _GameScreenState extends State<GameScreen> {
void loadAudio() {
final player = AudioCache();
player.load('audio/ready-321-go-music.mp3');
}
void playAudio() {
final player = AudioCache();
player.play('audio/ready-321-go-music.mp3');
}
void startGameSequence() async {
await loadAudio();
playAudio();
// After audio finishes loading / starts playing, THEN I'd like the 4-second delayed timer to start. (Currently, it seems that the time starts too early because the audio takes a bit to load.)
Future.delayed(
const Duration(seconds: 4),
() => startTimer(),
);
}
Timer _timer;
double _timeRemaining = 7.00;
void startTimer() {
const tick = const Duration(milliseconds: 10);
_timer = new Timer.periodic(
tick,
(Timer timer) => setState(
() {
if (_timeRemaining < 0.01) {
timer.cancel();
} else {
_timeRemaining = _timeRemaining - 0.01;
}
},
),
);
}
#override
initState() {
super.initState();
startGameSequence();
}
Thank you in advance for any help!
InitState is not async, wherefore this not working correctly.
A one solution is, load the audioFile in initState() and execute startGameSequence() in didUpdateWidget() function without the Future.delayed() .
#override
initState() {
super.initState();
await loadAudio();
}
#override
void didUpdateWidget(GameScreen oldWidget) {
startGameSequence()
super.didUpdateWidget(oldWidget);
}
void startGameSequence() {
playAudio();
}
This function execute just when the first layout appears on the screen.

setState() or markNeedsBuild() called during build exception prevents me from executing callback

I'm trying to execute a callback function once a timer ends in a particular widget but I keep getting this exception:
I/flutter (16413): Another exception was thrown: setState() or markNeedsBuild() called during build.
So I have this widget called countdown:
class Countdown extends StatefulWidget {
final VoidCallback onCountdownExpire;
Countdown(this.onCountdownExpire);
#override
CountdownState createState() => CountdownState();
}
class CountdownState extends State<Countdown> with TickerProviderStateMixin {
AnimationController controller;
String get timerString {
Duration duration = controller.duration * controller.value;
return '${duration.inMinutes}:${(duration.inSeconds % 60).toString()}';
}
#override
void initState() {
super.initState();
controller = AnimationController(
vsync: this,
duration: Duration(seconds: 2),
)..addStatusListener((AnimationStatus status){
if (status == AnimationStatus.completed)
widget.onCountdownExpire();
});
controller.reverse(from: 1.0);
}
...
...
... // omitted code
}
So once the animation is completed it will call the callback function:
class _QuizPageState extends State<QuizPage> {
... // omitted code
#override
void initState() {
... // omitted code
}
void onCountdownExpire() {
setState(() {
_topContentImage = AssetImage(questions[questionNum++].imagePath);
});
}
... // omitted code
}
I've tried to follow a solution but it does not work and gives me the same exception:
void onCountdownExpire() =>
setState(() {
_topContentImage = AssetImage(questions[questionNum++].imagePath);
});
I also tried this but to no avail:
#override
void initState() {
super.initState();
controller = AnimationController(
vsync: this,
duration: Duration(seconds: 2),
)..addStatusListener((AnimationStatus status) =>
(status == AnimationStatus.completed) ?
widget.onCountdownExpire():null
);
controller.reverse(from: 1.0);
}
maybe try including 'dart:async':
import 'dart:async';
then try wrapping your call of the onCountdownExpire function in a short-lived Timer():
...
Timer(
Duration(milliseconds:50),
() {
if (status == AnimationStatus.completed)
widget.onCountdownExpire();
},
);
...
this will make the setState() happen outside the build phase of the last frame of your animation.
The error occurs most likely because the Countdown() is being redrawn as part of the redraw of the QuizPage() widget. Adding the Timer() will force the update to happen outside of the build() scope, in an async fashion, and will still achieve the same results without the error.