Camera preview is stretching when changing aspect ratio from 16:9 to 4:3.
I am changing previewSize on IconButton click
camera plugin version: 0.8.1+3
Device information: Zenfone Max Pro M1 • android-arm64 • Android 11 (API 30)
Default preview size of camera is 1280x720 and aspect ration is 16/9. i am changing aspect ratio by changing preview size to 320x240. so my aspect ratio will be 4/3.
I am using below code:
class CameraScreenGithub extends StatefulWidget {
const CameraScreenGithub({Key? key}) : super(key: key);
#override
_CameraScreenGithubState createState() => _CameraScreenGithubState();
}
class _CameraScreenGithubState extends State<CameraScreenGithub> {
CameraController? _cameraController;
Future<void>? _initializeControllerFuture;
List<CameraDescription> _cameraDescription = [];
#override
void initState() {
super.initState();
_initializeCamera();
}
Future<void> _initializeCamera() async {
_cameraDescription.addAll(await availableCameras());
_setCamera(_cameraDescription[0]);
}
_setCamera(CameraDescription description) {
_cameraController = CameraController(description, ResolutionPreset.high);
_initializeControllerFuture = _cameraController?.initialize();
if (mounted) setState(() {});
}
#override
void dispose() {
_cameraController?.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: SafeArea(
child: Column(
children: [
Expanded(
child: Stack(
children: [
Center(
child: _cameraPreview(),
),
Positioned(
top: 20,
left: 20,
right: 20,
child: _aspectRatioButton(),
),
],
),
),
],
),
),
);
}
Widget _cameraPreview() => ClipRRect(
borderRadius: BorderRadius.circular(16),
child: FutureBuilder<void>(
future: _initializeControllerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return CameraPreview(_cameraController!);
}
return Container();
},
),
);
Widget _aspectRatioButton() => IconButton(
icon: Icon(
Icons.aspect_ratio,
color: Colors.white,
),
onPressed: () {
_cameraController?.value =
_cameraController!.value.copyWith(previewSize: Size(320, 240));
setState(() {});
},
);
}
try to follow the code what i provided:
camera aspect ratio
you need to use Transform scale
Related
I've been building an app in flutter for the past while and recently I added camera functionality. I've a button on my home page that directs me to a camera page with the camera preview, on the camera preview page I have a home icon which directs back to the home page.
I've noticed when I click the home page from the camera page it doesn't render correctly, I've multiple calls to the home page throughout the project and they all work fine its only when accessing from the camera page. I added then((value) => setState(() {})); to try and reset the state of the home page and I can see its being called but with no luck regarding the rendering[![enter image description here][1]][1]
I've added a picture of the Home Screen render issue, its worth noting im getting no errors on Xcode.
Camera screen code:
import 'package:camera/camera.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:moodful/Screens/HomeScreen/home_screen.dart';
import 'package:moodful/Screens/Login/login_screen.dart';
import 'package:moodful/main.dart';
import '../constants.dart';
class CameraPage extends StatefulWidget {
final List<CameraDescription>? cameras;
const CameraPage({this.cameras, Key? key}) : super(key: key);
#override
_CameraPageState createState() => _CameraPageState();
}
class _CameraPageState extends State<CameraPage> {
late CameraController controller;
// used to increment camera switch tap
int cameraIconTap = 0;
XFile? pictureFile;
#override
void initState() {
super.initState();
controller = CameraController(
widget.cameras![0],
ResolutionPreset.veryHigh,
);
controller.initialize().then((_) {
if (!mounted) {
return;
}
setState(() {});
});
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
if (!controller.value.isInitialized) {
return const SizedBox(
child: Center(
child: CircularProgressIndicator(),
),
);
}
return Scaffold(
appBar: AppBar(
backgroundColor: kPrimaryColor,
centerTitle: true,
title: Text('AppBar'),
leading: IconButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => HomeScreen()),
).then((value) => setState(() {}));
print("refresh done ");
},
icon: Icon(Icons.home),
),
actions: [
IconButton(
onPressed: () {
//increment on tap, calculate modulus, pass to widget.camera. 0 == rear camera, 1 == front camera
cameraIconTap++;
int modulusTap;
modulusTap = cameraIconTap % 2;
if (widget.cameras!.length > 1) {
setState(() {
controller = CameraController(
widget.cameras![modulusTap], ResolutionPreset.veryHigh);
});
controller.initialize().then((_) {
if (!mounted) {
return;
}
setState(() {});
});
} else {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text('No secondary camera found'),
duration: Duration(seconds: 2),
));
}
},
icon: const Icon(Icons.cameraswitch),
),
],
),
body: Center(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(8.0, 20.0, 8.0, 8.0),
child: Column(
children: [
Center(
child: SizedBox(
height: //400
size.height / controller.value.aspectRatio,
width: size.width * 1.3 / controller.value.aspectRatio,
child: AspectRatio(
aspectRatio: controller.value.aspectRatio,
child: CameraPreview(controller),
),
),
),
],
),
),
//),
Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
onPressed: () async {
pictureFile = await controller.takePicture();
setState(() {});
},
child: const Text('Capture Image'),
),
),
if (pictureFile != null)
Image.network(
pictureFile!.path,
height: 200,
)
//Android/iOS
// Image.file(File(pictureFile!.path)))
],
)));
}
}
Home Screen code:
import 'package:camera/camera.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:moodful/components/background.dart';
import 'package:moodful/Screens/Login/login_screen.dart';
import 'package:moodful/components/camera_screen.dart';
import 'package:moodful/main.dart';
class HomeScreen extends StatefulWidget {
HomeScreen({Key? key}) : super(key: key);
#override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
Future<DocumentSnapshot<Map<String, dynamic>>>? _fetchedData;
#override
void initState() {
super.initState();
_fetchedData = getData();
}
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return FutureBuilder(
future: _fetchedData,
builder: (BuildContext context,
AsyncSnapshot<DocumentSnapshot<Map<String, dynamic>>> snapshot) {
if (snapshot.hasData) {
return BackgroundLogoRight(
text: "Home",
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Spacer(),
Expanded(
flex: 5,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
flex: 10,
child: Text(
"${snapshot.data!.data()!["firstName"]} \n\n "
"Glad you're back, please enter a mood for today.\n\n "
"Be sure to check the insights page for the most up to date mood analysis\n\n"
"The more you know the more control you have",
style: TextStyle(color: Colors.black, fontSize: 15),
textAlign: TextAlign.center,
),
),
],
),
),
Expanded(
flex: 5,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
onTap: () async {
await availableCameras().then(
(value) => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CameraPage(
cameras: value,
),
),
),
);
}, // handle your image tap here
child: Image.asset(
"assets/images/recordMoodLogo.jpg",
width: size.width * 0.5,
fit: BoxFit.cover, // this is the solution for border
height: 110.0,
),
),
],
),
),
],
),
);
} else {
return CircularProgressIndicator();
}
},
);
}
Future<DocumentSnapshot<Map<String, dynamic>>> getData() async {
var currentUser = FirebaseAuth.instance.currentUser;
return await FirebaseFirestore.instance
.collection('USER_TABLE')
.doc(currentUser!.uid)
.get();
}
}
Thanks!
[1]: https://i.stack.imgur.com/pbC0w.png
I am trying to play videos obtained from the web with its original aspect ratio. With the code I have the videos are returned with a stretched aspect ratio. I am not sure why this is. I have been doing research but cannot find a solution. How do I fix this?
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
class VideoTile1 extends StatefulWidget {
const VideoTile1({Key? key}) : super(key: key);
#override
_VideoTileState createState() => _VideoTileState();
}
class _VideoTileState extends State<VideoTile1> {
late VideoPlayerController _videoController;
late Future _intitializeVideoPlayer;
#override
void initState() {
_videoController = VideoPlayerController.network(
'https://res.cloudinary.com/nifty-gateway/video/upload/v1610827323/A/Billelis/%CE%A0%CE%91%CE%98%CE%9F%CE%A3_qyarem.mp4');
_intitializeVideoPlayer = _videoController.initialize();
_videoController.setLooping(true);
_videoController.play();
super.initState();
}
#override
void dispose() {
_videoController.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return Container(
child: FutureBuilder(
future: _intitializeVideoPlayer,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return VideoPlayer(_videoController);
} else {
return Container(color: Colors.grey[800]);
}
},
),
);
}
}
You can try using AspectRatio Widget with aspectRatio: _videoController.value.aspectRatio,
VideoPlayer widget wrapper
https://api.flutter.dev/flutter/widgets/AspectRatio-class.html
I am also new to flutter, here is my way to use and play/pause video , i hope can help you.
return _videoPlayerController.value.isInitialized
? Center(
child: GestureDetector(
onTap: () {
setState(() {
_videoPlayerController.value.isPlaying
? _videoPlayerController.pause()
: _videoPlayerController.play();
});
},
child: AspectRatio(
aspectRatio: _videoPlayerController.value.aspectRatio,
child: Stack(
children: [
VideoPlayer(
_videoPlayerController,
),
_videoPlayerController.value.isPlaying
? Container()
: Center(
child: Icon(
Icons.play_arrow,
size: 80,
color: Colors.white.withOpacity(0.4),
),
)
],
),
),
),
)
: const CircularProgressIndicator();
Am able to show and hide the widget i want but it keeps flickering or rebuilding its self every time.
i just want to show the the capture icon button when the compass degree reaches 190 degree
this is my main widget
late List<CameraDescription> cameras;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
cameras = await availableCameras();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: CameraApp(),
);
}
}
class CameraApp extends StatefulWidget {
#override
_CameraAppState createState() => _CameraAppState();
}
class _CameraAppState extends State<CameraApp> {
String? imagePath;
XFile? imageFile;
late CameraController controller;
late Future<void> _initializeControllerFuture;
int? angleResult;
bool showCaptureButton = false;
String? getLocation;
bool k = false;
void getAngleFromCompass(newResult) {
WidgetsBinding.instance!.addPostFrameCallback((_) {
setState(() {
angleResult = newResult;
});
});
}
void getLocationRes(newResult) {
getLocation = newResult;
}
#override
void initState() {
super.initState();
controller = CameraController(
// Get a specific camera from the list of available cameras.
cameras[0],
// Define the resolution to use.
ResolutionPreset.high,
imageFormatGroup: ImageFormatGroup.yuv420,
);
_initializeControllerFuture = controller.initialize().then((value) {
setState(() {});
});
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
void _captureImage() async {
await takePicture().then((filePath) {
if (mounted) {
setState(() {
imagePath = filePath;
});
}
});
}
Widget cameraWidget(context) {
var camera = controller.value;
final size = MediaQuery.of(context).size;
var scale = size.aspectRatio * camera.aspectRatio;
if (scale < 1) scale = 1 / scale;
return Transform.scale(
scale: scale,
child: Center(
child: CameraPreview(controller),
),
);
}
#override
Widget build(BuildContext context) {
if (!controller.value.isInitialized) {
return Container();
}
theCompassApp(getAngleFromCompass) keeps flikering here
return Scaffold(
body: FutureBuilder(
future: _initializeControllerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Stack(
children: [
cameraWidget(context),
Align(
alignment: Alignment.topCenter,
child: LocationApp(getLocationRes),
),
Align(
child: CompassApp(getAngleFromCompass),
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
color: Color(0xAA333639),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
angleResult == 190 ? IconButton(
onPressed: () => _captureImage(),
iconSize: 40,
icon: Icon(
Icons.camera_alt,
color: Colors.white,
),
) : Text(''),
],
),
),
)
],
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
),
);
}
Compass App Widget
class CompassApp extends StatefulWidget {
final getAngleValue;
CompassApp(this.getAngleValue);
#override
_CompassAppState createState() => _CompassAppState();
}
class _CompassAppState extends State<CompassApp> {
bool _hasPermissions = false;
#override
void initState() {
super.initState();
_fetchPermissionStatus();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
backgroundColor: Colors.transparent,
body: Builder(builder: (context) {
if (_hasPermissions) {
return Column(
children: <Widget>[
Expanded(child: _buildCompass()),
],
);
} else {
return Text('');
}
}),
),
);
}
Widget _buildCompass() {
return StreamBuilder<CompassEvent>(
stream: FlutterCompass.events,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 30),
child: Text('Error reading heading not support'),
);
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
double? direction = snapshot.data!.heading;
int ang = direction!.round();
Compass angle passed to main widget here
widget.getAngleValue(ang);
if (direction.isNaN)
return Center(
child: Text("Device does not have sensors !"),
);
return Material(
color: Colors.transparent,
child: Column(
children: [
RotatedBox(
quarterTurns: 1,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 41),
child: Text(
'$ang',
style: TextStyle(
fontSize: 50,
color: Colors.white,
backgroundColor: Colors.black26),
),
),
),
],
),
);
},
);
}
void _fetchPermissionStatus() {
Permission.locationWhenInUse.status.then((status) {
if (mounted) {
setState(() => _hasPermissions = status == PermissionStatus.granted);
}
});
}
}
Have you tried Visibility or Opacity?
Visibility
Visibility(
visible: true, //false for invisible
child: Text('I am visible'),
),
Opacity
Opacity(
opacity: 1.0, //0.0 for invisible
child: Text('I am visible'),
),
My camera preview is currently stretched vertically as per the screenshot attached.
The code to the camera page is below, does anyone know how to adjust the code so that the camera preview does not appear to be stretched?
I need it to work for iOS and Android, and for every device.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
import 'dart:io';
import 'package:flutter/services.dart';
class Camera extends StatefulWidget {
Function setData;
Camera({Key key, this.setData}) : super(key: key);
#override
_CameraScreenState createState() => _CameraScreenState();
}
class _CameraScreenState extends State<Camera> {
CameraController controller;
List cameras;
int selectedCameraIndex;
String imgPath;
var image;
Future _openGallery() async {
image = await controller.takePicture();
if (widget.setData != null) {
widget.setData(File(image.path));
}
}
#override
void initState() {
super.initState();
SystemChrome.setEnabledSystemUIOverlays([]);
availableCameras().then((availableCameras) {
cameras = availableCameras;
if (cameras.length > 0) {
setState(() {
selectedCameraIndex = 0;
});
_initCameraController(cameras[selectedCameraIndex]).then((void v) {});
} else {
print('No camera available');
}
}).catchError((err) {
print('Error :${err.code}Error message : ${err.message}');
});
}
Future _initCameraController(CameraDescription cameraDescription) async {
if (controller != null) {
await controller.dispose();
}
controller = CameraController(cameraDescription, ResolutionPreset.high);
controller.addListener(() {
if (mounted) {
setState(() {});
}
if (controller.value.hasError) {
print('Camera error ${controller.value.errorDescription}');
}
});
try {
await controller.initialize();
} on CameraException catch (e) {}
if (mounted) {
setState(() {});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Stack(
children: <Widget>[
_cameraPreviewWidget(),
_cameraControlWidget(context)
],
),
),
);
}
Widget _cameraPreviewWidget() {
if (controller == null || !controller.value.isInitialized) {
return const Text(
'Loading',
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
fontWeight: FontWeight.w900,
),
);
}
final size = MediaQuery.of(context).size;
final deviceRatio = size.width / size.height;
return Stack(children: <Widget>[
Positioned.fill(
child: new AspectRatio(
aspectRatio: controller.value.aspectRatio,
child: new CameraPreview(controller),
),
),
]);
}
Widget _cameraControlWidget(context) {
return Container(
child: Align(
alignment: Alignment.center,
child: FloatingActionButton(
child: Icon(
Icons.center_focus_strong,
size: 39,
color: Color(0xd),
),
backgroundColor: Color(0xff33333D),
onPressed: () {
_openGallery();
Navigator.pop(context);
},
)
));
}
}
and here is the image as to how it is rendering. The button in the middle takes the photo.
I have struggled with the camera preview in Flutter as well. It is a pain to work with. This is what I am using right now. Hopefully it will help you
class CameraPreview extends StatelessWidget {
const CameraPreview({Key? key, this._cameraController}) : super(key: key);
final CameraController _cameraController;
#override
Widget build(BuildContext context) {
return ClipRect(
child: OverflowBox(
alignment: Alignment.center,
child: FittedBox(
fit: BoxFit.cover,
child: SizedBox(
height: 1,
child: AspectRatio(
aspectRatio: 1 / _cameraController.value.aspectRatio,
child: CameraPreview(_cameraCcontroller),
),
),
),
),
);
}
}
Can anyone help?
Currently, the Text that I am displaying over a video has a fixed size and position.
I'm wondering how I can change this dynamically/responsively to match the size of its parent widget (the Video).
I tried a method using a GlobalKey but got an error, I think it's because the video hadn't loaded..
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 &&
!_videoPlayerController.value.isBuffering) {
// 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: Stack(
children: <Widget>[
VideoPlayer(_videoPlayerController),
Positioned(
bottom: 20,
left: 20,
child: FittedBox(
child: Text(
'Text over\na video',
style: TextStyle(
color: Colors.white,
fontSize:50),
),
),
)
],
),
);
} else {
// If the VideoPlayerController is still initializing, show a
// loading spinner.
return Center(child: CircularProgressIndicator());
}
},
),
],
),
);
}
#override
void dispose() {
super.dispose();
_videoPlayerController.dispose();
}
}
LayoutBuilder can provide you with width and height properties which corresponds to the currently available space. Check this documentation here. It provides the builder with a BoxConstraints instance as in here. You can use this information to size your text.
Check the Align widget. It can place the child on specific location within the parent widget's coordinate system. In your case it will be on the coordinates of the Stack widget.
I would try something like the following.
Wrap the Text widget inside a Align widget and use FractionalOffset to place align the widget. You can directly use the Alignment instance also. The origin will vary vary for both the approach. Check the docs here
Then Wrap my Align widget inside a LayoutBuilder widget to get the available size and decide my font size based on it. Something like fontSize: constraints.maxWidth / 25
Below is sample working code.
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
);
}
}
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(),
// );
return LargeScreen();
}
}
class LargeScreen extends StatefulWidget {
#override
_LargeScreenState createState() => _LargeScreenState();
}
class _LargeScreenState extends State<LargeScreen> {
VideoPlayerController _videoPlayerController;
Future<void> _initializeVideoPlayerFuture;
#override
void initState() {
_videoPlayerController = VideoPlayerController.network(
'http://www.sample-videos.com/video123/mp4/720/big_buck_bunny_720p_20mb.mp4',
);
_initializeVideoPlayerFuture =
_videoPlayerController.initialize().then((onValue) {
setState(() {});
});
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 &&
!_videoPlayerController.value.isBuffering) {
// 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: Stack(
children: <Widget>[
VideoPlayer(_videoPlayerController),
LayoutBuilder(
builder: (context, constraints) {
return Align(
// this decides the position of the text.
alignment: FractionalOffset(0.05, 0.95),
child: FittedBox(
child: Text(
'Text over\na video',
style: TextStyle(
color: Colors.white,
// here font size is ratio of the maxwidth available for this widget.
fontSize: constraints.maxWidth / 25,
),
),
),
);
},
)
],
),
);
} else {
// If the VideoPlayerController is still initializing, show a
// loading spinner.
return Center(child: CircularProgressIndicator());
}
},
),
FloatingActionButton(
onPressed: () {
setState(() {
_videoPlayerController.value.isPlaying
? _videoPlayerController.pause()
: _videoPlayerController.play();
});
},
child: Icon(
_videoPlayerController.value.isPlaying
? Icons.pause
: Icons.play_arrow,
),
),
],
),
);
}
#override
void dispose() {
super.dispose();
_videoPlayerController.dispose();
}
}
It's easily accessible via MediaQuery.of(context).size (Documentation).
Remember that you have to call inside your build method since it demands the context