Help me!
I want mute and unmute button with Lottie animation. But this mute.json json animation have both.
So I need one click play Lottie half animation like this.
When clicked and
#override
void initState() {
super.initState();
_controller = AnimationController(vsync: this)
..value = 0.5
..addListener(() {
setState(() {
// Rebuild the widget at each frame to update the "progress" label.
});
});
}
Column(
children[
Lottie.asset(
controller: _controller,
'assets/mute.json',
animate: true,
onLoaded: (composition) {
setState(() {
_controller.duration = composition.duration;
});
},
),
],
),
bool mute = false;
#override
void initState() {
super.initState();
// add duration
_controller = AnimationController(vsync: this, duration: Duration(milliseconds: 300));
}
use animateTo method on controller.
InkWell(
onTap: () {
mute = !mute;
log(mute.toString());
if (mute) {
_controller.animateTo(0.5);
} else {
_controller.animateTo(0);
}
},
child: LottieBuilder.network(
"https://maxst.icons8.com/vue-static/landings/animated-icons/icons/no-sound/no-sound.json",
controller: _controller,
height: 200,
),
),
Related
I have a video file in my flutter application. I am playing that video with the help of video_player package from flutter. The video is playing perfectly, but according to their documentation the only way to have the pause/play buttons are floating action buttons. I want the pause/play button above the video and centre of it. Is there any way to do this?
Following is my code:
void initState() {
// TODO: implement initState
super.initState();
_controller = VideoPlayerController.network(
imageapiurl + '/question/' + id + '/' + questionimageurl,
);
// Initialize the controller and store the Future for later use.
_initializeVideoPlayerFuture = _controller.initialize();
// Use the controller to loop the video.
_controller.setLooping(true);
}
FutureBuilder(
future: _initializeVideoPlayerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.done) {
// If the VideoPlayerController has finished initialization, use
// the data it provides to limit the aspect ratio of the video.
return AspectRatio(
aspectRatio: _controller.value.aspectRatio,
// Use the VideoPlayer widget to display the video.
child: GestureDetector(
onTap: () {
setState(() {
// If the video is playing, pause it.
if (_controller.value.isPlaying) {
_controller.pause();
} else {
// If the video is paused, play it.
_controller.play();
}
});
},
child: VideoPlayer(_controller)),
);
} else {
// If the VideoPlayerController is still initializing, show a
// loading spinner.
return Center(child: CircularProgressIndicator());
}
},
),
Can someone please tell me how to add those buttons above the video?
Can add an overlay to play or pause video.
VideoPlayerController _controller;
bool _onTouch = false;
Timer _timer;
#override
void initState() {
super.initState();
_controller = VideoPlayerController.network(
widget.videoPath ?? 'none'
);
_controller.setLooping(widget.loop);
_controller.initialize().then((_) {
setState(() {
});
});
_controller.play();
}
#override
void dispose() {
_controller.dispose();
_timer?.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
return _controller.value.isInitialized ? Stack(
children: <Widget>[
VideoPlayer(_controller),
// Add a play or pause button overlay
Visibility(
visible: _onTouch,
child: Container(
color: Colors.grey.withOpacity(0.5),
alignment: Alignment.center,
child: FlatButton(
shape: CircleBorder(side: BorderSide(color: Colors.white)),
child: Icon(_controller.value.isPlaying ? Icons.pause : Icons.play_arrow, color: Colors.white,),
onPressed: () {
_timer?.cancel();
// pause while video is playing, play while video is pausing
setState(() {
_controller.value.isPlaying ?
_controller.pause() :
_controller.play();
});
// Auto dismiss overlay after 1 second
_timer = Timer.periodic(Duration(milliseconds: 1000), (_) {
setState(() {
_onTouch = false;
});
});
},
),
),
)
],
) : Container();
}
It is definitely possible to create custom pause / play buttons.
The only thing you need is a reference to your VideoPlayerController and call the pause() or play() methods.
In order to do so I would remove the use of a FutureBuilder and init the video player at the creation of your StatefulWidget:
VideoPlayerController _controller;
#override
void initState() {
super.initState();
_controller = VideoPlayerController.network(widget.networkVideo,
videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true))
..initialize().then((_) {
// Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
setState(() {
});
});
}
#override
Widget build(BuildContext context) {
return Center(
child: _controller.value.isInitialized
? AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: Stack(
alignment: Alignment.bottomCenter,
children: <Widget>[
VideoPlayer(_controller),
ClosedCaption(text: null),
// Here you can also add Overlay capacities
VideoProgressIndicator(
_controller,
allowScrubbing: true,
padding: EdgeInsets.all(3),
colors: VideoProgressColors(
playedColor: Theme.of(context).primaryColor),
),
],
),
)
: Container(
height: 250,
child: Center(
child: CircularProgressIndicator(),
),
),
);
}
I have a chat page where a user can send texts, images, audio and video as messages to the other users. Everything works fine except for the Audio and Video. In my chat application, a user is allowed to pick audio files using the file_picker plugin. After the user picks an audio file, it is then uploaded to a server. The server than sends back the audio file using a socket event. My app listens to the socket event and generates the message view depending on the type of messages. If its a text message, it is shown in a text widget. If its a audio message, it is shown in an audio widget. audio_player plugin is used to play the audio file. When the user uploads an audio, after a little time the audio file is shown to both the users chatting in the same room. Everything works fine if it is a single audio file. If the user uploads another audio file as in a 2nd audio, the audio controller replaces all my audio files in the chat view with the last audio uploaded. The code used for audio is below:
//for audio files (This code has been placed in _ChatPageState)
AnimationController _animationIconController1;
AudioCache audioCache;
AudioPlayer audioPlayer;
Duration _duration = new Duration();
Duration _position = new Duration();
Duration _slider = new Duration(seconds: 0);
double durationValue;
bool isSongPlaying = false;
bool isPlaying = false;
Later, the variables are initialsed in initState()
//for audio inside initState
_position = _slider;
_animationIconController1 = AnimationController(
vsync: this,
duration: Duration(milliseconds: 750),
reverseDuration: Duration(milliseconds: 750),
);
audioPlayer = new AudioPlayer();
audioCache = new AudioCache(fixedPlayer: audioPlayer);
audioPlayer.durationHandler = (d) => setState(() {
_duration = d;
});
audioPlayer.positionHandler = (p) => setState(() {
_position = p;
});
Next, if the user receives a audio file from socket listener an audio widget is presented, so that the users can play the file:
Widget audioMessage(int index) {
return Container(
width: MediaQuery.of(context).size.width * 0.6,
height: 30,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
GestureDetector(
onTap: () {
setState(() {
isPlaying ? _animationIconController1.reverse() : _animationIconController1.forward();
isPlaying = !isPlaying;
});
// Add code to pause and play the music.
if (!isSongPlaying){
audioPlayer.play('some hidden link/file/${messages[index].message}');
setState(() {
isSongPlaying = true;
});
} else {
audioPlayer.pause();
setState(() {
isSongPlaying = false;
});
}
},
child: ClipOval(
child: Container(
color: Colors.pink[600],
child: Padding(
padding: const EdgeInsets.all(8.0),
child: AnimatedIcon(
icon: isSongPlaying ==false ? AnimatedIcons.play_pause : AnimatedIcons.pause_play,
size: 14,
progress: _animationIconController1,
color: Colors.white,
),
),
),
),
),
Slider(
activeColor: Colors.white,
inactiveColor: Colors.grey,
value: _position.inSeconds.toDouble(),
max: _duration.inSeconds.toDouble(),
onChanged: (double value) {
seekToSeconds(value.toInt());
value = value;
},
),
],
),
);
}
I think the problem is with my audio and animation controllers. It is only initiated for only one player. So how can i go around this problem. A user can upload as many audios as he wants. How can i dynamically create audio controllers for as many audio files that are uploaded by the user?
So, I have accomplished this by moving all my audio codes into a separate dart file. See below for audio implementation:
class PlayAudio extends StatefulWidget {
final String url;
const PlayAudio({Key key, this.url}) : super(key: key);
#override
_PlayAudioState createState() => _PlayAudioState();
}
class _PlayAudioState extends State<PlayAudio> with TickerProviderStateMixin{
//for audio files
AnimationController _animationIconController1;
AudioCache audioCache;
AudioPlayer audioPlayer;
Duration _duration = new Duration();
Duration _position = new Duration();
Duration _slider = new Duration(seconds: 0);
double durationValue;
bool isSongPlaying = false;
bool isPlaying = false;
#override
void initState() {
// TODO: implement initState
super.initState();
//for audio inside initState
_position = _slider;
_animationIconController1 = new AnimationController(
vsync: this,
duration: new Duration(milliseconds: 750),
reverseDuration: new Duration(milliseconds: 750),
);
audioPlayer = new AudioPlayer();
audioCache = new AudioCache(fixedPlayer: audioPlayer);
audioPlayer.durationHandler = (d) => setState(() {
_duration = d;
});
audioPlayer.positionHandler = (p) => setState(() {
_position = p;
});
print('audio widget: ' + widget.url);
}
#override
void dispose() {
// TODO: implement dispose
super.dispose();
audioPlayer.dispose();
}
void seekToSeconds(int second) {
Duration newDuration = Duration(seconds: second);
audioPlayer.seek(newDuration);
}
#override
Widget build(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width * 0.6,
height: 30,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
GestureDetector(
onTap: () {
setState(() {
isPlaying ? _animationIconController1.reverse() : _animationIconController1.forward();
isPlaying = !isPlaying;
});
// Add code to pause and play the music.
if (!isSongPlaying){
audioPlayer.play('${widget.url}');
setState(() {
isSongPlaying = true;
});
} else {
audioPlayer.pause();
setState(() {
isSongPlaying = false;
});
}
},
child: ClipOval(
child: Container(
color: Colors.pink[600],
child: Padding(
padding: const EdgeInsets.all(8.0),
child: AnimatedIcon(
icon: AnimatedIcons.play_pause,
size: 14,
progress: _animationIconController1,
color: Colors.white,
),
),
),
),
),
Slider(
activeColor: Colors.white,
inactiveColor: Colors.grey,
value: _position.inSeconds.toDouble(),
max: _duration.inSeconds.toDouble(),
onChanged: (double value) {
seekToSeconds(value.toInt());
value = value;
},
),
],
),
);
}
}
This is now a separate class. Now, to play multiple audio files all I am doing is calling the above file into my main.dart wherever it is needed. Make sure you call 'new' everytime you want to play a new audio file, just pass an url. See below:
new PlayAudio(url: 'some URL');
Hope this helps to play multiple audio files in the same page. FYI, i am using https://pub.dev/packages/audioplayers
How do I make a AnimatedController that repeats, but starts up with a curve animation.
Code:
AnimationController :
var _animating = false;
AnimationController _rotationAnimationController;
Animation<double> _animation;
#override
void initState() {
super.initState();
_rotationAnimationController = AnimationController(
duration: Duration(milliseconds: 2000),
vsync: this,
);
_animation =
Tween<double>(begin: 0, end: 4 * pi ).animate(_rotationAnimationController)
..addListener(() {
setState(() {});
});
}
#override
void dispose() {
_rotationAnimationController.dispose();
super.dispose();
}
Button :
GestureDetector(
onTap: () {
Duration startStopTime = Duration(seconds: 3);
if(_animating) {
_rotationAnimationController.animateBack(1, duration: startStopTime, curve: Curves.easeOut);
setState(() {});
} else {
_rotationAnimationController.repeat() //This needs to start with a curve
}
setState(() {
_animating = !_animating;
});
},
child: [...]
),
If you could also make it so that when the repeat stops, it does that with a curve, that would be amazing too :)
Thanks for the help allready :D
The best I understood your question , you want a CurvedAnimation right ? This means your animation will repeat but follow a specific curve . So here the best I could do for you :
Define your AnimationController like this :
Animation<double> _animation;
AnimationController _animationController;
#override
void initState() {
super.initState();
_animationController =
AnimationController(vsync: this, duration: Duration(seconds: 2))
..addListener(() {
setState(() {});
});
final Animation curve =
CurvedAnimation(parent: _animationController, curve: Curves.easeOut);
_animation = Tween<double>(begin: 0, end: pi * 4).animate(curve);
}
and your GestureDetector like this :
GestureDetector(
onTap: () {
if (_animationController.isAnimating)
{
_animationController.animateBack(0,duration: Duration(seconds: 2), curve: Curves.easeIn);
}
else {
_animationController.repeat();
}
},
child: [...]
),
Edit :
I used a TweenAnimationBuilder to have the effect you want :
import 'package:flutter/material.dart';
import 'dart:math' as math;
class TweenAnimatedBuilderRotate extends StatefulWidget {
TweenAnimatedBuilderRotate({Key key}) : super(key: key);
#override
TweenAnimatedBuilderRotateState createState() =>
TweenAnimatedBuilderRotateState();
}
class TweenAnimatedBuilderRotateState
extends State<TweenAnimatedBuilderRotate> {
double _newAngle = 0;
Curve curveThatChanges = Curves.easeIn;
bool isAnimating = false;
int _millsecs = 2000;
void onCompletion() {
if (isAnimating) {
_newAngle += 4 * math.pi;
curveThatChanges = Curves.linear;
_millsecs = 1000;
setState(() {});
} else {
_newAngle = 0;
_millsecs = 2000;
}
}
void onContainerTap() {
if (isAnimating) {
isAnimating = false;
_newAngle = _newAngle;
setState(() {});
} else {
curveThatChanges = Curves.easeIn;
_newAngle += 4 * math.pi;
isAnimating = true;
setState(() {});
}
}
#override
Widget build(BuildContext context) {
return TweenAnimationBuilder(
tween: Tween<double>(begin: 0, end: _newAngle),
duration: Duration(milliseconds: _millsecs),
onEnd: () => onCompletion(),
curve: curveThatChanges,
builder: (
BuildContext ctx,
double angle,
Widget child,
) {
_newAngle = angle;
return Center(
child: Transform(
transform: Matrix4.identity()..rotateZ(_newAngle),
alignment: FractionalOffset.center,
child: GestureDetector(
child: Container(
color: Colors.blueGrey,
width: 200,
height: 200,
),
onTap: () => onContainerTap(),
),
),
);
});
}
}
You can refer to this Medium article to understand about how TweenAnimationdBuilder Works. You can also modify _millsecs variable to speed up/down the animation. Pass TweenAnimatedBuilderRotate() in the body parameter of the Scaffold(...).
I just build and deployed a flutter web app. The problem I encountered is that it doesn't scroll when I press arrow keys, also there is no scroll bar. (Only 2 figure gesture scrolling is possible)
I'm using SingleChildScrollView() with the column as its child.
Is there a way to implement them?
Or just one of them?
The code from Karan works, but when the app is in Debug Mode, instead of using the event.logicalKey.debugName == "Arrow Up", we could use event.logicalKey == LogicalKeyboardKey.arrowUp which works in both the debug and release mode.
class _MyKeyboardScrollingPageState extends State<MyKeyboardScrollingPage> {
final ScrollController _controller = ScrollController();
final FocusNode _focusNode = FocusNode();
void _handleKeyEvent(RawKeyEvent event) {
var offset = _controller.offset;
if (event.logicalKey == LogicalKeyboardKey.arrowUp) {
setState(() {
if (kReleaseMode) {
_controller.animateTo(offset - 200, duration: Duration(milliseconds: 30), curve: Curves.ease);
} else {
_controller.animateTo(offset - 200, duration: Duration(milliseconds: 30), curve: Curves.ease);
}
});
}
else if (event.logicalKey == LogicalKeyboardKey.arrowDown) {
setState(() {
if (kReleaseMode) {
_controller.animateTo(offset + 200, duration: Duration(milliseconds: 30), curve: Curves.ease);
} else {
_controller.animateTo(offset + 200, duration: Duration(milliseconds: 30), curve: Curves.ease);
}
});
}
}
#override
void dispose() {
_focusNode.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: RawKeyboardListener(
autoFocus = true,
focusNode = _focusNode,
onKey: _handleKeyEvent,
child: SingleChildScrollView(
controller: _controller,
child: SomeAwesomeWidget(),
),
),
);
}
}
I found one solution ...
Hope this helps someone with the same issue...
Using RawKeyboardListener(), we can listen to any keyboard stroke.
class _MyHomePageState extends State<MyHomePage> {
final ScrollController _controller = ScrollController();
final FocusNode _focusNode = FocusNode()
#override
void dispose() {
_focusNode.dispose();
super.dispose();
}
void _handleKeyEvent(RawKeyEvent event) {
var offset = _controller.offset; //Getting current position
if (event.logicalKey.debugName == "Arrow Down") {
setState(() {
if (kReleaseMode) {
//This block only runs when the application was compiled in release mode.
_controller.animateTo(offset + 50,
duration: Duration(milliseconds: 200), curve: Curves.ease);
} else {
// This will only print useful information in debug mode.
// print(_controller.position); to get information..
_controller.animateTo(offset + 50,
duration: Duration(milliseconds: 200), curve: Curves.ease);
}
});
} else if (event.logicalKey.debugName == "Arrow Up"){
setState(() {
if (kReleaseMode) {
_controller.animateTo(offset - 50,
duration: Duration(milliseconds: 200), curve: Curves.ease);
} else {
_controller.animateTo(offset - 50,
duration: Duration(milliseconds: 200), curve: Curves.ease);
}
});
#override
Widget build(BuildContext context) {
return Scaffold(
body: RawKeyboardListener(
autofocus: true,
focusNode: _focusNode,
onKey: _handleKeyEvent,
child: SingleChildScrollView(
controller: _controller,
child:...
}
}
You can wrap ScrollBar to SingleChildScrollView to show scroll bar, like this:
Scrollbar(
child: SingleChildScrollView(
child: Container(),
));
For the answers that are mentioned above to work, you need the following imports:
import 'package:flutter/services.dart';
import 'package:flutter/foundation.dart';
The Most simplest way to Scroll using mouse or Keyboard Arrow keys on Flutter Web is
ListView(
primary: true,
scrollDirection: Axis.vertical,
children:
No need to pass any ScrollController
I have a Row that consists x number of Button widgets. So when user clicks on one of the Button I need to fade out the other buttons and the slide the clicked button to the starting position(0,0) of the Row layout. I managed to get the fade out animation to work.
final _opacityTween = Tween<double>(begin: 1, end: 0);
_opacityAnimation = _opacityTween.animate(CurvedAnimation(
parent: _controller,
curve: new Interval(0.00, 0.50, curve: Curves.linear)
// curve: Curves.ease
));
// And using the opacityAnimation value in Opacity widget.
return Opacity(
opacity: _opacityAnimation.value,
child: child,
);
However I have been unsuccessful to using Animation classes and widgets to slide the child widget to the start position of the Row. Basically I am not sure what kind of classes could be useful to achieve such animation. I can use the Tween and set the end position to (0, 0) and use Translation to achieve it, however I am not sure how to set the begin position of the Animation when I initialise it as the position of a child is not known.
Try this solution. I thing it is almost what you need. You just have to add SizeTransition widget.
Ask if you have any question.
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
int _pressedButton;
Animation<double> _animation;
AnimationController _controller;
#override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
);
_animation = Tween<double>(begin: 1.0, end: 0.0)
.animate(_controller)
..addListener(() {
setState(() { });
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Test"),
),
body: Column(
children: <Widget>[
Row(
children: <Widget>[
_animatedButton(1),
_animatedButton(2),
_animatedButton(3),
_animatedButton(4)
]
),
Text(_pressedButton != null ? "$_pressedButton button pressed" : "No button pressed"),
RaisedButton(
child: Text("Reset"),
onPressed: () {
_controller.reset();
}
)
]
)
);
}
Widget _animatedButton(int button) {
if (button != _pressedButton) {
return SizeTransition(
sizeFactor: _animation,
axis: Axis.horizontal,
child: Opacity(
opacity: _animation.value,
child: _button(button)
)
);
} else {
return _button(button);
}
}
Widget _button(int button) {
return RaisedButton(
onPressed: () => onButtonClick(button),
child: Text("Button $button")
);
}
void onButtonClick(int button) {
setState(() {
_pressedButton = button;
});
_controller.forward();
}
}
UPDATE 1
Check one more way to do what you need. There are few different things in code below.
Change SingleTickerProviderStateMixin to TickerProviderStateMixin.
Use two animation controllers and animations (for fading out of unselected buttons and sliding left selected button).
Run animations sequentially (see animation tweens listeners).
Add bool _buttonSelected to track completion of animations.
Use _buttonSelected to build correct widget (Widget _buttonsWidget())
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
int _pressedButton = -1;
bool _buttonSelected = false;
Animation<double> _animationFadeOut;
AnimationController _controllerFadeOut;
Animation<double> _animationSlideLeft;
AnimationController _controllerSlideLeft;
#override
void initState() {
super.initState();
_initFadeOutAnimation();
_initSlideLeftAnimation();
}
void _initFadeOutAnimation() {
_controllerFadeOut = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
);
_animationFadeOut = Tween<double>(begin: 1.0, end: 0.0)
.animate(_controllerFadeOut)
..addListener(() {
setState(() {
if (_controllerFadeOut.isCompleted && !_controllerSlideLeft.isAnimating) {
_controllerSlideLeft.forward();
}
});
});
}
void _initSlideLeftAnimation() {
_controllerSlideLeft = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
);
_animationSlideLeft = Tween<double>(begin: 1.0, end: 0.0)
.animate(_controllerSlideLeft)
..addListener(() {
setState(() {
if (_controllerSlideLeft.isCompleted) {
_buttonSelected = true;
}
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Test"),
),
body: Column(
children: <Widget>[
_buttonsWidget(),
Text(_pressedButton != null ? "$_pressedButton button pressed" : "No button pressed"),
RaisedButton(
child: Text("Reset"),
onPressed: () {
_controllerFadeOut.reset();
_controllerSlideLeft.reset();
_pressedButton = -1;
_buttonSelected = false;
}
)
]
)
);
}
Widget _buttonsWidget() {
if (_buttonSelected) {
return Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[_buttonWidget(_pressedButton)]
);
} else {
return Row(
children: <Widget>[
_animatedButtonWidget(1),
_animatedButtonWidget(2),
_animatedButtonWidget(3),
_animatedButtonWidget(4)
]
);
}
}
Widget _animatedButtonWidget(int button) {
if (button == _pressedButton) {
return _buttonWidget(button);
} else {
return SizeTransition(
sizeFactor: _animationSlideLeft,
axis: Axis.horizontal,
child: Opacity(
opacity: _animationFadeOut.value,
child: _buttonWidget(button)
)
);
}
}
Widget _buttonWidget(int button) {
return RaisedButton(
onPressed: () => _onButtonClick(button),
child: Text("Button $button")
);
}
void _onButtonClick(int button) {
setState(() {
_pressedButton = button;
});
_controllerFadeOut.forward();
}
}