Timer control in flutter - flutter

I'm trying to set one boolean false after 2 seconds(showSpinner) but can't handle it, main idea is showing loading spinner for 2 seconds after than showing solution but spinner never stops and solution never shows up(spinner is loading spinner, text = solution)
#override
void initState(){
super.initState();
showSpinner=true;
Timer(Duration(seconds: 2),(){
setState(() {
showSpinner=false;
});
});
}
Widget build(BuildContext context) {
Widget child;
if (showSpinner == true && isPressed4 == true) {
setState(() {
Timer(Duration(seconds: 2), () {
showSpinner == false;
});
});
child = spinkit;
}
if (showSpinner == false && isPressed4 == true) {
text = simpleInterest.accumulationFunction(
time, yearlySimpleInterestRate, principal);
child = Text(
text,
style: TextStyle(fontSize: 18),
textAlign: TextAlign.center,
);
}
there are 3 buttons(isPressed1 for button 1 and isPressed2 for button2 and isPressd3 for button 3, if all true isPressed4 becomes true)
var floatingActionButton1 = FloatingActionButton(
onPressed: () {
setState(() {
isPressed1 = !isPressed1;
});
setState(() {
principal = double.parse(_principalController.text);
});
setState(() {
if (isPressed3 == true && isPressed2 == true && isPressed1 == true) {
isPressed4 = true;
}
});
},
elevation: 40,
backgroundColor: isPressed1 ? Colors.lightGreenAccent : null,
heroTag: "btn1",
child: Icon(Icons.check),
);

I don't know what is spinkit and isPressed4, but I would do it like this:
bool showSpinner;
#override
void initState() {
super.initState();
showSpinner = true;
Timer(Duration(seconds: 2), () {
setState(() {
showSpinner = false;
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Title'),
),
body: buildBody()
);
}
Widget buildBody(){
return showSpinner ?
CircularProgressIndicator() :
Text(
'The answer',
);
}
UPD:
Initially I have 2 seconds. Then:
If showSpinner == false and floatingActionButton1 has not been pressed, then I get text The time is up!.
If showSpinner == true and floatingActionButton1 has not been pressed, then I get text Button 4 is not pressed yet.
If showSpinner == true and floatingActionButton1 has been pressed, then I get CircularProgressIndicator()(as in the question).
If showSpinner == false and floatingActionButton1 has been pressed, then I get Text with The answer(as in the question):
bool showSpinner;
var floatingActionButton1;
bool isPressed1;
bool isPressed2;
bool isPressed3;
bool isPressed4;
#override
void initState() {
super.initState();
showSpinner = true;
isPressed1 = false;
isPressed2 = true;
isPressed3 = true;
isPressed4 = false;
floatingActionButton1 = FloatingActionButton(
onPressed: () {
setState(() {
isPressed1 = !isPressed1;
if (isPressed3 == true && isPressed2 == true && isPressed1 == true) {
isPressed4 = true;
}
});
},
backgroundColor: isPressed1 ? Colors.lightGreenAccent : null,
child: Icon(Icons.check),
);
Timer(Duration(seconds: 2), () {
setState(() {
showSpinner = false;
});
});
}
Widget build(BuildContext context) {
Widget child;
if(showSpinner == false && isPressed4 == false)
child = Text('The time is up!');
else if(showSpinner == true && isPressed4 == false)
child = Text('Button 4 is not pressed yet');
else if (showSpinner == true && isPressed4 == true) {
child = CircularProgressIndicator();
}
else if (showSpinner == false && isPressed4 == true) {
child = Text(
'The answer',
style: TextStyle(fontSize: 18),
textAlign: TextAlign.center,
);
}
return Scaffold(
appBar: AppBar(
title: Text('Title'),
),
body: child,
floatingActionButton: floatingActionButton1,
);
}

void startTimer() {
const oneSec = const Duration(seconds: 1);
_timer = new Timer.periodic(
oneSec,
(Timer timer) => setState(
() {
if (_start ==0) {
showSpinner=false;
timer.cancel();
} else {
_start = _start - 1;
}
},
),
);
}
#override
void dispose() {
_timer.cancel();
super.dispose();
}
solved with making this function and modifying buttons like;
var floatingActionButton1 = FloatingActionButton(
onPressed: () {
setState(() {
isPressed1 = !isPressed1;
});
setState(() {
principal = double.parse(_principalController.text);
});
setState(() {
if (isPressed3 == true && isPressed2 == true && isPressed1 == true) {
isPressed4 = true;
startTimer();
}
});
},
elevation: 40,
backgroundColor: isPressed1 ? Colors.lightGreenAccent : null,
heroTag: "btn1",
child: Icon(Icons.check),
);

Related

How can I stop Timer period in Flutter?

class _LiftButtonLayerState extends State<LiftButtonLayer> {
late Timer _timer;
late double _timeElapsed;
var isPause = false;
var isTrue = true;
#override
void initState() {
super.initState();
}
repeatDownActivate() {
_timeElapsed = 0;
final timer = Timer.periodic(Duration(milliseconds: 200), (timer) {
liftDownButton.postReq();
print("down singal 5per 1 second ");
});
}
Container(
width: 180,
height: 60,
child: GFButton(
onPressed: () {
repeatUpActivate();
print('clicked Up Button');
},
text: '▲',
textStyle: const TextStyle(
fontSize: 26,
color: GFColors.WHITE,
),
shape: GFButtonShape.pills,
size: GFSize.LARGE,
buttonBoxShadow: true,
)),
I found out that I can use Timer.cancel() from Flutter's Timer library.
I made a function for timer.cancel() , and applied timer.cancel() to onPressed on the new other button, but it doesn't work.
repeatUpActivate(isTrue) {
if (isTrue == '200') {
_timeElapsed = 0;
var _timer = Timer.periodic(Duration(milliseconds: 200), (timer) {
liftUpButton.postReq();
print("down singal 5per 1 second (ms)");
});
} else {
return false;
}
}
child: GFButton(
onPressed: () {
repeatUpActivate('900');
},
can this help you? to better understand how to cancel a timer
late Timer _timer;
int i = 0;
#override
void initState() {
// TODO: implement initState
super.initState();
launchTimer();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Test")),
body: Center(
child: ElevatedButton(
child: Text('stop timer'),
onPressed: () async {
stopTimer(stopTimer: true);
},
),
),
);
}
void launchTimer() {
_timer = Timer.periodic(Duration(milliseconds: 200), (timer) {
i++;
print(i);
});
}
void stopTimer({required bool stopTimer}) {
if (stopTimer) {
_timer.cancel();
}
}

A TextEditingController was used after being disposed in Flutter

I made two TextEditing Controllers and initialized them. Also, I initialized them in my initState too, so that when my screen is disposed they can be initialized again, then why am I getting this error? The two text controllers are used in various places and the OTPBackButton is the widget which takes us back to the previous screen.
When I am opening this screen for first time, it works fine but when I click on this OTPBackButton (or simply the back button) and come back to this screen again, this error is shown to me.
I am including full widget if it helps -
class MobileInput extends StatefulWidget {
final bool isOTP;
final bool isSignup;
VoidCallback signUpState;
TwoArgumentEventCallback signInWithOTP;
Callback sendOTP;
MobileInput({
this.isOTP = true,
required this.sendOTP,
required this.signInWithOTP,
required this.signUpState,
this.isSignup = false,
});
#override
_MobileInputState createState() => _MobileInputState();
}
class _MobileInputState extends State<MobileInput> {
.....
TextEditingController contactController = TextEditingController();
TextEditingController otpController = TextEditingController();
.....
#override
void initState() {
...
contactController = TextEditingController();
otpController = TextEditingController();
super.initState();
}
#override
void dispose() {
// contactController.dispose();
// otpController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return GestureDetector(
.........
(isOTP)
? Center(
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
style: text.bodyText2!
.apply(color: secondaryTextColor),
children: [
TextSpan(
text:
'We sent a verification code to your\nphone number '),
TextSpan(
text: (contactController.text.isNotEmpty)
? '(+91) ' +
contactController.text.toString()
: '',
style:
text.bodyText2!.apply(color: brandPurple),
),
],
),
),
)
: Container(),
SizedBox(
height: getProportionateScreenWidth(32),
),
isOTP
? PinCodeTextField(
animationType: AnimationType.scale,
focusNode: focusNode,
onChanged: (value) {
print(otpController.text + ' after input');
if (otpController.text.length < 6) {
setState(() {
isDisabled = true;
});
}
},
.....
),
onCompleted: (value) {
otpController.text = value;
setState(() {
otpButtonText = "Verify Phone Number";
isDisabled = false;
});
},
)
: CustomTextField(
.....
onChanged: (value) {
if (isSignup) {
if (contactController.text.length == 10) {
FocusScope.of(context).unfocus();
emailEntered = true;
if (checkboxValue) {
setState(() {
isDisabled = false;
});
} else {
setState(() {
isDisabled = true;
});
}
} else {
setState(() {
isDisabled = true;
});
}
} else {
if (contactController.text.length == 10) {
FocusScope.of(context).unfocus();
emailEntered = true;
setState(() {
isDisabled = false;
});
} else {
setState(() {
isDisabled = true;
});
}
}
},
......
),
.....
......
isOTP
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Didn\'t received?',
style: text.bodyText2!.apply(color: Colors.white),
),
TextButton(
onPressed: () {
setState(() {
otpController.clear();
focusNode.requestFocus();
// autoFocus = true;
});
(otpController.text.isEmpty)
? widget.sendOTP(contactController.text)
: null;
},
child: RichText(
........
)
: Container(),
SizedBox(height: getProportionateScreenWidth(20)),
isOTP
? LoginCTA(
//After input otp
onPressed: () async {
.....
if (emailEntered &&
otpController.text.length == 6) {
bool res;
try {
res = await widget.signInWithOTP(
contactController.text, otpController.text);
} catch (e) {
res = false;
print(e);
}
..............
LoginCTA(
//After input mobile number
onPressed: () async {
if (emailEntered &&
contactController.text.length == 10) {
widget.sendOTP(contactController.text);
setState(() {
isOTP = true;
isDisabled = true;
});
} else {
......
}
},
.......
),
SizedBox(height: getProportionateScreenWidth(28)),
!isOTP
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
(isSignup)
? 'Already have an account? '
: 'Don\'t have an account? ',
style: TextStyle(
fontSize: 16,
color: Colors.white,
),
),
GestureDetector(
onTap: () => setState(() {
isSignup = !isSignup;
if (isSignup) {
if (contactController.text.length == 10) {
FocusScope.of(context).unfocus();
emailEntered = true;
if (checkboxValue) {
setState(() {
isDisabled = false;
});
} else {
setState(() {
isDisabled = true;
});
}
} else {
setState(() {
isDisabled = true;
});
}
} else {
if (contactController.text.length == 10) {
FocusScope.of(context).unfocus();
emailEntered = true;
setState(() {
isDisabled = false;
});
} else {
setState(() {
isDisabled = true;
});
}
}
}),
.......
],
)
: Container(),
],
),
),
],
),
),
);
}
}
You need to mark your TextEditingController instances as late final when you define them and then initialize them in the initState() function and dispose of them in dispose() as I show you here:
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
late final TextEditingController _textController;
#override
void initState() {
_textController = TextEditingController();
super.initState();
}
#override
void dispose() {
_textController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
throw UnimplementedError();
}
}

setState() or markNeedsBuild() called during build when playing rive animation

I got error setState() or markNeedsBuild() called during build while I try to invoke this function:
void togglePlay() {
if (_controller == null) {
return;
}
setState(() => _controller.isActive = !_controller.isActive);
}
Error doesn't not occur when I use it with button as a onTap function. But I need to invoke only with other functions.
Here is complete code:
void togglePlay() {
if (_controller == null) {
return;
}
setState(() => _controller.isActive = !_controller.isActive);
}
bool get isPlaying => _controller?.isActive ?? false;
Artboard _riveArtboard;
RiveAnimationController _controller;
// rive
#override
void initState() {
print('guess position');
// rive
try {
rootBundle.load('assets/match.riv').then(
(data) async {
final file = RiveFile.import(data);
final artboard = file.mainArtboard;
artboard.addController(_controller = SimpleAnimation('Animation 1'));
_controller.isActive = false;
setState(() => _riveArtboard = artboard);
},
);
} catch (e) {
print(e.toString());
}
// rive
super.initState();
}
List top = [];
List left = [];
List rotation = [];
next() {
List correctT = [];
List correct = [];
for (int n = 0; n < widget.rotation.length; n++) {
for (int m = 0; m < widget.rotation.length; m++) {
if (rotation[m] == widget.rotation[n] &&
top[m] < widget.top[n] + 45 &&
top[m] > widget.top[n] - 45 &&
left[m] < widget.left[n] + 45 &&
left[m] > widget.left[n] - 45) {
log('widget match $m qgueals match $n');
if (!correctT.contains(n) && !correct.contains(m)) {
correctT.add(n);
correct.add(m);
}
}
}
}
if (correctT.length == widget.rotation.length) {
log('nice');
togglePlay(); // FUNCTION CAUSING ERROR
}
}
nav() {
SchedulerBinding.instance.addPostFrameCallback((_) {
Navigator.push(
context, new MaterialPageRoute(builder: (context) => RandomPos(2)));
});
}
bool created = false;
createList() {
if (!created) {
for (int i = 0; i < widget.top.length; i++) {
top.add(h - 250);
left.add(50);
rotation.add(0);
}
setState(() {
created = true;
});
}
}
match(int i) {
return Positioned(
top: top[i].toDouble(),
left: left[i].toDouble(),
child: RotationTransition(
turns: new AlwaysStoppedAnimation(rotation[i] / 360),
child: GestureDetector(
onTap: () {
rotation[i] = rotation[i] + 45;
setState(() {});
next();
},
onDoubleTap: () {
rotation[i] = rotation[i] - 45;
setState(() {});
next();
},
child: Draggable(
data: 10,
onDragUpdate: (details) {
top[i] = top[i] + details.delta.dy;
left[i] = left[i] + details.delta.dx;
setState(() {});
},
onDragEnd: next(),
child: Container(
height: 250,
width: 250,
child: _riveArtboard == null
? const SizedBox()
: Rive(artboard: _riveArtboard),
),
feedback: Container()),
),
));
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: createList(),
builder: (context, snapshot) {
return Container(
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.fill,
image: AssetImage('wood${widget.wood}.jpg'))),
child: Stack(
children: [for (int i = 0; i < rotation.length; i++) match(i)],
),
);
}),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.white,
child: Text(
'next',
style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold),
),
onPressed: () => nav()),
);
}
}
Thank you if you could have a look and help me in solving
A few soluttion for typical "set state called during rebuild" didnt work or caused other errors
This error comes when we are calling setState during the build phase.
Use addPostFrameCallback which runs after build rendering is done
void togglePlay() {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (_controller == null) {
return;
}
setState(() => _controller.isActive = !_controller.isActive);
});
}
OR
Timer.run(() {
if (_controller == null) {
return;
}
setState(() => _controller.isActive = !_controller.isActive);
});

Flutter Audioplayers with Clip/Edit Function like Just_ Audio player

Whenever I record audio in my flutter app there's a noise at the start and the end of the clip due to tapping the start and stop buttons on my phone. Flutter's Just-Audio plugin has an Edit/Clip function that will remove these noises. IE:
await player.setClip(start: Duration(seconds: 10), end: Duration(seconds: 20));
But unfortunately, the Just-Audio plugin doesn't play quite right with my app. I've got a swiping pageview and each page has it's own player. Just-Audio doesn't seem to like this and acts as if each swiped page is the same player.
So I'm using the Audioplayers plugin which allows for multiple players. But the Audioplayer plugin doesn't have the Clip/Edit function that the Just-Audio plugin has. So can anyone see where I can get the same Clip/Edit function into the Audioplayers plugin?
Here's the Just Player plugin's function;
Future<Duration?> setClip({Duration? start, Duration? end}) async {
if (_disposed) return null;
_setPlatformActive(true)?.catchError((dynamic e) {});
final duration = await _load(
await _platform,
start == null && end == null
? _audioSource!
: ClippingAudioSource(
child: _audioSource as UriAudioSource,
start: start,
end: end,
));
return duration;
}
And here's a widget for the Audioplayers plugin;
import 'dart:async';
import 'package:audioplayers/audioplayers.dart';
import 'package:audioplayers/notifications.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class PlayerWidget extends StatefulWidget {
final String url;
final PlayerMode mode;
const PlayerWidget({
Key? key,
required this.url,
this.mode = PlayerMode.MEDIA_PLAYER,
}) : super(key: key);
#override
State<StatefulWidget> createState() {
return _PlayerWidgetState(url, mode);
}
}
class _PlayerWidgetState extends State<PlayerWidget> {
String url;
PlayerMode mode;
late AudioPlayer _audioPlayer;
PlayerState? _audioPlayerState;
Duration? _duration;
Duration? _position;
PlayerState _playerState = PlayerState.STOPPED;
PlayingRoute _playingRouteState = PlayingRoute.SPEAKERS;
StreamSubscription? _durationSubscription;
StreamSubscription? _positionSubscription;
StreamSubscription? _playerCompleteSubscription;
StreamSubscription? _playerErrorSubscription;
StreamSubscription? _playerStateSubscription;
StreamSubscription<PlayerControlCommand>? _playerControlCommandSubscription;
bool get _isPlaying => _playerState == PlayerState.PLAYING;
bool get _isPaused => _playerState == PlayerState.PAUSED;
String get _durationText => _duration?.toString().split('.').first ?? '';
String get _positionText => _position?.toString().split('.').first ?? '';
bool get _isPlayingThroughEarpiece =>
_playingRouteState == PlayingRoute.EARPIECE;
_PlayerWidgetState(this.url, this.mode);
#override
void initState() {
super.initState();
_initAudioPlayer();
}
#override
void dispose() {
_audioPlayer.dispose();
_durationSubscription?.cancel();
_positionSubscription?.cancel();
_playerCompleteSubscription?.cancel();
_playerErrorSubscription?.cancel();
_playerStateSubscription?.cancel();
_playerControlCommandSubscription?.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
key: const Key('play_button'),
onPressed: _isPlaying ? null : _play,
iconSize: 64.0,
icon: const Icon(Icons.play_arrow),
color: Colors.cyan,
),
IconButton(
key: const Key('pause_button'),
onPressed: _isPlaying ? _pause : null,
iconSize: 64.0,
icon: const Icon(Icons.pause),
color: Colors.cyan,
),
IconButton(
key: const Key('stop_button'),
onPressed: _isPlaying || _isPaused ? _stop : null,
iconSize: 64.0,
icon: const Icon(Icons.stop),
color: Colors.cyan,
),
IconButton(
onPressed: _earpieceOrSpeakersToggle,
iconSize: 64.0,
icon: _isPlayingThroughEarpiece
? const Icon(Icons.volume_up)
: const Icon(Icons.hearing),
color: Colors.cyan,
),
],
),
Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.all(12.0),
child: Stack(
children: [
Slider(
onChanged: (v) {
final Position = v * _duration!.inMilliseconds;
_audioPlayer
.seek(Duration(milliseconds: Position.round()));
},
value: (_position != null &&
_duration != null &&
_position!.inMilliseconds > 0 &&
_position!.inMilliseconds <
_duration!.inMilliseconds)
? _position!.inMilliseconds / _duration!.inMilliseconds
: 0.0,
),
],
),
),
Text(
_position != null
? '$_positionText / $_durationText'
: _duration != null
? _durationText
: '',
style: const TextStyle(fontSize: 24.0),
),
],
),
Text('State: $_audioPlayerState'),
],
);
}
void _initAudioPlayer() {
_audioPlayer = AudioPlayer(mode: mode);
_durationSubscription = _audioPlayer.onDurationChanged.listen((duration) {
setState(() => _duration = duration);
if (Theme.of(context).platform == TargetPlatform.iOS) {
// optional: listen for notification updates in the background
_audioPlayer.notificationService.startHeadlessService();
// set at least title to see the notification bar on ios.
_audioPlayer.notificationService.setNotification(
title: 'App Name',
artist: 'Artist or blank',
albumTitle: 'Name or blank',
imageUrl: 'Image URL or blank',
forwardSkipInterval: const Duration(seconds: 30), // default is 30s
backwardSkipInterval: const Duration(seconds: 30), // default is 30s
duration: duration,
enableNextTrackButton: true,
enablePreviousTrackButton: true,
);
}
});
_positionSubscription =
_audioPlayer.onAudioPositionChanged.listen((p) => setState(() {
_position = p;
}));
_playerCompleteSubscription =
_audioPlayer.onPlayerCompletion.listen((event) {
_onComplete();
setState(() {
_position = _duration;
});
});
_playerErrorSubscription = _audioPlayer.onPlayerError.listen((msg) {
print('audioPlayer error : $msg');
setState(() {
_playerState = PlayerState.STOPPED;
_duration = const Duration();
_position = const Duration();
});
});
_playerControlCommandSubscription =
_audioPlayer.notificationService.onPlayerCommand.listen((command) {
print('command: $command');
});
_audioPlayer.onPlayerStateChanged.listen((state) {
if (mounted) {
setState(() {
_audioPlayerState = state;
});
}
});
_audioPlayer.onNotificationPlayerStateChanged.listen((state) {
if (mounted) {
setState(() => _audioPlayerState = state);
}
});
_playingRouteState = PlayingRoute.SPEAKERS;
}
Future<int> _play() async {
final playPosition = (_position != null &&
_duration != null &&
_position!.inMilliseconds > 0 &&
_position!.inMilliseconds < _duration!.inMilliseconds)
? _position
: null;
final result = await _audioPlayer.play(url, position: playPosition);
if (result == 1) {
setState(() => _playerState = PlayerState.PLAYING);
}
// default playback rate is 1.0
// this should be called after _audioPlayer.play() or _audioPlayer.resume()
// this can also be called everytime the user wants to change playback rate in the UI
_audioPlayer.setPlaybackRate();
return result;
}
Future<int> _pause() async {
final result = await _audioPlayer.pause();
if (result == 1) {
setState(() => _playerState = PlayerState.PAUSED);
}
return result;
}
Future<int> _earpieceOrSpeakersToggle() async {
final result = await _audioPlayer.earpieceOrSpeakersToggle();
if (result == 1) {
setState(() => _playingRouteState = _playingRouteState.toggle());
}
return result;
}
Future<int> _stop() async {
final result = await _audioPlayer.stop();
if (result == 1) {
setState(() {
_playerState = PlayerState.STOPPED;
_position = const Duration();
});
}
return result;
}
void _onComplete() {
setState(() => _playerState = PlayerState.STOPPED);
}
}

Slider widget does not work on iOS and Web

I have an audio player in my app. I used Slider.adaptive to make the seek bar. Although It works well on Android, it gives me this error on iOS and the Web:
════════ Exception caught by widgets library
═══════════════════════════════════ Assertion failed:
../…/cupertino/slider.dart:332 value != null && value >= 0.0 && value
<= 1.0 is not true
The relevant error-causing widget was Slider
Here is how my code looks like:
class PlayBackButtons extends StatefulWidget {
#override
_PlayBackButtonsState createState() => _PlayBackButtonsState();
}
class _PlayBackButtonsState extends State<PlayBackButtons> {
bool _isPlaying = false;
AudioPlayer _audioPlayer;
Duration _duration = new Duration();
Duration _position = new Duration();
int result = 0;
int tutsLenght;
int index;
#override
void initState() {
super.initState();
_audioPlayer = AudioPlayer();
Future.delayed(Duration.zero, () {
Provider.of<Tutorials>(context, listen: false).resetIndex();
});
}
#override
void dispose() {
_cleanup();
super.dispose();
}
double progress() => max() > (_position?.inMilliseconds ?? 0).toDouble()
? (_position?.inMilliseconds ?? 0).toDouble()
: 0.0;
double max() => (_duration.inMilliseconds ?? 0.0).toDouble();
void _cleanup() {
if (result == 1) {
_audioPlayer.stop();
_audioPlayer.dispose();
Provider.of<Tutorials>(context).resetIndex();
}
}
void _play(String url) async {
result = await _audioPlayer.play(url);
setState(() => _isPlaying = true);
if (result == 1) {
_audioPlayer.onDurationChanged.listen((Duration dd) {
setState(() {
_duration = dd;
});
});
_audioPlayer.onAudioPositionChanged.listen((Duration dd) {
setState(() {
_position = dd;
});
});
} else {
print('audio player crashed');
}
}
void _pause() async {
await _audioPlayer.pause();
setState(() => _isPlaying = false);
}
void _next() async {
await _audioPlayer.stop();
setState(() => _isPlaying = false);
if (Provider.of<Tutorials>(context).index < tutsLenght) {
} else {
Navigator.pop(context);
}
}
void _previous() {
if (Provider.of<Tutorials>(context).index != 0) {
_audioPlayer.stop();
setState(() => _isPlaying = false);
Provider.of<Tutorials>(context).decrementIndex();
}
}
#override
Widget build(BuildContext context) {
index = Provider.of<Tutorials>(context).index;
final tuts = Provider.of<Tutorials>(context, listen: false).tutsOfASection;
tutsLenght = tuts.length;
final url = tuts[index].audio;
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Slider.adaptive(
min: 0.0,
value: progress(),//_position.inSeconds.toDouble(),
max: max(),//_duration.inSeconds.toDouble(),
onChanged: (double value) {
setState(() => _audioPlayer.seek(Duration(seconds: value.toInt())));
},
),
Row(
children: [
IconButton(
icon: Icon(Icons.skip_previous),
onPressed: () => _previous(),
),
IconButton(
icon: _isPlaying ? Icon(Icons.pause) : Icon(Icons.play_arrow),
onPressed: () {
if (_isPlaying) {
_pause();
} else {
_play(url);
}
},
),
IconButton(
icon: Icon(Icons.skip_next),
onPressed: () => _next(),
),
],
mainAxisAlignment: MainAxisAlignment.center,
),
],
);
}
}