How to make a container take up the height of the parent widget? - flutter

I have a card layout which looks like the following:
The code for it it as follows:
Card(
color: tap ? Colors.green : Colors.white,
margin: EdgeInsets.symmetric(horizontal: 5.0, vertical: 3.0),
child: Container(
child: Row(
children: [
**Container(
height: !widget.menuItem.image.endsWith('/')? 70.0 : 0.0,
width: !widget.menuItem.image.endsWith('/')? 70.0 : 0.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(topLeft: Radius.circular(5.0), topRight: Radius.circular(5.0)),
image: DecorationImage(
image: NetworkImage(widget.menuItem.image),
fit: BoxFit.cover
),
),**
),
SizedBox(
width: 8.0,
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
children: [
Text(
howManyThere() > 0 ? "${howManyThere()} x " : '',
style: addBTNHighlight,
),
Expanded(
child: Text(
widget.menuItem.item_name,
overflow: TextOverflow.ellipsis,
style: tap ? TitleWhite18Bold : Title18bold,
),
),
],
),
Container(
child: Text(
widget.menuItem.description,
//overflow: TextOverflow.ellipsis,
style: tap ? SubTitleWhite13 : SubTitle13,
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: Text(
"£" + num.parse(widget.menuItem.price).toStringAsFixed(2),
style: tap ? priceHighlightSelected : priceHighlight,
),
),
],
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 30.0, vertical: 6.0),
decoration: BoxDecoration(),
child: Text("ADD", style: tap ? priceHighlightWhite : addBTNHighlight)),
],
),
],
),
),
)
As you can see that I am almost there with my desired layout but with one small issue which can be seen in first and last item. Because of the given width and height of the image container, it leaves a gap on top and bottom.
What I would like to happen is that the width to stay the same but the height to be flexible and adjust with the height of the card itself. Is it possible?
When I remove the height attribute of the container with the image, I get a blank space of the image
UPDATE: I tried LayoutBuilder as well but without any Luck, it throws 'Incorrect use of ParentWidget' Exception. The code I used is as follows:
Row(
children: [
widget.menuItem.image.endsWith('/')
? Container()
: **LayoutBuilder(
builder: (context, constraints){
return Container(
width: 70.0,
height: constraints.minHeight,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(5.0)),
image: DecorationImage(
image: NetworkImage(widget.menuItem.image),
fit: BoxFit.cover
),
),
);
}**
),

Try this :
Column(
children: [
Expanded(
child: Container(
width: !widget.menuItem.image.endsWith('/')? 70.0 : 0.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(topLeft: Radius.circular(5.0), topRight: Radius.circular(5.0)),
image: DecorationImage(
image: NetworkImage(widget.menuItem.image),
fit: BoxFit.cover
),
),
),
),
],
),

The problem here is your description text which overflow your desired max height. You can try to constraint your Container with a height: 70.0 or you can add to your Text widget the property maxLines: 1.

LayoutBuilder only give you constraints that parent pass to child, which is something like 100 < width < 200, 300 < height < infinity. At this point the height of all texts on the right are not determined yet, so it is impossible to determined the height of image base on that.
One way to do this is to assign key to each list item, get item heights after everything is layout, the rebuild with those fixed height. This will become a two pass process and is less efficient.
The official solution to this is IntrinsicHeight, which conceptually do the same thing, but in a more optimized way.
Here is a minimum example:
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
home: App(),
),
);
}
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemCount: 10,
itemBuilder: (context, index) {
return Card(
clipBehavior: Clip.antiAliasWithSaveLayer,
// This is where magic happened
child: IntrinsicHeight(
child: Row(
children: [
Container(
width: 100,
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage('https://cataas.com/cat'),
fit: BoxFit.cover,
),
),
),
Expanded(
child: Text(
// just put some random text with dynamic height
'Text\n' * (Random().nextInt(4) + 4),
),
),
],
),
),
);
},
),
);
}
}

Remove height constraints, then
Try replacing fit: BoxFit.cover with BoxFit.fitHeight or BoxFit.fill.

In order to fill the image with card height you can give container required height and wrap the Image widget in AspectRatio widget with ratio 1. This will always helps image widget to fill parent card height. Have a look in below code.
Parent Widget
Container(
margin: EdgeInsets.only(bottom: 12),
height: 100,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
AspectRatio(
aspectRatio: 1,
child: _pictureWidget(widget.menuItem.image),
),
/// Replace `Container` with the needed widget
Expanded(child: Container()),
],
),
)
_pictureWidget
Widget _picture(String url) {
return _pictureWidget(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(5.0), topRight: Radius.circular(5.0)),
child: Image.network(
url != null ? url : "",
fit: BoxFit.cover,
loadingBuilder: (BuildContext context, Widget child,
ImageChunkEvent loadingProgress) {
if (loadingProgress == null) return child;
return Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes
: null,
),
);
},
),
);
}

Related

Height adjustment of an Image in a row inside a Card

my idea is to create a card widget that has an image on the left and corresponding text data on the right side. The picture should always take the full height and 1/3 of the width (I used expanded with flex to get this. Not sure if this is the best way). The image should be placed in a stack in order to stack further widgets.
My problem is that I am not able to place the image in the stack without specifying a fixed height. I tried Positoned.fill but this did not work for me.
This is my code so far for the card:
Widget build(BuildContext context) {
return Card(
elevation: 5,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
child: Row(
children: [
Expanded(
child: Stack(
children: [
Container(
// child: Positioned.fill(
// child: ClipRRect(
// borderRadius: BorderRadius.circular(15.0),
// child: Image(
// image: AssetImage("assets/eyes.jpg"),
// fit: BoxFit.cover,
// ),
// ),
// ),
),
],
),
),
Expanded(
flex: 2,
child: Container(
color: Colors.grey,
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("This is a test"),
SizedBox(height: 10),
Text("This is a test test"),
SizedBox(height: 10),
Text("This is a test test test"),
SizedBox(height: 10),
Text("This is a test test"),
SizedBox(height: 10),
Text("This is a test"),
],
),
),
),
),
],
),
);
}
Here is a picture of the current state. The image should take up the entire white area on the left side of the card.
Instead of adding a child image widget inside your container, you can add a decoration image to the container itself. Here's how you do it:
Container(
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: AssetImage("url"),
),
),
),

Image is not fitted when used in leading of ListTile

The asset image is 200x200px, but when it used in leading of ListTile it's not fitted as axpected.
The rendered image looks this way. How to display it proportioanlly 90x90 as it's defined in Image height and width properties?
Card(
key: ValueKey(favorites[index]),
margin: EdgeInsets.all(20),
child: ListTile(
leading: kNoImage,
title: Text(favorites[index]),
subtitle: Text(favorites[index]),
trailing: IconButton(
icon: Icon(Icons.close),
onPressed: () {
debugPrint('entry deleted');
},
),
),
);
const kNoImage = Image(
image: AssetImage('assets/no-user-image.png'),
height: 90,
width: 90,
fit: BoxFit.cover,
);
Change the value of fit to something like BoxFit.scaleDown or BoxFit.contain to get the desired result. See the BoxFit docs to see what these values mean.
The max height for leading widget for your case is ≈56px.
So, if you want to explicitly want to keep it 90, I'd suggest you to make your own ListTile with Row() and Column() which is pretty easy.
This is what I found in ListTile(),
final Offset baseDensity = visualDensity.baseSizeAdjustment;
if (isOneLine)
return (isDense ? 48.0 : 56.0) + baseDensity.dy;
if (isTwoLine)
return (isDense ? 64.0 : 72.0) + baseDensity.dy;
return (isDense ? 76.0 : 88.0) + baseDensity.dy;
I prefer not to use ListTile because of it default behavior. Therefor, I prefer
on dartPad or on Gist
Custom ListTile
Card(
elevation: 8,
child: Container(
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(defaultBorderRadiusCircular),
),
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 6),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
SizedBox(
width: 90.0,
height: 90.0,
child: ClipRRect(
borderRadius:
BorderRadius.circular(defaultBorderRadiusCircular),
child: Image.asset(
'imagePath',
width: 90,
height: 90,
fit: BoxFit.cover,
),
),
),
const SizedBox(
width: 24,
),
Column(
children: [
const Text(
'Danau Beretan',
),
const Text(
'Indonesia',
),
],
),
],
),
Row(
children: [
Icon(Icons.star_half_outlined),
Text("4.5"),
],
)
],
),
),
),

Ripple does not cover text

I figured I can draw ripple over an image by enclosing the image in Ink(decoration: BoxDecoration(image: ...)), but how can I do the same for Text?
This is supposed to be a card with an image on top and a title and some other information at the bottom:
#override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final post = _postModel.post;
return Material(
color: theme.colorScheme.background,
child: InkWell(
onTap: () {},
splashColor: Colors.red,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
if (post.imageUri != null)
// This image IS covered by the ripple (what I need)
AspectRatio(
aspectRatio: 5 / 3,
child: Ink(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(3),
image: DecorationImage(
image: NetworkImage('https://picsum.photos/700'),
fit: BoxFit.cover,
),
),
),
),
// This must be underneath the ripple, but isn't.
Text(post.title, style: theme.textTheme.headline5),
Container(
padding: EdgeInsets.symmetric(horizontal: 0, vertical: 5),
color: Colors.transparent,
height: 60,
child: Row(
children: <Widget>[
// This is also covered by the ripple
Ink(
height: 35,
width: 35,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(3),
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage('https://picsum.photos/100'),
),
),
),
SizedBox(width: 5),
// This must be underneath the ripple but isn't
Ink(
// decoration: Decoration,
child: Text(
post.author,
style: theme.textTheme.caption,
),
),
Spacer(),
],
),
),
],
),
),
),
);
}
When I press, the ripple covers both images as expected, however, I can't figure out how to make the ripple cover the text widgets as well. I tried wrapping the text in an Ink widget (the last one), but it doesn't work - the text is still displayed over the ripple.
Unpressed:
Pressed (the text is visible):

Flutter design with timeline

I'm trying to make this drawing but I'm struggling.
I don't understand how to make the white block of information dynamic. That is to say that it automatically adapts to the size in height of the elements it contains.
I am for the moment obliged to give a defined height (here 200), I would like the size to be automatic. Moreover if I do not give a defined size my gray line on the left side disappears.
The design :
ListTile(
title : Column(
children: [
Container(
width: double.infinity,
child: Row(
children: [
Container(
width: 50,
alignment: Alignment.center,
child: Container(
width: 20,
height: 20,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(40),
border: Border.all(
width: 5,
color: Colors.blue,
style: BorderStyle.solid
)
),
),
),
Text('9:00 am - 9:15 am')
],
)
),
Container(
child: IntrinsicHeight(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
width : 50,
alignment: Alignment.center,
child: IntrinsicHeight(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
color: Colors.grey,
width: 3,
height : 200 // if I remove the size it desappear
),
],
),
),
),
Expanded(
child: Container(
margin: EdgeInsets.symmetric(vertical :10),
padding: EdgeInsets.symmetric(vertical: 20, horizontal: 10),
color: Colors.white,
child: Column(
children: [
Text('Titre'),
Text('Info 1'),
Text('Info 2')
],
),
),
)
],
),
),
)
],
),
),
You can try this package for timelines https://pub.dev/packages/timeline_tile.
This package also automatically Sizes your widget height and width.
And for Automatically Sizing your widgets next time, You could use Expanded widget at most places in your code.
Note: An Expanded widget must be a descendant of a Row, Column, or Flex, So Use it carefully.
I think it's much easier with this package, timelines.
TimelineTileBuilder.connected(
connectionDirection: ConnectionDirection.before,
itemCount: processes.length,
contentsBuilder: (_, index) {
//your Container/Card Content
},
indicatorBuilder: (_, index) {
if (processes[index].isCompleted) {
return DotIndicator(
color: Color(0xff66c97f),
child: Icon(
Icons.check,
color: Colors.white,
size: 12.0,
),
);
} else {
return OutlinedDotIndicator(
borderWidth: 2.5,
);
}
},
connectorBuilder: (_, index, ___) => SolidLineConnector(
color: processes[index].isCompleted ? Color(0xff66c97f) : null,
),
),
),

Widget which adjusts to the children size?

Is there a widget in Flutter which adjusts itself on the content of it's children which are in Rows and Columns?
I can't seem to find anything related, and I am constantly hit with infinite overflows errors.....
As an example, lets say I would have 3 Widgets in a Row, 1 of which is a text, which I cannot guess how long it will be, but I need to expand the parent as much as the widget needs and at the sametime wrap the text
I would have something like:
Row(
children: [
Expanded(child: Widget1(....)),
Expended(child: Text(.....)),
Expanded(child: Widget2(....))
]
)
Right?
I would want the parent of the row to expand as much as it needs for the children to be shown, I want to align Widget1 and Widget2 in different ways (1-center, 2-bottom).
If I put them in Column I can center Widget1, but I cannot put Widget2 at the bottom because if I put Column to max size it overflows, if I don't it doesn't align...
How would you do it?
I really don't understand the logic behind the inifnite layout thing....
For example, here is where I am stuck at the moment.
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Global.findCategory(event.categId).color.withAlpha(150),
blurRadius: 20,
spreadRadius: -20,
offset: Offset(0, 5)
)
],
color: Colors.white,
borderRadius: BorderRadius.circular(30)
),
child: Row(
children: <Widget>[
Align(
alignment: Alignment.center,
child: Padding(
padding: const EdgeInsets.all(5.0),
child: Container(
width: Global.scaleSmallDevice(context) * 64,
height: Global.scaleSmallDevice(context) * 64,
decoration: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(10),
image: DecorationImage(
fit: BoxFit.cover,
image: event.image
)
)
),
)
,
),
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
CustomText(
text: title,
color: Global.findCategory(event.categId).color,
fontSize: 16,
),
Flexible(
child: CustomText(
text: shortInfo,
color: Global.findCategory(event.categId).color,
fontSize: 12,
),
)
],
),
),
Stack(
alignment: Alignment.bottomCenter,
children: <Widget>[
Align(
alignment: Alignment.bottomCenter,
child: StatefulBuilder(
builder: (context, setState) {
IconData buttonIcon = (event.favorite) ? Icons.favorite : Icons.favorite_border;
return SplashButton(
splashColor: Global.findCategory(event.categId).color,
child: Icon(buttonIcon, color: Global.findCategory(event.categId).color, size: 30),
onTap: () async {
await event.setFavorite(context);
setState(() {
buttonIcon = (event.favorite) ? Icons.favorite : Icons.favorite_border;
});
}
);
},
),
),
],
),
),
],
);
}
In this case I want the StatefulBuilder widget to be on the bottom and the entire container be as small as the column with the CustomText widgets.
The widget with image inside I want it to be centered, while the text to be fully available to the user.
If I don't add a Column as Container, it goes as the height allows it and the StatefulBuilder widget center itself at the bottom, in the curent code it's at the center.....
Sorry if I'm not coherent, it's a bit late and I am tired after coding all day and stuck at this widget for a few hours.