I am trying to make the button below resize depending on different screen sizes but retain the same ratios (i.e. text and button size should change proportionately so they look the same on all screens). I am using the AutoSizeText package to resize the text based on screensize but the text doesn't seem to getting smaller on the smaller screen which might be causing the button to resize oddly.
[![button 1][1]][1] - lager screen
[![button 2][2]][2] - smaller screen
I have tried using a mediaquery to size the button with height and width but that does not seem to be working.
Is there a recommended way of doing this?
class PurchaseButton extends StatelessWidget {
final Product product;
PurchaseButton({Key key, #required this.product}) : super(key: key);
#override
Widget build(BuildContext context) {
double deviceWidth = MediaQuery.of(context).size.width;
Container(
margin: EdgeInsets.symmetric(horizontal: 0.05 * deviceWidth),
child: Row(
children: <Widget>[
Expanded(
child: MaterialButton(
// height: SizeConfig.blockSizeVertical * 6,
onPressed: () async {
await Provider.of<SubscriptionModel>(context).makePurchase(product);
},
child: Text('Join now! Only ${product.priceString}',
style: Theme.of(context).textTheme.body1.copyWith(
fontWeight: FontWeight.w600, fontSize: 0.03 * deviceWidth)),
color: Theme.of(context).primaryColor,
),
),
],
),
);
return Container();
}
}
Many ways to do so, here's a suggestion:
Wrap your widget with this tree of widgets:
Container: to manipulate the width according to the screen size,
-- Row: we need it to force Expanded to work,
---- Expanded: will Expand its content to the whole space it has,
------[the widget you want to expand]
#override
Widget build(BuildContext context) {
double deviceWidth = MediaQuery.of(context).size.width;
double deviceHight = MediaQuery.of(context).size.height;
Container(
// If the button size(Row) is 90% then we give margin 5% + 5% like this
margin: EdgeInsets.symmetric(horizontal: 0.05 * deviceWidth),
// We need a Row in order to "Expanded" to work
child: Row(
children: <Widget>[
// Use "Expanded" if you want the button to fill the Row's size
// Use "Flexible" if you want the button to fit the text inside size.
Expanded(
child: MaterialButton(
onPressed: () {
print('Hi');
},
child: Text(
'Join now! Only...',
style: Theme.of(context)
.textTheme
.body1
.copyWith(fontWeight: FontWeight.w600),
),
color: Theme.of(context).primaryColor,
),
),
],
),
);
Regarding AutoSizeText it takes the size of text's container into consideration, not the screen size, my suggestion is to use regular Text(..) widget with font size taken from MediaQuery.of(context).size.width
e.g
child: Text(
'Join now! Only...',
style: Theme.of(context)
.textTheme
.body1
.copyWith(
fontWeight: FontWeight.w600,
fontSize: 0.03 * deviceWidth,
),
),
Related
In the screen, I have a Column, it has a cusotm made widget of specific height. Then, I have Expanded, in which I have a TabBar which has three tabs.
In one of those tabs, I want to show a list. First, I have a padding, which contains column. The column has some text, which should remain at top and the list should be shown in the space which is remaining. I am using Expanded for that, but it is not working.
I can't use ListView directly, and also can't use expanded. It is only working when I am giving it a container of fix size. Now, in different screens, it will look different. So, I want to take all of the remaining space and build the list there. Code for reference -
Here is the doubts screen, which is one of the tabs of main screen -
import 'package:flutter/material.dart';
import 'package:my_board_plus/size_config.dart';
import 'package:my_board_plus/styles.dart';
import '../../api_handling/api_fetch/fetch_doubt_questions.dart';
import '../../data_models/doubt_question_model.dart';
class NewDoubtsScreen extends StatefulWidget {
const NewDoubtsScreen({Key? key}) : super(key: key);
#override
State<NewDoubtsScreen> createState() => _NewDoubtsScreenState();
}
class _NewDoubtsScreenState extends State<NewDoubtsScreen> {
late Future<List<DoubtQuestionModel>> doubtQuestionsList;
#override
void initState() {
doubtQuestionsList = fetchDoubtQuestion();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: backgroundColor2,
floatingActionButton: Container(
width: getProportionateScreenWidth(130),
height: getProportionateScreenHeight(50),
decoration: BoxDecoration(
color: brandPurple,
shape: BoxShape.rectangle,
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: Center(
child: Text(
'? My Doubts',
style: TextStyle(
color: Colors.white,
fontSize: 15,
),
),
),
),
body: Padding(
padding: EdgeInsets.only(top: 8.0, left: 5),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Trending Doubts',
style: TextStyle(
color: Colors.white,
),
),
Text(
'View all',
style: TextStyle(
color: brandYellow,
decoration: TextDecoration.underline
),
),
],
),
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Container(
height: getProportionateScreenHeight(530),
width: double.infinity,
color: Colors.red,
),
),
],
),
),
);
}
}
The red area that you are seeing is the one. I want it to occupy whole area available in the phone screen, so I can show list in it which should be scrollable. In this case, it is occupying all, but in different screens, it might not. So, please give me some suggestions.
You can try to give the height of the container like
height: double.infinity
Or you can give the height of it with substracting the other height from the screen size like
height: MediaQuery.of(context).size.height - getProportionateScreenHeight(50) //the heigth size that you give the other widget that top of it
try to wrap your Padding widget with the Expanded widget,
Expanded widget in column will take the rest of the space of the screen.
also no need to give height to Container widget, so you can remove getProportionateScreenHeight(530) this one
I am learning flutter, and faced this issue:
I want the equal button to stretch to the left and cover the whole green area as shown in the picture.
The equal button is in one column and the other four buttons are in another column. And in turn, both of these columns are in a row. i.e
Column:
Row:
Column 1:
Equal-Button // If I warp it in expended, it goes out of sight.
Column 2:
the other four buttons
What is the proper way to fix it, not just hacks. How would you do it?
Here is the whole code:
Github Gist
You'll need to have it in its own column then use crossAxisAlignment.stretch. Just ensure it occupies a finite space
You can wrap the Elevated Button with Sized Box and give it a width of your choice,
than if you want to put the '=' string to the right instead of center you can just wrap the text with align and set alignemnt to your choice
SizedBox(
width: 300,
child: ElevatedButton(
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Colors.grey.shade600),
),
child: Align(
alignment: Alignment.centerRight,
child: Text(
"=",
style: TextStyle(fontSize: 26),
),
),
onPressed: () {},
),
)
Assuming both the = button and / button are in a Row widget you could wrap the = button in a Expanded widget forcing it to take all available space along it MainAxis.
Here's a quick example:
class Sample extends StatelessWidget {
const Sample({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(child: Container(color: Colors.red, height: 50)),
Container(
height: 50,
width: 100,
color: Colors.green,
margin: const EdgeInsets.symmetric(horizontal: 10),
)
],
);
}
}
I am working with a NestedScrollView and a SliverAppBar and need to make adjusments to the expandedHeight: value of the SliverAppBar based on the height of a widget hosting some text. Currently the expandedHeight: is 400.0. However, because of the text widget the 400.0 value may not always render an appropriate design. So I want to make the expandedHeight: relative by adding the height of the text widget to the static 400.0 value.
Like this:
expandedHeight: 400.0 + textWidgetHeight
I need some help getting only the height value of the widget as a double.
This is the code for the widget:
Container _textWidget(String text) {
return Container(
width: 180.0,
margin: EdgeInsets.only(
top: 15.0,
),
child: Text(
text,
textAlign: TextAlign.center,
style: TextStyle(
color: AppTheme.define().hintColor,
fontFamily: 'RobotoMedium',
fontSize: 10.0,
),
),
);
}
you can do this, using a combination of context.size and SchedulerBinding.instance.addPostFrameCallback.
SchedulerBinding.instance.addPostFrameCallback is used inside the build method of a Widget or State to schedule a callback function once the rendering of the current frame is completed.
So, first make your _textWidget into a new StatelessWidget,
class TextWidget extends StatelessWidget {
String text;
var onTextBuild;
TextWiidget(this.text, this.onTextBuild);
Widget build(BuildContext context) {
SchedulerBinding.instance?.addPostFrameCallback((timeStamp) {
// This will be called once this frame is built.
onTextBuild(context.size); // context.size gives the Size that this widget ha occupied.
});
return Container(
width: 180.0,
margin: EdgeInsets.only(top: 15.0),
child: Text(
text,
textAlign: TextAlign.center,
style: TextStyle(
color: AppTheme.define().hintColor,
fontFamily: 'RobotoMedium',
fontSize: 10.0,
),
),
);
}
}
Now, when you are using your TextWidget, ensure it's parent is a StatefulWidget and create a new state variable,
Size textWidgetSize = Size(0, 0);
And while using your TextWidget, use it like this to get the size and update state.
....
TextWidget(text, (size) {
setState(() {
textWidgetSize = size;
});
}),
....
Then, in your SliverAppBar, you can use your new state variable like this,
expandedHeight: 400 + (textWidgetSize != null ? textWidgetSize.height : 0),
Container height is set to fixed 40 but once I'm using that Widget in AppBar() it takes all the possible height. Here is the code for my custom widget which has Fixed height of Container,
class LPBorderButtonWithIcon extends StatelessWidget {
final GestureTapCallback onPressed;
final String text;
final String iconAsset;
final Color textColor;
LPBorderButtonWithIcon(
{#required this.onPressed,
#required this.text,
#required this.textColor,
#required this.iconAsset});
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onPressed,
child: Container(
height: 40,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25),
border: Border.all(color: Color(0XFFd8dce1))),
child: Row(
children: [
WidthSizedBox(15),
Image.asset(
iconAsset,
height: 14,
width: 14,
),
WidthSizedBox(5),
Text(text,
style: TextStyle(
color: textColor,
fontSize: 12,
fontFamily: "GilroyMedium")),
WidthSizedBox(15),
],
),
));
}
}
and here I'm using LPBorderButtonWithIcon() in this screen,
class CreateRulesScreen extends StatefulWidget {
#override
_CreateRulesScreenState createState() => _CreateRulesScreenState();
}
class _CreateRulesScreenState extends State<CreateRulesScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
brightness: Brightness.light,
backgroundColor: Colors.white,
elevation: 1,
centerTitle: false,
titleSpacing: 0.0,
leading: BackButton(
color: LPColor.primary,
onPressed: () {
Navigator.of(context).pop();
},
),
title: Text(
"Create Rule",
style: LPStyle.titleStyle,
),
actions: [
Container(
margin: EdgeInsets.only(top: 12, bottom: 12, right: 16),
child: LPBorderButtonWithIcon(
onPressed: null,
text: "Create",
textColor: Color(0XFF508ff4),
iconAsset: "images/ic_publish.png",
),
)
],
),
);
}
}
and below is the result where that custom container takes all the possible height. Please let me know how can I set fixed height to my custom widget.
Place your Container inside an Align, Aling will force the container to occupy only the space it needs.
Align(
child: Container(
height: 20,
width: 30,
color: Colors.white,
),
)
The parent widget takes the entire space available to draw the widget, Here Container is the parent widget, and it's taking whatever space is available, so to give height to the Container, that needed to be placed inside any widget which assigns x,y position of widgets to get it to draw.
Container(
height: 40, // Its not going to apply height as it's parent widget
)
So to work out the above code you have to align Container to any other widget like Center, Align, etc.
For Eg:
Scaffold(
body: Container(
height: 600,
color: Colors.red,
child: Container(
height: 200,
color: Colors.yellow,
),
),
);
The above example child container will not draw yellow color in 200 height, it will take the entire 600 height space.
Output:
To Solve this we have assigned some widgets to the child Container so that it will get the x, y position to start drawing the child widget. Here Center widget is used.
Eg:
Scaffold(
body: Container(
height: 600,
color: Colors.red,
child: Center(
child: Container(
height: 200,
color: Colors.yellow,
),
),
),
);
Output:
Some Limitation:
A widget can decide its own size only within the constraints given to
it by its parent. This means a widget usually can’t have any size it
wants.
A widget can’t know and doesn’t decide its own position in the
screen, since it’s the widget’s parent who decides the position of
the widget.
Since the parent’s size and position, in its turn, also depends on
its own parent, it’s impossible to precisely define the size and
position of any widget without taking into consideration the tree as
a whole.
If a child wants a different size from its parent and the parent
doesn’t have enough information to align it, then the child’s size
might be ignored. Be specific when defining alignment.
Reference link: https://flutter.dev/docs/development/ui/layout/constraints
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.