Chewie video player widget has satisfactory alignment and overall UI for iOS (red borders are added by me for testing purposes):
However, on android it looks like this:
So everything is positioned a bit lower than where they should, causing the sound control to collide with the progress bar.
build method to reproduce:
#override
Widget build(BuildContext context) {
return (_videoPlayerController.value.isInitialized &&
_chewieController.aspectRatio != null)
? ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(10)),
child: AspectRatio(
aspectRatio: _chewieController.aspectRatio!,
child: Chewie(controller: _chewieController)))
: AspectRatio(
aspectRatio: POST_IMAGE_ASPECT_RATIO,
child: Center(
child: DefaultCircularProgressIndicator(),
));
}
I encounter this in the page where I view the posts items in my application (they have images, texts, videos, comments and more of that kind of generic items). I thought some other widget might have been causing the issue so I tried using various widgets to wrap the video player. Using SafeArea actually seemed to solve the misalignment issue but then I get another interesting problem which, on iOS looks like:
And on Android it looks like:
So SafeArea causes some unwanted parts to appear.
build method to reproduce:
#override
Widget build(BuildContext context) {
return (_videoPlayerController.value.isInitialized &&
_chewieController.aspectRatio != null)
? SafeArea(
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(10)),
child: AspectRatio(
aspectRatio: _chewieController.aspectRatio!,
child: Chewie(controller: _chewieController))))
: AspectRatio(
aspectRatio: POST_IMAGE_ASPECT_RATIO,
child: Center(
child: DefaultCircularProgressIndicator(),
));
}
I draw the red borders where I call this widget, I did not include that part of the code simply because I only added a container that only draws red borders and sets this video player as its child.
What could be causing this issue with SafeArea, and is there a way to fix the alignment issue on android without adding these whitespaces to the widget? Thanks in advance.
Related
I have a view that contains a Column of Widgets. One of the Widgets contains a button that will open a bottom sheet. Within that bottom sheet, a user can tap a TextField and open the keyboard which will keep the bottom sheet above the keyboard.
When I do this as-is, I get Bottom Overflowed by XXX Pixels. The yellow box is behind my bottom sheet, right above the keyboard.
I have tried wrapping the Column in a SingleChildScrollView but when I do that all of the Widgets in my Column disappear.
I have also tried wrapping in a Scaffold & that did not work either:
example:
Scaffold(
resizeToAvoidBottomInset: false, // tried setting to true as well
body: Column...
Any suggestions?
Here's some of the base setup:
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildWidget1(),
_buildWidget2(),
_buildWidget3(),
// When wrapped in a SingleChildScrollView
// this seems to be making everything in the column
// disappear...
Expanded(child: Container()),
etc.
],
);
}
void _bottomSheetButtonPressed(context) {
showModalBottomSheet(
barrierColor: Colors.transparent,
backgroundColor: Colors.transparent,
context: context,
isScrollControlled: true,
builder: (context) {
return Padding(
padding:
EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Container(
decoration: BoxDecoration(
color: Colors.transparent,
borderRadius: BorderRadius.only(
topLeft: const Radius.circular(24),
topRight: const Radius.circular(24),
),
),
child: _showBottomSheetItemsWidget(),
),
);
},
);
}
The colors are transparent just so I can see what is happening behind the bottom sheet.
So, with this I am getting the Bottom Overflowed issue... and that is what I am trying to resolve.
Update:
After further debugging, I do see what I believe is making all my Widgets disappear. I have an Expanded Widget that has a child of Container to separate some of my Widgets.
The correct solution is indeed to wrap what you see into a scrollable widget such as SingleChildScrollView. But this can happen if and only if the contents of your scrollable widgets aren't infinite.
Indeed, the reason your widget simply "disappear" is an internal widget that forces infinite height (in this case, the Expanded widget), while the parent do not force a maximum height (the SingleChildScrollView), since it expects any number of widgets to be displayed. This causes an intrinsic / conceptual error (if you think about it) and therefore the framework throws an error.
It kinda depends on what you want to achieve, but as a rule of thumb in cases like this chances are that you want to wrap your scrollable widgets inside a SizedBox / Container / ConstrainedBox, so that you specify a height and therefore you force it to be not infinite.
In that case, your child widgets can use the Expanded logic with no issues.
Let me know if that helps.
In the code below, a row of two 300x300 boxes (_Box) wrapped with FittedBox shrinks to fit in the screen. As a result, each box becomes smaller than 300x300.
However, if I get the width of a box in the build() method of _Box using RenderBox.size and LayoutBuilder(), the obtained size is 300.0 and Infinity respectively.
How can I get the actual displayed size?
I'd like to get it inside the _Box class, without it getting passed from the parent.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SafeArea(
child: FittedBox(
child: Row(
children: <Widget>[
_Box(Colors.red),
_Box(Colors.orange),
],
),
),
),
),
);
}
}
class _Box extends StatelessWidget {
const _Box(this.color);
final Color color;
#override
Widget build(BuildContext context) {
RenderBox renderBox = context.findRenderObject();
print(renderBox?.size?.width); // 300.0
return LayoutBuilder(
builder: (_, constraints) {
print(constraints.maxWidth); // Infinity
return Container(
width: 300,
height: 300,
color: color,
);
},
);
}
}
The Flutter constraints object is used to limit how large or small a widget can be rendered, and is usually never really used to get the current size of the widget.
The renderBox.size object is of Size class, and as a result it has both renderBox.size.width and renderBox.size.height as defined getters. Note that these values can only be set once the layout phase of the current view is over: see the findRenderObject() docs page.
This means that you will have to avoid calling findRenderObject() from the build() method. Instead you will have to define a callback function that must execute after the layout process is complete. You can do this using that have widgets that have callback functions like onTap or onSelected. How you implement this and finally get the actual layout size by running the callback function is totally dependent on your use case.
Further recommended reading:
DevelopPaper - Sample code for getting the width and height of screen and widget in Flutter
Flutter on Github - How to get a height of a widget? #16061
Rémi Rousselet's amazing answer explaining his workaround (using an Overlay widget)
I'll answer my own question, although it is not a direct answer.
I couldn't find a way to get the size shrinked by FittedBox, but I realised that I was able to get around it by using Flexible instead.
SafeArea(
child: Row(
children: const [
Flexible(
child: _Box(Colors.red),
),
Flexible(
child: _Box(Colors.orange),
),
],
),
);
#override
Widget build(BuildContext context) {
return ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 300.0),
child: AspectRatio(
aspectRatio: 1.0,
child: ColoredBox(color: color),
),
);
}
It still seems impossible to get the size via RenderBox, and it is now possible with LayoutBuilder. But either way, I didn't need them.
The constraints of the two boxies are shrinked by Flexible if a smaller space is available, but they expand as big as the space allows, so I limited the maximum size using ConstrainedBox and AspectRatio.
I didn't have to stick to FittedBox. I think I was obsessed with the idea of using it and couldn't think of other solutions when I posted the question two years ago.
I am a beginner to Flutter and I tried creating a dummy login page. When I tried to run the app it was flawless on Pixel 3 XL but it was not good in Pixel 2 XL. How would I manage the responsiveness? I came across the MediaQuery widget, but how could I create a completely responsive app for each device? There are many variations among today's mobile devices (like notched display, fullscreen, etc) and thereafter comes the tablets. How could I manage this? Should I write code for each and every resolution?
Here is my design on Pixel 3 XL and Pixel 2 XL
You can use SingleChildScrollView as parent,
SingleChildScrollView(
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Stack(
),
),
);
Edit: Also remove fit: StackFit.expand, from Stack widget.
The reason behind this problem is the sizedbox widget with height 100px.
removing that and using mainaxisalignment as spaceevenly gives the required output.
Here is the modified code:
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomPadding: false,
body: Stack(
fit: StackFit.expand,
children: <Widget>[
bgimage,
Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
logoimage,
userName(),
password(),
forgotPassword(),
loginButton(),
googleSignIn(),
signUp()
],
),
],
),
);
}
This solves the problem.
Best easiest way to make responsive UI for different screen size is Sizer plugin.
Responsive UI in any screen size device also tablet.
Check it this plugin ⬇️
https://pub.dev/packages/sizer
.h - for widget height
.w - for widget width
.sp - for font size
Use .h, .w, .sp after value like this ⬇️
Example:
Container(
height: 10.0.h, //10% of screen height
width: 80.0.w, //80% of screen width
child: Text('Sizer', style: TextStyle(fontSize: 12.0.sp)),
);
I have build many responsive UI with this plugin.
Use this package flutter_next using this you design responsive UI even for desktop web and everything and they have an demo also so you can check there
How to Adjust the screen to all android devices screens in flutter. I have checked in stackoverflow but unfortunately i haven't got any satisfying answer.
Basically, Flutter automatically sets the height and width depending on the device sizes wherever possible. Example - if you have use a list view having 100 items then some devices may show 5 items at a time and some may show 6 items. It is automatically done by Flutter.
The problem comes only when you specify an absolute value for height and/or width. Let's say you've a widget and you specify width as 450. Now, it may fit on the bigger screens but on the small screen (e.g. width 400 points)then you'll see pixel overflow error in the UI.
Now, to solve this problem, you can use MediaQuery.of(context).size.height/width as suggested by LGM.
I think you have some questions about responsive layouts, so i will give you some examples with MediaQuery:
With MediaQuery you can get the device screen width:
double width = MediaQuery.of(context).size.width;
The height:
double height = MediaQuery.of(context).size.height;
as well as the orientation:
Orientation orientation = MediaQuery.of(context).orientation;
and also various information about the device, and then, you can base the layout on orientation, size etc.
Here's an example:
Container(
width: MediaQuery.of(context).orientation == Orientation.portrait ?
MediaQuery.of(context).size.width / 1.3 : MediaQuery.of(context).size.width / 2.1,
child: RaisedButton(
elevation: 0.0,
color: Colors.green,
child: Text("CONTINUE", style: TextStyle(color: Colors.white),),
onPressed: (){
//code of onPressed
);
}
),
),
You can use too Align, to define where the widget should be, Positioned, the properties of Column, Center etc.
One more example, using FittedBox to deal with texts:
#override
Widget build(BuildContext context) {
return Material(
child: SafeArea(
child: Scaffold(
body: ListView(
children: <Widget>[
FittedBox(child: Text("Test"),),
FittedBox(child: Text("Flutter Flutter Flutter"),)
],
),
),
),
);
}
The result:
Portrait:
Landscape:
Scrollbar widget in Flutter seems to have a fixed thickness of 6. I think the Scrollbar looks ugly, it is too thick. Also if I compare Flutter Scrollbar to scrollbars in other apps in my device, they all look different (they look better).
Can I change the Scrollbar thickness without creating a new Scrollbar widget?
I had an idea to force Scrollbar partially out of screen to make it look thinner, but I don't know how to do that.
I am using the Scrollbar with a ListView.
Just change the thickness property from scrollbar theme data:
class MyApp extends StatelessWidget {
final double _thickness = 2;
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.light().copyWith(
scrollbarTheme: ScrollbarThemeData().copyWith(
thickness: MaterialStateProperty.all(_thickness),
)
),
);
}
}
I found a relatively simple workaround which to wrap the Scrollbar in a Stack widget and add to Stack a Container which will hide part of the Scrollbar. There will be a small 'white' margin in the right edge of the screen but you will see similar margin e.g. in OS settings views (at least in Galaxy S7 settings app). Sample code (notice that this is for Android only, not tested in iOS):
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.topRight,
fit: StackFit.loose,
children: <Widget>[
Scrollbar(
child: buildListView(),
),
Container(
width: 3,
color: Theme.of(context).canvasColor,
)
],
);
}