drawing an image on top of another in flutter - flutter

I use stack to place images, my back image is drawn on top of the other one, how can i fix the posterpath to be on top, also top image doesn't round the edges:
return Stack(
children: [
Positioned(
child: AspectRatio(
aspectRatio: 390 / 220,
child: ColorFiltered(
colorFilter: ColorFilter.mode(
Colors.black.withOpacity(0.3),
BlendMode.dstATop,
),
child: backdropPath != null
? Image.network(ImageDownloader.imageUrl(backdropPath))
: Image.asset(AppImages.noImageBig),
),
),
),
Positioned(
child: Center(
child: Padding(
padding: const EdgeInsets.only(top: 110.0),
child: Container(
clipBehavior: Clip.antiAlias,
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(12)),
),
height: 212.0,
width: 174.0,
child: posterPath != null
? Image.network(ImageDownloader.imageUrl(posterPath))
: Image.asset(AppImages.noImageBig),
),
),
),
),

The ordering of children in a Stack is determined by their order in the source code - later items in the list are drawn on top. If you want to switch the z-order of the two children, swap them in the source code.
For your second question, you can achieve "rounded corner" with ClipRRect widget.

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"),
),
),
),

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):

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

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,
),
);
},
),
);
}

GridView inside a Container inside a Column doesn't fill up the rest of the screen

The short version:
GridView without enough items inside a Container doesn't fill up the rest of the screen.
The long version:
My flutter app has a Column that should be scrollable, the Column has the following children:
Container with an Image
A SizedBox for spacing
A Container with a GridView (for styling)
If the items inside my GridView are not enough, the color of the container (that contains the GridView) doesn't get applied to the end of the screen.
This my code structure:
return Scaffold(
backgroundColor: Color(0xFF0f2447),
body: Container(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
// First Column Item
Container(
height: 250,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/ise.png'),
fit: BoxFit.cover,
),
),
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomLeft,
colors: [
Colors.black.withOpacity(0.0),
Colors.black.withOpacity(0.2),
],
),
),
),
),
// Second Column Item
SizedBox(
height: 20,
),
// Third Column Item
Container(
// What I have tried
// height: double.infinity, (Error: BoxConstraints forces an infinite height.)
// height: 500, (Works, but depends on the device height (can't be static) )
// height: "the remaining height of the screen whatever it is"
margin: EdgeInsets.only(left: 10, right: 10),
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.only(
topLeft: Radius.circular(50),
topRight: Radius.circular(50),
),
),
child: GridView.count(
physics: BouncingScrollPhysics(),
shrinkWrap: true,
crossAxisCount: 3,
children: currentList
.map(
(item) => ItemIcon(
label: item['title'],
icon: AssetImage(item['iconPath']),
changeView: changeView,
itemId: item['itemId'],
),
)
.toList(),
),
),
],
),
),
),
);
ItemIcon is a custom widget, it has an image with a fixed size and a Text with some padding.
Current list is simply as follows: (Just to avoid long code)
final List<Map<String, Object>> examples = [
{
'title': 'Test',
'itemId': 0,
'iconPath': 'assets/icons/icon.png',
},
...
]
Apologies for the confusing title.
Try to wrap the third container in Expanded class, and it will do your job.
Expanded takes up the remaining spaces in Row/Column.
Code
Expanded(
child: Container(
// What I have tried
// height: double.infinity, (Error: BoxConstraints forces an infinite height.)
// height: 500, (Works, but depends on the device height (can't be static) )
// height: "the remaining height of the screen whatever it is"
margin: EdgeInsets.only(left: 10, right: 10),
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.only(
topLeft: Radius.circular(50),
topRight: Radius.circular(50),
),
),
child: GridView.count(
physics: BouncingScrollPhysics(),
shrinkWrap: true,
crossAxisCount: 3,
children: currentList
.map(
(item) => ItemIcon(
label: item['title'],
icon: AssetImage(item['iconPath']),
changeView: changeView,
itemId: item['itemId'],
),
)
.toList(),
),
)
)
Alternative
You can anyway rely on the previous code, and just play with the height using MediaQuery class. This will keep the height uniformed to different device
Container(
// play with the floating number to get the desired height
height: MediaQuery.of(context).size.height * 0.61,
margin: EdgeInsets.only(left: 10, right: 10),
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.only(
topLeft: Radius.circular(50),
topRight: Radius.circular(50),
),
),
child: GridView.count(
physics: BouncingScrollPhysics(),
shrinkWrap: true,
crossAxisCount: 3,
children: currentList
.map(
(item) => ItemIcon(
label: item['title'],
icon: AssetImage(item['iconPath']),
changeView: changeView,
itemId: item['itemId'],
),
)
.toList(),
),
)
Another Workaround for Landscape view
You can use Stack class for achieving uniformity. Make use of the third widget as is. Just use the Stack to align the items for your landscape view.
Please read about Positioned class, which help you align your items in the view
So the view will be
Stack(
Container with an Image
// the last one comes on top of the first view
// no sizedbox required since you can align the item using Positioned
Positioned(
top: use media_query_dimension,
left: use media_query_dimension,
right: use media_query_dimension,
bottom: use media_query_dimension.
child: A Container with a GridView (for styling)
)
)

Transformation distorts the border radius of container in flutter

Align(
alignment: Alignment.bottomCenter,
child: Container(
//padding: EdgeInsets.all(20.0),
height: 50,
width: 320,
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.rectangle,
borderRadius: BorderRadius.all(Radius.circular(15.0))),
child: Stack(children: <Widget>[
Align(
alignment: Alignment.center,
child: Transform(
alignment: Alignment.topRight,
transform: Matrix4.diagonal3Values(4.0, 1.0, 1.0),
child: Container(
height: 45,
width: 45,
decoration: BoxDecoration(
//color: Color(0xffff8d27),
color: Colors.grey,
shape: BoxShape.rectangle,
borderRadius: BorderRadius.all(Radius.circular(10.0))
)),
),
),
Row(
children: <Widget>[
Expanded(
child: Center(
child: Text(
'Reservation',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
),
Align(
alignment: Alignment.center,
child: Icon(Icons.attach_money)),
Expanded(
child: Center(
child: Text(
'Now',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
)
],
)
])))
This is the current result
I have used a stack with the bottom layer being a container with grey color and 15.0 border radius aligned in the center, and the top layer containing text in two expandables and an icon in centre. I am trying to implement a two way slider button. I have not implemented the sliding part yet but that won't be an issue. When the user slides on a particular side the grey colored container in the bottom of stack streches on the respective side of the slide in turn depicting a selection. I am using the transform widget to scale the container in x dimension or simply speaking stretching it. The problem is that this transformation tends to distort the border radius of the container which is not producing the desired effect.
How can I stretch or transform the container so that it does not affect the border radius?
final _transformRight = Matrix4.identity()..translate(130.0);
final _transformLeft = Matrix4.identity(); //It means zero.
bool _shouldTransformRight = false;
#override
Widget build(BuildContext context) {
...
body: GestureDetector(
child: Container(
...
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15.0),
),
child: Stack(
children: <Widget>[
AnimatedContainer(
...
duration: Duration(milliseconds: 200),
decoration: BoxDecoration(
color: Colors.grey,
//You can create a variable to unify the
//borderRadius for all containers.
borderRadius: BorderRadius.circular(15.0),
),
transform: _shouldTransformRight
? _transformRight
: _transformLeft,
),
Row(...),
],
),
),
onHorizontalDragEnd: (details) {
setState(() {
_shouldTransformRight = !_shouldTransformRight;
});
}
),
...
}
Summary of the edits that I've made to your code:
No need to provide a shape and a corner radius for the container at the same time. Using one of them alone is sufficient.
In the corner radius of the container I used BorderRadius.circular(...) instead of BorderRadius.all(...), because it's shorter.
I've replaced the first child of the Stack with AnimatedContainer and used it's transform argument to position it accordingly. The Matrix4.diagonal3Values(4.0, 1.0, 1.0) it was the cause for the distorted border radius.
Lastly, I wrapped the whole widget tree in a GestureDetector, so that I can get the user input, which based on it I'm gonna translate the AnimatedContainer. You can configure which callBack of GestureDetector to use, here I've used onHorizontalDragEnd.