Flutter - assistance with complex widget layout - flutter

I'm designing a shopping app that pulls data from an e-commerce store API. I've created this layout for the store items:
Each item is a Card with a Row as the main child, which contains the Image widget and two Columns for the text. The first column holds the title and subtitle, while the second one holds the price. It looks good now, but if the subtitle is any bigger, then this happens:
I'd ideally like the title and price to be grouped together, with the title aligned left and the price justified right, with the subtitle being able to take up the rest of the remaining width of the card.
I am familiar with how the Flutter layout works, but this task seems a bit beyond me. Even getting to this stage was a real challenge. I know that the parent's height and width can be obtained with LayoutBuilder, but this sort of thing requires calculating sibling width (and possibly height), which I'm not sure how to do. So any help here would be good.
Here is the code for the widget:
import 'package:flutter/material.dart';
class ProductItem extends StatelessWidget {
final String name;
final String shortDescription;
final double price;
final String imageUrl;
ProductItem({this.name, this.shortDescription, this.price, this.imageUrl});
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(4.0),
child: Card(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Image.network(
imageUrl,
fit: BoxFit.fill,
width: MediaQuery.of(context).size.width * 0.3,
),
SizedBox(width: 15,),
Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
name,
textAlign: TextAlign.left,
style: new TextStyle(
fontSize: 20.0,
color: const Color(0xFF000000),
fontWeight: FontWeight.w600,
),
),
Text(
shortDescription,
textAlign: TextAlign.left,
style: new TextStyle(
fontSize: 16.0,
color: const Color(0xFF000000),
fontWeight: FontWeight.w200,
),
),
],
),
SizedBox(width: 20),
Expanded(
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'฿${price.toStringAsFixed(0)}',
textAlign: TextAlign.right,
style: new TextStyle(
fontSize: 20.0,
color: const Color(0xFF000000),
fontWeight: FontWeight.w500,
),
)
],
),
)
],
),
),
);
}
}

Try to wrap the shortDescription Text widget with Expanded widget and set the maxLines in Text widget to two or according to your preference, like this:
Expanded(
child: Text(
shortDescription,
maxLines: 2,
textAlign: TextAlign.left,
style: new TextStyle(
fontSize: 16.0,
color: const Color(0xFF000000),
fontWeight: FontWeight.w200,
),
),
),

Related

Flutter. Row with MainAxisAlignment.spaceBetween overflowed

I am new in flutter and I'm trying to implement screen like this:
As you can see I need to make something like a table. I ran into a problem while implementing table rows. Here is my code:
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text("2.", ),
const SizedBox(width: 10),
Text(text: "This is second with very long text"),
],
),
),
const SizedBox(width: 8),
Text("500"),
],
),
But I'm getting the error:
A RenderFlex overflowed by 42 pixels on the right.
For some reason my text ("This is second with very long text") doesn't move to a new line, but goes off screen.
Please tell me what am I doing wrong?
You have to wrap your Text("This is second with very long text") widget inside Flexible widget like this:
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text("2."),
const SizedBox(width: 10),
Flexible(
child: Text("This is second with very long text"),
),
],
),
),
const SizedBox(width: 8),
Text("500"),
],
),
Just use a ListTile for that.
ListTile(
leading: Text("2. "),
title: Text("This is second with very long text"),
trailing: Text("500"),
)
This is much simpler to implement and handles long text automatically.
You can set padding etc as you wish. You can find more information about list tiles in the official documentation.
you need a Column, to wrap the widgets inside, inside the column you need a first Row that will hold the first and second,
then a HorizontalLine, and then the rest of the rows:
Please, see the example below
Container(
padding: EdgeInsets.all(10),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
//first row:
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('First', style: TextStyle(color: Colors.black, fontWeight: FontWeight.bold)),
Text('Second', style: TextStyle(color: Colors.black, fontWeight: FontWeight.bold)),
]
),
//This SizedBox gives space between widgets
SizedBox(height:5),
Container(color: Colors.black, height: 2, width: double.infinity),
//This SizedBox gives space between widgets
SizedBox(height:5),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('1', style: TextStyle(color: Colors.black, fontWeight: FontWeight.bold)),
//In order to make the Text multi-lined you need to wrap-it into a SizedBox widget
SizeddBox(
//as I can see, the middle text widget is almost the 70% of the total width
//You can get the total width from MediaQuery.of(context).size.width
width: MediaQuery.of(context).size.width * 0.7,
child: Text('This is first', style: TextStyle(color: Colors.black)),
),
Text('500', style: TextStyle(color: Colors.black, fontWeight: FontWeight.bold)),
]
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('2', style: TextStyle(color: Colors.black, fontWeight: FontWeight.bold)),
//In order to make the Text multi-lined you need to wrap-it into a SizedBox widget
SizeddBox(
//as I can see, the middle text widget is almost the 70% of the total width
//You can get the total width from MediaQuery.of(context).size.width
width: MediaQuery.of(context).size.width * 0.7,
child: Text('This is second with a very long text', style: TextStyle(color: Colors.black)),
),
Text('12', style: TextStyle(color: Colors.black, fontWeight: FontWeight.bold)),
]
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('3', style: TextStyle(color: Colors.black, fontWeight: FontWeight.bold)),
//In order to make the Text multi-lined you need to wrap-it into a SizedBox widget
SizeddBox(
//as I can see, the middle text widget is almost the 70% of the total width
//You can get the total width from MediaQuery.of(context).size.width
width: MediaQuery.of(context).size.width * 0.7,
child: Text('This is third with a long text too', style: TextStyle(color: Colors.black)),
),
Text('150', style: TextStyle(color: Colors.black, fontWeight: FontWeight.bold)),
]
),
]
)
In this code, you can change the styling and etc as you wish.
I would suggest you to extract the the Row widget that holds the number, the text and the score in a Stateless widget, or in a method that returns Widget object, but it's very soon for this.
I hope I helped you.
Enjoy Flutter!

How to stop shrinking the text in flutter web

I am new to flutter, need to know about a problem I am facing. I have built a web app using flutter. Which contains columns, containers, grid views, and other flutter widgets.
The problem I am facing is, though my layout is responsive when I shrink the chrome browser, it starts shrinking the content. Usually, in sites like Amazon, and other web pages, the shrink browser shows an extra scroll bar horizontally and vertically, and the content size remains the same.
How the same can be done using flutter?
Before
After
Here is the code:
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: DateCard(
eventName: 'Code Freeze', date: DateTime.now())),
SizedBox(width: 5),
Flexible(
child: DateCard(
eventName: 'Switchover', date: DateTime.now())),
SizedBox(width: 5),
Flexible(
child: DateCard(eventName: 'SP GA', date: DateTime.now())),
],
),
class DateCard extends StatelessWidget {
final String eventName;
final DateTime date;
const DateCard({this.eventName, this.date});
#override
Widget build(BuildContext context) {
return Column(
children: [
Material(
shape: SuperellipseShape(
borderRadius: BorderRadius.circular(28),
),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 18, vertical: 3),
child: FittedBox(
fit: BoxFit.contain,
child: Column(
children: [
Text(
date.day.toString(),
// maxLines: 1,
// overflow: TextOverflow.visible,
style: TextStyle(
fontSize: SizeConfig.blockSizeHorizontal * 3,
letterSpacing: -1),
),
Text(
DateFormat("MMM").format(date),
// overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: SizeConfig.blockSizeHorizontal * 1.2,
),
),
],
),
),
),
),
SizedBox(height: 5),
Text(
eventName,
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white, fontSize: 14),
)
],
);
}
}

Text Wrapping in Flutter

I am writing a widget in flutter for a project which should look like this, I am new to flutter and I am trying to imitate the card below
So far this is my code for the widget
import 'package:flutter/material.dart';
import 'package:smooth_star_rating/smooth_star_rating.dart';
class TrailCard extends StatelessWidget {
const TrailCard({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Card(
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
child: InkWell(
onTap: () {},
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(20.0),
child: Image.asset(
'assets/images/cocoa.jpg',
height: 100,
width: 90,
fit: BoxFit.fitHeight,
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Boring Cocoa",
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.bold),
),
Text('Rajhamundry, AP'),
Text(
'Feel the authentic flavours of life while you visit the farms around Rajahmundry',
overflow: TextOverflow.ellipsis,
)
],
),
),
],
)),
)),
);
}
}
I tried using flexible and expanded to try and make the content look the way but I am getting overflow error
You can wrap the Padding widget with a Flexible widget so it takes only the amount of space needed:
NOTE: you should also set the maximum amount of lines the Text widget should take before showing the ellipsis.
Flexible(
// new line
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Boring Cocoa",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
Text('Rajhamundry, AP'),
Text(
'Feel the authentic flavours of life while you visit the farms around Rajahmundry more text to cause overflow',
// set maximum number of lines the text should take
maxLines: 3, // new line
overflow: TextOverflow.ellipsis,
)
],
),
),
);
RESULT:
Wrap Padding with Expanded
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Boring Cocoa",
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.bold),
),
Text('Rajhamundry, AP'),
Text(
'Feel the authentic flavours of life while you visit the farms around Rajahmundry',
overflow: TextOverflow.ellipsis,
)
],
),
),
)

Flutter how to move an item to the bottom

I have this two items that once needs to appear in the middle but the other other needs to move to the bottom.
import 'package:flutter/material.dart';
class WelcomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.blue,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'K',
style: TextStyle(
fontSize: 200,
color: Colors.white,
decoration: TextDecoration.none,
fontStyle: FontStyle.italic,
),
),
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [Text('hello')],
)
],
),
);
}
}
This is how it looks.
I want to move the Hello Text to the bottom. How can I do that?
You need to use two things for that, to make it work more efficiently. These are:
Expanded Class -> This will provide space, that is, takes up the remaining space
Align class -> This will help you to align the item
Please Note: Align(), itself, cannot aligns, the item, unless space is given. If you just use the Align(), you will not see any change, cos Text('Hello') looks for space, but no space is there.
Code
Container(
width: double.infinity, // <-- Takes up total width of the device
height: double.infinity, // <-- Takes up the total height of the device
color: Colors.blue,
child: Column(
children: [
Expanded(
flex: 2, // <-- Flex takes care of the remaining space ratio
child: Center(
child: Text(
'K',
style: TextStyle(
fontSize: 200,
color: Colors.white,
decoration: TextDecoration.none,
fontStyle: FontStyle.italic,
),
)
)
),
Align(
alignment: Alignment.bottomCenter,
child: Text('Hello', style: TextStyle(color: Colors.white, fontSize: 17.0))
)
]
)
)
Result
If you don't want your K text to move on top, you would have to use Stack. This way, your widget inside can position themselves anywhere they want.
Stack(
alignment:Alignment.center,
children:[
Text("K"),
Align(
alignment:Alignment.bottomCenter,
child:Text("hello"),
),
]
),
Take a look at the stack widget here: https://www.youtube.com/watch?v=liEGSeD3Zt8
return Container(
color: Colors.blue,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Spacer(),
Text(
'K',
style: TextStyle(
fontSize: 200,
color: Colors.white,
decoration: TextDecoration.none,
fontStyle: FontStyle.italic,
),
),
Spacer(),
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [Text('hello')],
)
],
),
);
how about this way? add two Spacer

Under which circumstances textAlign property works in Flutter?

In the code below, textAlign property doesn't work. If you remove DefaultTextStyle wrapper which is several levels above, textAlign starts to work.
Why and how to ensure it is always working?
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new DefaultTextStyle(style: new TextStyle(fontSize: 10.0), child: new Column(children: <Widget>[
new Text("Should be left", textAlign: TextAlign.left,),
new Text("Should be right", textAlign: TextAlign.right,)
],))
);
}
}
Both approaches, suggested by Remi apparently don't work "in the wild". Here is an example I nested both inside rows and columns. First approach doesn't do align and second approach makes application just crash:
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new Directionality(textDirection: TextDirection.ltr, child: new DefaultTextStyle(
style: new TextStyle(fontSize: 10.0, color: Colors.white),
child: new Column(children: <Widget>[
new Row(children: <Widget>[
new Container(color: Colors.grey, child: new Column(children: <Widget>[
new Align(alignment: Alignment.centerLeft, child: new Text("left")),
new Align(alignment: Alignment.centerRight, child: new Text("right")),
],)),
new Container(color: Colors.grey, child: new Column(children: <Widget>[
new Align(alignment: Alignment.centerLeft, child: new Text("left")),
new Align(alignment: Alignment.centerRight, child: new Text("right")),
],)),
],),
/*new Row(children: <Widget>[
new Container(color: Colors.grey, child: new Column(children: <Widget>[
new SizedBox(width: double.infinity, child: new Text("left", textAlign: TextAlign.left,)),
new SizedBox(width: double.infinity, child: new Text("right", textAlign: TextAlign.right)),
],)),
new Container(color: Colors.grey, child: new Column(children: <Widget>[
new SizedBox(width: double.infinity, child: new Text("left", textAlign: TextAlign.left)),
new SizedBox(width: double.infinity, child: new Text("right", textAlign: TextAlign.right)),
],)),
],)*/]
)));
}
}
What I get from code is
i.e. text is centered, ignoring alignment of Align element.
DefaultTextStyle is unrelated to the problem. Removing it simply uses the default style, which is far bigger than the one you used so it hides the problem.
textAlign aligns the text in the space occupied by Text when that occupied space is bigger than the actual content.
The thing is, inside a Column, your Text takes the bare minimum space. It is then the Column that aligns its children using crossAxisAlignment which defaults to center.
An easy way to catch such behavior is by wrapping your texts like this :
Container(
color: Colors.red,
child: Text(...)
)
Which using the code you provided, render the following :
The problem suddenly becomes obvious: Text don't take the whole Column width.
You now have a few solutions.
You can wrap your Text into an Align to mimic textAlign behavior
Column(
children: <Widget>[
Align(
alignment: Alignment.centerLeft,
child: Container(
color: Colors.red,
child: Text(
"Should be left",
),
),
),
],
)
Which will render the following :
or you can force your Text to fill the Column width.
Either by specifying crossAxisAlignment: CrossAxisAlignment.stretch on Column, or by using SizedBox with an infinite width.
Column(
children: <Widget>[
SizedBox(
width: double.infinity,
child: Container(
color: Colors.red,
child: Text(
"Should be left",
textAlign: TextAlign.left,
),
),
),
],
),
which renders the following:
In that example, it is TextAlign that placed the text to the left.
Specify crossAxisAlignment: CrossAxisAlignment.start in your column
In Colum widget Text alignment will be centred automatically, so use crossAxisAlignment: CrossAxisAlignment.start to align start.
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(""),
Text(""),
]);
textAlign property only works when there is a more space left for the Text's content. Below are 2 examples which shows when textAlign has impact and when not.
No impact
For instance, in this example, it won't have any impact because there is no extra space for the content of the Text.
Text(
"Hello",
textAlign: TextAlign.end, // no impact
),
Has impact
If you wrap it in a Container and provide extra width such that it has more extra space.
Container(
width: 200,
color: Colors.orange,
child: Text(
"Hello",
textAlign: TextAlign.end, // has impact
),
)
Set alignment: Alignment.centerRight in Container:
Container(
alignment: Alignment.centerRight,
child:Text(
"Hello",
),
)
You can use the container, It will help you to set the alignment.
Widget _buildListWidget({Map reminder}) {
return Container(
color: Colors.amber,
alignment: Alignment.centerLeft,
padding: EdgeInsets.all(20),
height: 80,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
alignment: Alignment.centerLeft,
child: Text(
reminder['title'],
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 16,
color: Colors.black,
backgroundColor: Colors.blue,
fontWeight: FontWeight.normal,
),
),
),
Container(
alignment: Alignment.centerRight,
child: Text(
reminder['Date'],
textAlign: TextAlign.right,
style: TextStyle(
fontSize: 12,
color: Colors.grey,
backgroundColor: Colors.blue,
fontWeight: FontWeight.normal,
),
),
),
],
),
);
}
For maximum flexibility, I usually prefer working with SizedBox like this:
Row(
children: <Widget>[
SizedBox(
width: 235,
child: Text('Hey, ')),
SizedBox(
width: 110,
child: Text('how are'),
SizedBox(
width: 10,
child: Text('you?'))
],
)
I've experienced problems with text alignment when using alignment in the past, whereas sizedbox always does the work.
You can align text anywhere in the scaffold or container except center:-
Its works for me anywhere in my application:-
new Text(
"Nextperience",
//i have setted in center.
textAlign: TextAlign.center,
//when i want it left.
//textAlign: TextAlign.left,
//when i want it right.
//textAlign: TextAlign.right,
style: TextStyle(
fontSize: 16,
color: Colors.blue[900],
fontWeight: FontWeight.w500),
),
Text(' Use textAlign: TextAlign.center',
style: TextStyle(fontWeight: FontWeight.w900,fontSize: 20,),
textAlign: TextAlign.center,)
Always use textAlign When trying to direct you text