I'm struggling to make my text responsive in my application I'm re-writing from Kotlin to Flutter.
The point is that I have a text widget, which needs to be responsive. When I open it on a phone with 16:9 screen ratio, it's quite ok, but when I open my application on a phone with 18:9 screen ratio, the text doesn't fill the remaining space.
In Kotlin I had Contraint layout with guidelines which made the job very easy, but i don't know how to do it in Flutter.
I'm using AutoTextSize package, but it doesn't work which I intend it to work.
In my Kotlin app, it looks like this
In Flutter on my Samsung Note 9 with screen ratio 18:9 looks like this:
My code:
import 'package:flutter/material.dart';
import 'package:auto_size_text/auto_size_text.dart';
import '../../helpers/makdolan.dart';
class GeneratedCouponScreen extends StatelessWidget {
final String couponImage;
GeneratedCouponScreen({Key key, #required this.couponImage}) : super(key: key);
#override
Widget build(BuildContext context) {
var _makdolan = Makdolan();
var now = _makdolan.calculateDate();
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: Container(
padding: EdgeInsets.all(16.0),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('DATA WYDANIA:', style: TextStyle(color: Colors.black, fontSize: 16.0, fontWeight: FontWeight.bold)),
Text(now, style: TextStyle(color: Colors.black, fontSize: 16.0))
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('UNIKALNY KOD:', style: TextStyle(color: Colors.black, fontSize: 16.0, fontWeight: FontWeight.bold)),
Text(_makdolan.calculateUniqueCode(), style: TextStyle(color: Colors.black, fontSize: 16.0))
],
)
],
),
SizedBox(height: 8.0),
Image.asset(couponImage),
SizedBox(height: 8.0),
AutoSizeText.rich(
TextSpan(
text: 'Kupon ten upoważnia do jednokrotnego odbioru produktu gratis przy kolejnym dowolnym zakupie z oferty klasycznej. Kupon ten ważny jest przez 7 dni od czasu jego wygenerowania i może być zrealizowany w dowolnej restauracji McDonald\'s w Polsce z wyłączeniem restauracji znajdyujących się na terenie Portu Lotniczego im. Fryderyka Chopina w Warszawie oraz Portu Lotniczego im. Lecha Wałęsy w Gdańsku. Szczegółowy regulamin ankiety „Opinia Gości" znajduje się na stronie ',
style: TextStyle(color: Colors.black),
children: [
TextSpan(
text: 'www.mcdonalds.pl ',
style: TextStyle(color: Color(0xffffc300), decoration: TextDecoration.underline)
),
TextSpan(
text: 'w sekcji ',
style: TextStyle(color: Colors.black)
),
TextSpan(
text: 'Regulaminy',
style: TextStyle(color: Color(0xffffc300), decoration: TextDecoration.underline)
)
]
),
maxLines: 12,
),
Spacer(),
Card(
child: Container(
height: 95.0,
color: Color(0xffffc300),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('DRUKUJ /', style: TextStyle(fontSize: 28.0)),
Text('ZAPISZ JAKO PDF', style: TextStyle(fontSize: 28.0),)
],
),
)
),
),
Card(
child: Container(
height: 95.0,
color: Color(0xffffc300),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('PRZEŚLIJ KUPON', style: TextStyle(fontSize: 28.0)),
Text('(WYSYŁKA W CIĄGU 24 GODZIN)', style: TextStyle(fontSize: 17.0),)
],
),
)
),
)
],
),
),
)
);
}
}
You might try screenutil package.
The approach is basically getting a ratio based on screen width/height/dpi. So you can adjust any UI element (like fontsize) accordingly. So the idea is more or less to match the size of fonts on ur original tested device (where you have the design) and adapt it to other resolutions.
The idea is basic equation:
DesignScreenWidth -> 24pt(current font size)
Note9ScreenWidth -> x (adjusted font size for note9)
x = Note9ScreenWidth * 24 / DesignScreenWidth
So, that's how you get any ratio to adjust content based on width, height, screen PPI, or whatever. You basically treat the values as proportions and multiply by that normalization factor
currentFactor=currentValue/designedValue.
Hope it clarifies a little the concept for “multiresolution awareness”
Best easiest way to to make responsive Text for different screen size is Sizer plugin.
Check it this plugin ⬇️
https://pub.dev/packages/sizer
.sp - for font size
If you want to set responsive text size in any screen size device also tablet then use .sp after value.
Example:
Text('Sizer', style: TextStyle(fontSize: 12.0.sp)) //use SomeValue.sp in fonsize for responsive text
Also you can use this plugin for responsive widget
.h - for widget height
.w - for widget width
Example:
Container(
height: 10.0.h, //10% height of screen
width: 80.0.w, //80% width of screen
child: Text('Sizer', style: TextStyle(fontSize: 12.0.sp)),
);
hi you some one else mention it in the above answer that you can use the screenutil package for text resizing which is kind of good . but I would also like to recommend to try FittedBox widgets in flutter it is also helpul some time you need fitted box with screen util in some situation screen util does not help a lot. although it is a good package for make your design responsive . or you can also try to use different text size size for different screen size .
The is a Flutter package called auto_size_text 2.1.0 which may well address your problem.
https://pub.dev/packages/auto_size_text/versions/2.1.0#-readme-tab-
Related
Tell me how to correctly make a large indent between RedeemButton() and button ? I need to make such an indent as shown in the screenshot.
body
Column(childre: [
const RedeemButton(
textStyle: constants.Styles.smallBookTextStyleWhite,
buttonStyle: constants.Styles.smallMediumTextStyleWhite),
const Padding(
padding: EdgeInsets.only(bottom: 50),
child: Text(
'Hello',
style: TextStyle(
color: Colors.white,
),
),
),
As you are using a Column Widget, to separate your 2 children you can use the mainAxisAlignment set to spaceBetween, like this:
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [...
If you want a spacific distance number, you can set a SizedBox Widget between them, and set a height that fits your need in a height property
You can use Spacer widget if you want.
All texts in Figma have some height, for example 1.5, but when I set that height to the TextStyle, all lines with the new height are aligned to the bottom.
If using Center or Align widgets - wrong result. Examples has bottom vertical alignment in their lines. Like on bottom screenshots.
[
Is there a possibility to set vertical alignment in flutter Text wiget for every line? Or maybe someone has some helpful tips to solve the problem?
Text(
'Example\nExample',
textAlign: TextAlign.center,
style:TextStyle(
height: 2.5,
fontSize: 20,
),
);
Solution:
As user1032613 suggested, such a solution helped.
final text = 'Example Example\nExample Example';
const double height = 2.5;
const double textSize = 16.0;
const double bottomPadding = (height * textSize - textSize) / 2;
const double baseline = height * textSize - height * textSize / 4;
final Widget textWidget = Container(
color: const Color(0xFFFFFFFF),
padding: const EdgeInsets.only(bottom: bottomPadding),
child: Baseline(
baselineType: TextBaseline.alphabetic,
baseline: baseline,
child: Text(
text,
style: const AppTextStyle(
height: height,
fontSize: textSize,
color: const Color(0xFFaa3a3a),
),
),
),
);
There is a property called leadingDistribution which can be used for that:
Text(
'text',
style: TextStyle(
height: 2.0,
leadingDistribution: TextLeadingDistribution.even,
),
)
This is a quite common problem in Flutter when using custom fonts.
The solution our team currently uses is either use a Padding or a Baseline widget and manually tweak the text to make it appear vertically centered.
This can be done by setting textHeightBehavior property of Text.
Text(
'text',
style: TextStyle(
color: Colors.black,
height: 16 / 12,
),
textHeightBehavior: const TextHeightBehavior(
applyHeightToFirstAscent: true,
applyHeightToLastDescent: true,
leadingDistribution: TextLeadingDistribution.even,
),
),
Most important thing is to set leadingDistribution as TextLeadingDistribution.even.
One way:
Align(
alignment: Alignment.center,
child: Text("Text"),
),
Another way:
Center(
child: Text("Hello World", textAlign: TextAlign.center,),
),
As I alluded to in my previous post on the subject (Multi-line flutter text field occupies all of Flexible space with ugly right padding) I'm a bit of a perfectionist. Unfortunately my flutter layout-fu isn't as strong as my ambition. I'm creating a messaging app, and I'm working on adding a timestamp to the message box. My code so far (thanks also to this answer: Complex alignment of a sub widget based on wrapping text like in the Telegram chat messenger) is this:
Code to create a row:
Widget getAppUserMessageRow() {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
SizedBox(width: AppState.i.chatItemUserLeftInset),
Flexible(
fit: FlexFit.loose,
child: message.cm.messageType.getMessageWidget(message),
),
],
);
}
Code to create the message box itself:
Widget build(BuildContext context) {
bool isFromAppUser = message.cm.isFromAppUser(AppState.i.activeUserId);
return Container(
padding: EdgeInsets.symmetric(
vertical: AppState.i.chatItemMessageVerticalInset),
child:
Container(
decoration: BoxDecoration(
color: isFromAppUser ? AppState.i.chatItemUserMessageBackgroundColour : Colors.white,
//.withOpacity(!message.isFromAppUser ? 0.1 : 0.8),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(isFromAppUser ? AppState.i.chatItemMessageBorderRadius : 0),
topRight: Radius.circular(isFromAppUser ? 0 : AppState.i.chatItemMessageBorderRadius),
bottomRight: Radius.circular(isFromAppUser ? AppState.i.chatItemMessageCurvedBorderRadius : AppState.i.chatItemMessageBorderRadius),
bottomLeft: Radius.circular(isFromAppUser ? AppState.i.chatItemMessageBorderRadius : AppState.i.chatItemMessageCurvedBorderRadius),
),
boxShadow: [
BoxShadow(
color: AppState.i.chatItemMessageBoxShadowColour,
spreadRadius: AppState.i.chatItemMessageBoxShadowSpreadRadius,
blurRadius: AppState.i.chatItemMessageBoxShadowBlurRadius,
offset: AppState.i.chatItemMessageBoxShadowOffset, // changes position of shadow
),
],
),
padding: EdgeInsets.symmetric(
vertical: AppState.i.chatItemMessageVerInset,
horizontal: AppState.i.chatItemMessageHorInset),
child: Stack(
children: [
Text(
// WTF? This calculates enough space for the message receipt time to wrap
message.cm.messageText + (isFromAppUser ? ' \u202F' : ' \u202F'),
style: TextStyle(
fontSize: AppState.i.chatItemMessageTextFontSize,
color:
isFromAppUser ? AppState.i.chatItemMessageUserTextFontColour : AppState.i.chatItemMessageOtherUserTextFontColour,
),
textWidthBasis: TextWidthBasis.longestLine,
),
// This Positioned item creates the message timestamp in the area gained by adding the spaces above, assuming the spaces forced
// The text field into adding an extra line.
Positioned(
width: 60,
height: 15,
right: 0,
bottom: -2,
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Text(
DateFormat.Hm().format(message.cm.messageDisplayTime(AppState.i.activeUserId)),
style: TextStyle(
fontSize: AppState.i.chatItemMessageTimeFontSize,
color: isFromAppUser ? AppState.i.chatItemMessageUserTimeFontColour : AppState.i.chatItemMessageOtherUserTimeFontColour,
),
textAlign: TextAlign.right,
),
!isFromAppUser ? Container() :
Padding(
padding: EdgeInsets.only(left: AppState.i.chatItemMessageReceiptLeftInset),
child: message.cm.getReadReceiptIcon()
)
],
),
),
],
),
)
);
}
Here are some examples of good and bad - look at the right-hand-side padding on the bad examples. The timestamp should be more right-aligned with the text above:
And more:
Adding spaces + a non-printable character to make room for the date timestamp + read receipt combo is a genius trick, but with a downside. It pushes out the right alignment when the text is close to the space that will be occupied by the timestamp. So in some cases as per the above, the box consumes unnecessary space.
So, can this be fixed, or can a different layout be used to achieve the effect I'm looking for? I can't use anything too computational, because right now the performance is pretty decent when scrolling long lists.
To make this slightly more concrete, some 'fixed' examples.
Example: Message box with Timestamp
I think you can try this way:
Calculate/Simulate text and check the message is overlapping the timestamp.
If it overlaps, add \n to change the line. Otherwise, return the original one.
You need the following things:
LayoutBuilder: BoxConstraint above the text widget
TextPainter: Simulate the layout (also need the TextStyle, textWidthBasis or there may affect the layout result)
Here is the code:
// assume 'message' is a String need to be displayed
...
LayoutBuilder(builder: (context, constraints) {
final reserve = ' \u202F';
final originalPainter = TextPainter(
text: TextSpan(text: text, style: TextStyle(fontSize: 20)),
textDirection: TextDirection.ltr,
textWidthBasis: TextWidthBasis.longestLine,
)..layout(maxWidth: constraints.maxWidth);
final reservePainter = TextPainter(
text: TextSpan(text: text + reserve, style: TextStyle(fontSize: 20)),
textDirection: TextDirection.ltr,
textWidthBasis: TextWidthBasis.longestLine,
)..layout(maxWidth: constraints.maxWidth);
final changeLine = reservePainter.width > originalPainter.width + 0.001 || reservePainter.height > originalPainter.height + 0.001;
return Text(
changeLine ? text+'\n' : text,
...
)
}
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,
),
),
I have two widget : Text A, Text B, Text A is very long text,
B is right , I want to fix B position and full visible, and let A widget using remaining space and new line to show B text.
Here is the code:
Row(
children: <Widget>[
Text('AAAAAAAAAAAA--------------------------------------------------------'),
//Spacer(),
Text(
"BBBBBBBBB",
style: TextStyle(fontSize: 13, color: Color(0xFF666666)),
)
],
)
Row(
children: <Widget>[
Expanded(child: Text(
'AAAAAAAAAAAA---------------------------------------------------------------------------------------------------')),
//Spacer(),
Text(
"BBBBBBBBB",
style: TextStyle(fontSize: 13, color: Color(0xFF666666)),
)
],
)