Layout not centered after implementing SizeConfig - flutter

I am using the SizeConfig method to set the height and width of my app's layout but for some reason, after implementing these functions, the layout is showing up all shifted to the left (i.e. not center). I do not see why this is happening, especially as without the SizeConfig functions the layout is centered.
Does anyone know what could be causing this?
class SubscribePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
SizeConfig().init(context);
final ThemeData theme = Theme.of(context);
return Consumer<SubscriptionModel>(
builder: (context, subscriptionModel, _) {
if (subscriptionModel.entitlements != null) {
final subscription = subscriptionModel.entitlements['subscription'];
if (subscription != null) {
final monthlySub = subscription.offerings["monthly"];
if (monthlySub != null) {
return Scaffold(
body: SingleChildScrollView(
child: Container(
height: SizeConfig.blockSizeVertical * 90,
width: SizeConfig.blockSizeHorizontal * 90,
child: Center(
child: Column(
children: <Widget>[
Container(
height: MediaQuery.of(context).size.height/2.5,
child: SvgPicture.asset(
'assets/svg/reading-student.svg'),
),
Padding(padding: EdgeInsets.all(MediaQuery.of(context).size.height/40)),
AutoSizeText(
'Your pizza.',
style: theme.textTheme.title,
textAlign: TextAlign.center,
),
Padding(padding: EdgeInsets.all(MediaQuery.of(context).size.height/40)),
AutoSizeText(
'Your very own pizza.',
style: theme.textTheme.body1,
textAlign: TextAlign.center,
),
Padding(padding: EdgeInsets.all(MediaQuery.of(context).size.height/35)),
PurchaseButton(product: monthlySub),
],
),
),
),
),
);
}
}
}
size_config.dart file:
import 'package:flutter/widgets.dart';
class SizeConfig {
static MediaQueryData _mediaQueryData;
static double screenWidth;
static double screenHeight;
static double blockSizeHorizontal;
static double blockSizeVertical;
void init(BuildContext context) {
_mediaQueryData = MediaQuery.of(context);
screenWidth = _mediaQueryData.size.width;
screenHeight = _mediaQueryData.size.height;
blockSizeHorizontal = screenWidth / 100;
blockSizeVertical = screenHeight / 100;
}
}

The SizeConfig.init() is working as expected. The reason your layout is aligned a little towards left is because you are centering your content inside a container which has a height and width equal to 90% of the screen height and width.
Explanation :-
Let's assume that the screen width is 500 and height is 1000. So after calling the SizeConfig.init() these are the values of the fields -
screenWidth = 500.0
screenHeight = 1000.0
blockSizeHorizontal = 5.0
blockSizeVertical = 10.0
Now when you set the size of the under mentioned container, you have the following values for width and height, 450.0 and 900.0 respectively.
body: SingleChildScrollView(
child: Container(
height: SizeConfig.blockSizeVertical * 90, // 900.0
width: SizeConfig.blockSizeHorizontal * 90, // 450.0
child: Center(
Then you center the rest of content inside this container, which means the content is centered according to the 450 x 900 block rather than the required 500 x 1000 (original screen) block. You can see this by changing the color of the container.
Result of changing the color of the container

Related

Flutter transform rotate inside row / column

I have a Column with some widgets inside.
If I trying to rotate one of them, it behaves like in the stack widget.
But I want to save layout of the rotated widget inside parent widget.
My question is how to revert (return) layout of the rotated widget inside Column, Row, etc... ?
The explanation and code example:
Right layout without rotation:
right layout screenshot
Column(
children: [
Text('Progress'),
SizedBox(height: 8.0,),
LinearProgressIndicator(
value: _counter / 100,
minHeight: 20,
),
],
)
Wrong layout with rotated widget:
wrong layout screenshot
Column(
children: [
Text('Progress'),
SizedBox(height: 8.0,),
Transform.rotate(
angle: -45 / (180 / pi),
child: LinearProgressIndicator(
value: _counter / 100,
minHeight: 20,
),
),
],
)
Wanted result screenshot
Thanks to Yeasin Sheikh for good idea,
task solved, final code here
Transform widget is just painting on UI, doesn't take actual space. You can wrap with SizedBox to provide the height.
If you can tweak the flexibility of your own from
deg2Ran(double deg) => deg / (180 / pi);
double boxHeight() {
final value = (((500 * deg2Ran(angle))) / (deg2Ran(-45))).abs();
return value < 20 ? 20.0 : value; // 20 your min height
}
class SX extends StatefulWidget {
const SX({Key? key}) : super(key: key);
#override
State<SX> createState() => _SXState();
}
class _SXState extends State<SX> {
double angle = -45;
deg2Ran(double deg) => deg / (180 / pi);
double boxHeight() {
final value = (((500 * deg2Ran(angle))) / (deg2Ran(-45))).abs();
return value < 20 ? 20.0 : value; // 20 your min height
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Slider(
min: -180,
max: 180,
value: angle,
onChanged: (v) {
setState(() {
angle = v;
});
}),
Text('Progress'),
SizedBox(
height: 8.0,
),
SizedBox(
height: boxHeight(),
child: Center(
child: Transform.rotate(
angle: deg2Ran(angle),
alignment: Alignment.center,
child: LinearProgressIndicator(
value: 22 / 100,
minHeight: 20,
),
),
),
),
],
),
);
}
}
You can handle overflow and some decoration

Flutter: Content hidden by phones bottom Navigation bar

In my register screen, part of the content(at the bottom) is hidden by the phone's bottom navbar. The content is only visible when I close the bottom navbar.
What I want to achieve is, whenever the bottom navbar is displayed on the phone, I want content that is hidden by it to be pushed upwards for visibility and whenever the navbar is removed from sight, I want the content to remain at it's position.
Here is my code.
class Body extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SizedBox(
width: double.infinity,
child: Padding(
padding:
EdgeInsets.symmetric(horizontal: getProportionateScreenWidth(20)),
child: SingleChildScrollView(
child: Column(
children: [
Text(
"Register Account",
style: headingStyle,
),
Text(
"Complete your details or continue \nwith social media.",
textAlign: TextAlign.center,
),
SizedBox(height: SizeConfig.screenHeight * 0.05), // 5%
SignUpForm(),
SizedBox(height: SizeConfig.screenHeight * 0.03), // 3%
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
SocialCard(
icon: "assets/icons/google-icon.svg",
press: () {},
),
SocialCard(
icon: "assets/icons/facebook-2.svg",
press: () {},
),
SocialCard(
icon: "assets/icons/twitter.svg",
press: () {},
)
]),
SizedBox(height: getProportionateScreenHeight(15)),
Text( <--- HIDDEN FROM VIEW
"By continuing you confirm that you agree with our Term and Condition",
textAlign: TextAlign.center,
)
],
),
),
),
);
}
}
// Get Screen Size
class SizeConfig {
static MediaQueryData _mediaQueryData;
static double screenWidth;
static double screenHeight;
static double defaultSize;
static Orientation orientation;
void init(BuildContext context) {
_mediaQueryData = MediaQuery.of(context);
screenWidth = _mediaQueryData.size.width;
screenHeight = _mediaQueryData.size.height;
orientation = _mediaQueryData.orientation;
}
}
// Get the proportionate height as per screen size
double getProportionateScreenHeight(double inputHeight) {
double screenHeight = SizeConfig.screenHeight;
// 812 is the layout height that designer use
return (inputHeight / 812.0) * screenHeight;
}
Visual of the problem
You can try implementing SafeArea, which takes care of this. This widget acts as a padding for the phones that have this kind of issue with unreachable content. It was originally thought for the iPhones without touchId at first, but if I'm not wrong fixes this problem on Androids as well.
This is the link to the documentation: https://api.flutter.dev/flutter/widgets/SafeArea-class.html
You can wrap your containers (And your scaffold as well), or your widgets individually, as follow:
SafeArea(
minimum: const EdgeInsets.all(15.0),
child: Text('Your Widget'),
)
In your case I would wrap your entire Scaffold into a SafeArea this way:
SafeArea(
child: Scaffold(
...
)
)
Wrap your entire widget inside a SafeArea
This would ensure additional padding is put, to account for System-Based intrusions like the Status Bar, Nav Bar, Notch, etc.

Wrap stacked cards in flutter

I'm trying to stack some playing cards like the image shows. BUT I want the cards to wrap into a new line when there is no more space on the device.
This is what I have so far
class CollectionOfCards extends StatelessWidget {
List<PlayingCard> cards;
CollectionOfCards(this.cards);
#override
Widget build(BuildContext context) {
return Stack(
children: [
...cards
.asMap()
.map(
(key, value) => MapEntry(
key,
Positioned(
child: PlayingCardWidget(value),
left: 35.0 * key,
),
),
)
.values
.toList(),
],
overflow: Overflow.visible,
);
}
}
Any ideas how to do this? I imagine it is somewhat a common task.
Another idea would be to make a fan of cards that can always fit but I imagine thats harder to achieve!
Assume we want to have 2 rows of cards, each containing 10 cards. We need to use Positioned in a way that every card is positioned according to its index in the row(from 0 to 9) and its column(column 0 or column 1). So we need to set top and left in the Positioned widget. The 0.7 * cardHeight in top is for the vertical stacking, you can change that to cardHeight if you don't want to have vertical stacking.
Note: Set width and height of the cards using MediaQuery size. I didn't have the cards so I just made empty containers instead.
class _MyHomePageState extends State<MyHomePage> {
List<Widget> _cards = [];
void _initCards() {
double cardHeight = 400 / 2;
double cardWidth = 2500 / 20;
for (int i = 0; i < 20; i++) {
_cards.add(Positioned(
left: ((i >= 10) ? i - 10 : i) * cardWidth / 2,
top: (i >= 10) ? 0.7 * cardHeight : 0,
child: Card(
elevation: 10,
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
child: Container(width: cardWidth, height: cardHeight),
),
));
}
}
#override
void initState() {
super.initState();
_initCards();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: EdgeInsets.symmetric(horizontal: 15.0),
child: Stack(
children: _cards
),
decoration: BoxDecoration(
color: Colors.yellow,
),
),
);
}
}
Result:
Just for reference. I ended up with solution like this inspired by #Mobina's answer
class CollectionOfCards extends StatelessWidget {
final List<PlayingCard> cards;
final double stackSkew = 0.4;
CollectionOfCards(this.cards);
#override
Widget build(BuildContext context) {
final double widgetWidth = MediaQuery.of(context).size.width - 16;
final double cardWith = PlayingCardWidget.defaultWidth(context);
final int cardsPerRow =
(widgetWidth / cardWith / stackSkew - 1 / stackSkew).floor();
return Stack(
children: [
...cards
.asMap()
.map(
(index, card) {
return MapEntry(
index,
Positioned(
child: Draggable(
childWhenDragging: Container(),
feedback: PlayingCardWidget(card: card),
child: PlayingCardWidget(
card: card,
),
),
left: stackSkew *
cardWith *
(index - (cardsPerRow) * (index / cardsPerRow).floor()),
top: (card.heightFromWidth(cardWith) + 2) *
(index / cardsPerRow).floor(),
),
);
},
)
.values
.toList(),
],
overflow: Overflow.visible,
);
}
}
It seems to work flawless.
One way you can do this is by passing left as well as top parameter to Positioned calculated based on -
Screen width
Card index ("key" in your code)
Card width
Card height
See the demo on dartpad - https://dartpad.dev/d36e018c1f1c7a6cd4b91d5b09e69a7c
Try using Wrap() widget inside a Column()
Column(
children: <Widget>[
Wrap(
children: <Widget>[
//your card widget
]
)
]
)

Need to animate the size of the child of a FractionallySizedBox

I'm playing with some theories on how to approach a layout. The simple version is that I have a Container and I'm allowing its size to be determined by its parent FractionallySizedBox. On orientation change (calculated intentionally), the size calculated by the FractionallySizedBox changes correctly but it's instantaneous, not animated.
I would like it to animate the size change, but please realize that AnimatedSize won't work the way I need because its behavior is actually more like an Animated Clipper. The child of the AnimatedSize actually changes instantly, it's the AnimatedSize that animates its size to reveal more or less of the child as it animates. This is not the desired behavior. I'm looking to zoom in and out.
The reason the orientation is calculated is this is intended to work on all platforms, and desktops and web don't report an orientation afaik. But by comparing height to width, I can determine which is wider, width or height.
Thanks for any help with getting the size to zoom in and out.
import 'dart:math';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Adaptive Layouts',
theme: ThemeData(
primarySwatch: Colors.teal,
),
home: AdaptiveRotationTest(title: 'Adaptive Rotation Test'),
);
}
}
class AdaptiveRotationTest extends StatelessWidget {
AdaptiveRotationTest({Key key, this.title}) : super(key: key);
final String title;
double screenHeight;
double screenWidth;
double borderWidth;
bool isPortrait;
#override
Widget build(BuildContext context) {
screenHeight = MediaQuery.of(context).size.height;
screenWidth = MediaQuery.of(context).size.width;
borderWidth = min(screenWidth, screenHeight) * 0.02;
isPortrait = screenHeight > screenWidth ? true : false;
return Scaffold(
appBar: AppBar(
title: const Text('Adaptive Rotation Test'),
),
body: Container(
height: double.infinity,
width: double.infinity,
color: Colors.yellow,
child: Container(
padding: EdgeInsets.all(borderWidth),
child: Stack(
children: <Widget>[
AnimatedAlign(
duration: const Duration(milliseconds: 500),
alignment: Alignment(
isPortrait == true ? -0.9 : 0.9,
isPortrait == true ? -0.9 : 0.9,
),
///
/// Need to animate the size, without knowing the size since it's calculated as heightFactor and widthFactor.
/// Also, AnimatedSize behaves more like an AnimatedClipper over a Container that changes size instantly, which
/// is not what we're going for. We're looking to zoom in or out.
child: FractionallySizedBox(
heightFactor: isPortrait == true ? 0.3 : 0.5,
widthFactor: .5,
child: AnimatedContainer(
duration: const Duration(milliseconds: 500),
color: isPortrait == true ? Colors.purple : Colors.green,
),
),
),
],
),
),
),
);
}
}

Sizing elements to percentage of screen width/height

Is there a simple (non-LayoutBuilder) way to size an element relative to screen size (width/height)? For example: how do I set the width of a CardView to be 65% of the screen width.
It can't be done inside the build method (obviously) so it would have to be deferred until post build. Is there a preferred place to put logic like this?
This is a supplemental answer showing the implementation of a couple of the solutions mentioned.
FractionallySizedBox
If you have a single widget you can use a FractionallySizedBox widget to specify a percentage of the available space to fill. Here the green Container is set to fill 70% of the available width and 30% of the available height.
Widget myWidget() {
return FractionallySizedBox(
widthFactor: 0.7,
heightFactor: 0.3,
child: Container(
color: Colors.green,
),
);
}
Expanded
The Expanded widget allows a widget to fill the available space, horizontally if it is in a row, or vertically if it is in a column. You can use the flex property with multiple widgets to give them weights. Here the green Container takes 70% of the width and the yellow Container takes 30% of the width.
If you want to do it vertically, then just replace Row with Column.
Widget myWidget() {
return Row(
children: <Widget>[
Expanded(
flex: 7,
child: Container(
color: Colors.green,
),
),
Expanded(
flex: 3,
child: Container(
color: Colors.yellow,
),
),
],
);
}
Supplemental code
Here is the main.dart code for your reference.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("FractionallySizedBox"),
),
body: myWidget(),
),
);
}
}
// replace with example code above
Widget myWidget() {
return ...
}
FractionallySizedBox may also be useful.
You can also read the screen width directly out of MediaQuery.of(context).size and create a sized box based on that
MediaQuery.of(context).size.width * 0.65
if you really want to size as a fraction of the screen regardless of what the layout is.
This might be a little more clear:
double width = MediaQuery.of(context).size.width;
double yourWidth = width * 0.65;
Hope this solved your problem.
There are several possibilities:
1- The first one is the use of the MediaQuery :
Code :
MediaQuery.of(context).size.width //to get the width of screen
MediaQuery.of(context).size.height //to get height of screen
Example of use :
Container(
color: Colors.yellow,
height: MediaQuery.of(context).size.height * 0.65,
width: MediaQuery.of(context).size.width,
)
Output :
2- The use of FractionallySizedBox
Creates a widget that sizes its child to a fraction of the total available space.
Example :
FractionallySizedBox(
widthFactor: 0.65, // between 0 and 1
heightFactor: 1.0,
child:Container(color: Colors.red
,),
)
Output :
3- The use of other widgets such as Expanded , Flexible and AspectRatio and more .
You could build a Column/Row with Flexible or Expanded children that have flex values that add up to the percentages you want.
You may also find the AspectRatio widget useful.
There is many way to do this.
1. Using MediaQuery : Its return fullscreen of your device including appbar,toolbar
Container(
width: MediaQuery.of(context).size.width * 0.50,
height: MediaQuery.of(context).size.height*0.50,
color: Colors.blueAccent[400],
)
2. Using Expanded : You can set width/height in ratio
Container(
height: MediaQuery.of(context).size.height * 0.50,
child: Row(
children: <Widget>[
Expanded(
flex: 70,
child: Container(
color: Colors.lightBlue[400],
),
),
Expanded(
flex: 30,
child: Container(
color: Colors.deepPurple[800],
),
)
],
),
)
3. Others Like Flexible and AspectRatio and FractionallySizedBox
First get the size of screen.
Size size = MediaQuery.of(context).size;
After this you can get width and multiply it with 0.5 to get 50% of screen width.
double width50 = size.width * 0.5;
But problem generally comes in height, by default when we use
double screenHeight = size.height;
The height we get is global height which includes StatusBar + notch + AppBar height. So, in order to get the left height of the device, we need to subtract padding height (StatusBar + notch) and AppBar height from total height. Here is how we do it.
double abovePadding = MediaQuery.of(context).padding.top;
double appBarHeight = appBar.preferredSize.height;
double leftHeight = screenHeight - abovePadding - appBarHeight;
Now we can use following to get 50% of our screen in height.
double height50 = leftHeight * 0.5
MediaQuery.of(context).size.width
you can use MediaQuery with the current context of your widget and get width or height like this
double width = MediaQuery.of(context).size.width
double height = MediaQuery.of(context).size.height
after that, you can multiply it with the percentage you want
Use the LayoutBuilder Widget that will give you constraints that you can use to obtain the height that excludes the AppBar and the padding. Then use a SizedBox and provide the width and height using the constraints from the LayoutBuilder
return LayoutBuilder(builder: (context2, constraints) {
return Column(
children: <Widget>[
SizedBox(
width: constraints.maxWidth,
height: constraints.maxHeight,
...
if you are using GridView you can use something like Ian Hickson's solution.
crossAxisCount: MediaQuery.of(context).size.width <= 400.0 ? 3 : MediaQuery.of(context).size.width >= 1000.0 ? 5 : 4
Code :
MediaQuery.of(context).size.width //to get the width of screen
MediaQuery.of(context).size.height //to get height of screen
You can use the Align widget. The heightFactor and widthFactor parameters are multiplied by the size of the child widget. Here is an example that will make a widget with a fixed height in% ratio
Align(
alignment: Alignment.topCenter,
heightFactor: 0.63,
widthFactor: ,
child: Container(
width: double.infinity,
),
Use scaler to define the layout width and height in percentage
dependencies:
scaler: ^1.1.0+1
After setting this in pubspec.yaml you can use this by following the code -
import 'package:scaler/scaler.dart';
Example After import use this -
import 'package:scaler/scaler.dart';
#override
Widget build(BuildContext context) {
/**
* Container with 25% width of screen
* and 25% height of screen
*/
return Container(
height: Scaler.height(0.25, context),
width: Scaler.width(0.25, context),
child: Container()
);
}
To more detail about this
https://pub.dev/packages/scaler
For width
double width = MediaQuery.of(context).size.width;
double yourWidth = width * 0.75;
For Height
double height = MediaQuery.of(context).size.height;
double yourHeight = height * 0.75;
If you don't want static height and width just use Expanded widget\
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatelessWidget(),
);
}
}
class MyStatelessWidget extends StatelessWidget {
const MyStatelessWidget({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Expanded Row Sample'),
),
body: Center(
child: Row(
children: <Widget>[
Expanded(
flex: 2,
child: Container(
color: Colors.amber,
height: 100,
),
),
Container(
color: Colors.blue,
height: 100,
width: 50,
),
Expanded(
child: Container(
color: Colors.amber,
height: 100,
),
),
],
),
),
);
}
}
I am surprised that no one has yet suggested LayoutBuilder in 2023 which gives you access to the parent's width BoxConstraints.constraints.maxWidth, which is the most versatile method.
Expanded can only set the percentage of the available spacing, but what if you really want to set a percentage based on the actual parent's widget only, not the entire screen width, what if have a fixed width widget in a row, even a more complex, what if you also want an Expanded to expand the remaining Row.
MediaQuery.of(context).size.width is relative to the entire screen, not to the actual parent.
FractionallySizedBox works similarly but you can't put it in Row
Also, this method the perfect emulation of CSS % unit of measurement.
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, BoxConstraints constraints) {
return SizedBox(
width: 470,
child: Row(
children: [
SizedBox(
width: 200,
child: Icon(
Icons.link,
color: Colors.white,
),
),
SizedBox(
width: constraints.maxWidth*0.6,
child: Icon(
Icons.message,
color: Colors.white,
),
),
Expanded(
child: Icon(
Icons.phone,
color: Colors.white,
),
),
SizedBox(
width: constraints.maxWidth*0.3,
child: Icon(
Icons.account_balance,
color: Colors.white,
),
),
],
),
);
}
);
}
}
In this example, we are setting a parent widget of 470 width with Row inside. In the Row, one element has a 200 fixed width, another with a 60% of the parent with 470, another with 30% of that same parent, and another expanding any remaining space.