I'm trying to show a video inside a container, videos could exceed container's aspect ratio, so the they need to be cropped.
With images it is simple as that:
Image.network(someUrl, fit: BoxFit.cover)
but how to do it with VideoPlayer?
my current code looks like this:
Stack(
children: [
Container(
width: double.infinity,
height: double.infinity,
child: ShowcaseVideoPlayer(someUrl)
),
Material(
color: Colors.transparent,
child: InkWell(
onTap: () {},
),
),
],);
class ShowcaseVideoPlayer extends StatefulWidget {
final String url;
ShowcaseVideoPlayer(this.url);
#override
_ShowcaseVideoPlayerState createState() => _ShowcaseVideoPlayerState();
}
class _ShowcaseVideoPlayerState extends State<ShowcaseVideoPlayer> {
VideoPlayerController _controller;
Future<void> _initializeVideoPlayerFuture;
#override
void initState() {
_controller = VideoPlayerController.network(widget.url);
_initializeVideoPlayerFuture = _controller.initialize();
_controller.setVolume(0);
super.initState();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _initializeVideoPlayerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
_controller.play();
// If the VideoPlayerController has finished initialization, use
// the data it provides to limit the aspect ratio of the VideoPlayer.
return AspectRatio(
aspectRatio: _controller.value.aspectRatio,
// Use the VideoPlayer widget to display the video.
child: VideoPlayer(_controller),
);
} else {
// If the VideoPlayerController is still initializing, show a
// loading spinner.
return Center(),
);
}
},
);
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
}
I need a stack as I will have to show other things over the video. I tried without the stack and the player maintains the aspect ratio of the video, basically letter-boxing it. With the stack, the video stratches, losing the original aspect ratio:
to be clear, what I want is this:
EDIT: I tried random things on it and managed to achieve something, I removed the Container and wrapped the VideoPlayer like this:
Stack(
children: [
OverflowBox(
child: Wrap(
children: [
ShowcaseVideoPlayer(someUrl)
],
),
)]);
now the the video is actually cropped, but not centered (I see the upper part of the video instead of the central one). Wrapping Wrap in a Center and setting alignment: Alignment.center on OverfowBox have no effect. Any idea?
Related
class Animation extends State<ConnectToFriend> {
late RiveAnimationController riveAnimationController;
#override
void initState() {
super.initState();
riveAnimationController = SimpleAnimation('idle');
setState(() {});
}
#override
Widget build(BuildContext context) {
return LoaderOverlay(
useDefaultLoading: false,
overlayOpacity: 0.6,
overlayWidget: Center(child: CircularProgressIndicator()),
child: Scaffold(
body: Positioned(
child: Container(
child: RiveAnimation.asset(
'assets/animations/4054-8407-polito (2).riv',
controllers: [riveAnimationController],
animations: const ['idle'],
fit: BoxFit.cover,
onInit: (p0) => setState(() {}))))),
);
}
}
enter image description here
enter image description here
enter image description here
i can't play the polito animation in rive flutter, it's static
can anyone help me?I am new to Rive and because there is close to no good documentation for Rive 2 I wanted to ask here.
here is link https://rive.app/community/4054-8407-polito/
Summarize the problem
I need help finding a Flutter construct which will allow me to repeatedly check the return value of an API function call for getting the current time of a playing video (position). My goal is to update the Slider() to the position of the current video. I am using plugin flutter_vlc_player for playing back videos.
The most important part of the code below is the videoPlayerController.getPosition() function call. I need a way to repeatedly call this function and get the latest value. This is what I am struggling with.
The SeekBar class instantiated is the Slider() I am updating.
I think I am close to a solution as StreamBuilder is meant to update based on events. Also if I perform hot refresh of app after playing a video, the Slider updates once.
What I am seeing is the stream function is called twice but returns null each time because the video isn't playing yet. I need the stream function to be called while the video is playing.
I/flutter (29465): snapshot: null
I/flutter (29465): snapshot: null
One last thing: videoPlayerController.getPosition() is a Future.
Describe what you've tried:
I tried using StreamBuilder() and FutureBuilder() but I got the same results. The current position is only fetched twice when I need it to be continuously fetched during video playback. I checked the Flutter documentation on StreamBuilder but their example only shows when there is one item to be grabbed and not multiple. I need to rebuild the Slider() widget based on the value returned from function repeatedly.
Show some code:
VlcPlayerController videoPlayerController = VlcPlayerController.network(
'rtsp://ip_addr:8554/test',
hwAcc: HwAcc.FULL,
autoPlay: true,
options: VlcPlayerOptions(
video: VlcVideoOptions(),
rtp: VlcRtpOptions(['--rtsp-tcp'],),
extras: ['--h264-fps=30']
),
);
await Navigator.push(context,
MaterialPageRoute(builder: (context) =>
Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
backgroundColor: Colors.blueAccent,
title: const Text("Playback")),
body: Center(
child: Column(
children: [
VlcPlayer(
controller: videoPlayerController,
aspectRatio: 16/9,
placeholder: const Center(child: CircularProgressIndicator()),
),
StatefulBuilder(builder: (context, setState) {
return Row(
children: [
TextButton(
child: Icon(isPlaying ? Icons.play_arrow : Icons.pause),
style: ButtonStyle(backgroundColor: MaterialStateProperty.all<Color>(Colors.blueAccent),
foregroundColor: MaterialStateProperty.all<Color>(Colors.white)),
onPressed: () {
setState(() {
if(videoPlayerController.value.isPlaying)
{
isPlaying = true;
videoPlayerController.pause();
}
else {
isPlaying = false;
videoPlayerController.play();
}
});
}
),
Text("${videoPlayerController.value.position.inMinutes}:${videoPlayerController.value.position.inSeconds}",
style: const TextStyle(color: Colors.white)),
],
);
}),
StreamBuilder<Duration>(
stream: Stream.fromFuture(videoPlayerController.getPosition()),
builder: (BuildContext context, AsyncSnapshot <Duration> snapshot) {
Duration position = snapshot.data ?? const Duration();
print('snapshot: ${snapshot.data?.inSeconds.toDouble()}');
return Column(
children: [
SeekBar(
duration: const Duration(seconds: 5),
position: position,
onChangeEnd: (newPosition)
{
videoPlayerController.seekTo(newPosition);
},
)
],
);
}
),
]
)
),
)
)
);
Thank you for reading and help. I am still learning Flutter/Dart so any references to helpful classes will be great.
Is there a reason you want to use StreamBuilder in this case? You can use a StatefulWidget and add a listener to the controller. Then update the position inside that listener.
Use this to add listener:
videoPlayerController.addListener(updateSeeker);
Also make sure to remove the listener in dispose method:
videoPlayerController.removeListener(updateSeeker);
Here is the updateSeeker method:
Future<void> updateSeeker() async {
final newPosition = await videoPlayerController.getPosition();
setState(() {
position = newPosition;
});
}
Here is an example of a widget that plays the video and shows its position in a Text widget:
class VideoPlayer extends StatefulWidget {
const VideoPlayer({Key? key}) : super(key: key);
#override
_VideoPlayerState createState() => _VideoPlayerState();
}
class _VideoPlayerState extends State<VideoPlayer> {
final videoPlayerController = VlcPlayerController.network(url);
var position = Duration.zero;
#override
void initState() {
super.initState();
videoPlayerController.addListener(updateSeeker);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
VlcPlayer(
aspectRatio: 16 / 9,
controller: videoPlayerController,
),
Text(position.toString()),
],
),
);
}
Future<void> updateSeeker() async {
final newPosition = await videoPlayerController.getPosition();
setState(() {
position = newPosition;
});
}
#override
void dispose() {
videoPlayerController.removeListener(updateSeeker);
super.dispose();
}
}
how to make this animation on flutter
icon and text
the default state is the icon is shown and the text is disappears
when click the icon: the icon goes up and text goes under icon and appears
otherwise the icon goes in center and the text disappears
like this video
https://i.imgur.com/S0LXr3o.mp4
https://drive.google.com/file/d/1nwpgjOM_6TUaaVaSdsIZp0oYi4CdWSMR/view?usp=sharing
you can use AnimationController and AnimationBuilder combined with Stack + Positioned
or you can even use the Transform Widget with the same concept!
I've write an example to make the animation
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class AnimationExerciseScreen extends StatefulWidget {
const AnimationExerciseScreen({Key? key}) : super(key: key);
#override
_AnimationExerciseScreenState createState() =>
_AnimationExerciseScreenState();
}
class _AnimationExerciseScreenState extends State<AnimationExerciseScreen>
with SingleTickerProviderStateMixin {
#override
void initState() {
super.initState();
animationController = AnimationController(
vsync: this,
duration: Duration(seconds: 3),
);
animationController.forward();
}
#override
void dispose() {
animationController.dispose();
super.dispose();
}
late final AnimationController animationController;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: 100,
child: AnimatedBuilder(
animation: animationController,
builder: (context, child) => Stack(
children: [
Positioned(
top: 0 + (40 * animationController.value),
child: Icon(Icons.cloud),
),
Positioned(
bottom: 0 + (40 * animationController.value),
child: Opacity(
opacity: 1 - animationController.value,
child: Text('Cloud'),
),
),
],
),
),
),
],
),
),
);
}
}
video link:
https://imgur.com/RJg5PWw
Explanation:
the animation controller has a value of 0 to 1 with the type of double, which will represent the amount percentage of the animation
in the above example, I'm using 3 seconds duration of the animation controller so the animation will be visible to our eyes easily, so I use animationController.forward to play the animation at the initState
note: the placement of the animation is not optimized for performance, this example is just for example to understand how the animation works
if you want to optimize the animation, you can put your widget to child attribute of the AnimationBuilder for more info you can read them here and here and here and so much more! you can explore tons of articles to improve your flutter app's performance!
Im developing a app that when the user enters the first screen is a loading screen where initialize somethings from the Provider, the user cant input anything in this screen, when the loading finishes i want to push a new screen without the user "clicking" for it.
In this code, im actually given a delay of 3 seconds for the _login.getStoredEmail() to run and set a variable inside LoginController which in the next screen i consume, but of course this wont work everytime, eventually will break.
class GeneralSplashScreen extends StatefulWidget {
#override
_GeneralSplashScreenState createState() => _GeneralSplashScreenState();
}
class _GeneralSplashScreenState extends State<GeneralSplashScreen> {
#override
void initState() {
Future.delayed(
Duration(
seconds: 3,
),
() {
Navigator.pushReplacementNamed(context, kRoutes.login);
},
);
super.initState();
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
final LoginController _login = Provider.of<LoginController>(context);
_login.getStoredEmail();
return Scaffold(
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 60),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Image.asset(
'lib/assets/images/logo.png',
fit: BoxFit.contain,
),
Text(
"Business Mananger",
textAlign: TextAlign.end,
style: TextStyle(
fontFamily: kFontFamily.montserrat,
fontSize: 10,
),
),
LoadingBar(),
],
),
),
);
}
}
Navigate to a different screen inside the initState() method.
initState() {
initializeAndNavigate()
}
initializeAndNavigate() async {
await initializeSomething();
Navigator.of(context) ...
}
Declare initState
Future _future;
#override
void initState() {
// TODO: implement initState
super.initState();
_future = doStuff();
}
Use FutureBuilder
FutureBuilder(
future: _future,
builder: (_, dataSnapshot) {
if (dataSnapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator()); // here add loading screen
} else {
return Dashboard();
}
},
)
I'm trying to position some text over a video.
The video is currently taking as much space as possible while retaining it's original aspect ratio. Ideally I'd like it to keep doing this as I want the video to resize to fit the browser window.
I'm assuming I need to get get the height/width of the video dynamically..
If anyone knows how to make a video play automatically that would be great as well - I'm using the video_player package.
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
drawer: ResponsiveLayout.isSmallScreen(context) ? NavDrawer() : null,
body: Container(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
NavBar(),
Body(),
Footer(),
],
),
),
),
);
}
}
class Body extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ResponsiveLayout(
largeScreen: LargeScreen(),
mediumScreen: LargeScreen(),
smallScreen: LargeScreen(),
);
}
}
class LargeScreen extends StatefulWidget {
#override
_LargeScreenState createState() => _LargeScreenState();
}
class _LargeScreenState extends State<LargeScreen> {
VideoPlayerController _videoPlayerController;
Future<void> _initializeVideoPlayerFuture;
#override
void initState() {
_videoPlayerController = VideoPlayerController.asset(
'assets/videos/video.mp4',
);
_initializeVideoPlayerFuture = _videoPlayerController.initialize();
super.initState();
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 40),
child: Column(
children: <Widget>[
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 VideoPlayer.
return AspectRatio(
aspectRatio: _videoPlayerController.value.aspectRatio,
// Use the VideoPlayer widget to display the video.
child: VideoPlayer(_videoPlayerController),
);
} else {
// If the VideoPlayerController is still initializing, show a
// loading spinner.
return Center(child: CircularProgressIndicator());
}
},
),
],
),
);
}
#override
void dispose() {
super.dispose();
_videoPlayerController.dispose();
}
}
you can wrap your Widgets in stack widget and use positioning to position your text
AspectRatio(
aspectRatio: _videoPlayerController.value.aspectRatio,
// Use the VideoPlayer widget to display the video.
child: Stack(children:<Widget>[
VideoPlayer(_videoPlayerController),
Positioned(bottom:10,left:10,
child:Text("my text here))
])
)
update
If anyone knows how to make a video play automatically that would be great as well - I'm using the video_player package.
you can set the state of your video using your controller
_videoPlayerController.value.isPlaying
I want the video to resize to fit the browser window.
you can wrap your video in a SizedBox.expand which Creates a box that will become as large as its parent allows.
sized box constructors