Overlay a card partially on an image - flutter

I am trying to partially overlay a Card on an image using Stack, just like this
So this is what I have tried
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
body: Container(
margin: const EdgeInsets.all(10.0),
child: Stack(
alignment: AlignmentDirectional.bottomCenter,
children: <Widget>[
ClipRRect(
child: Image.asset("assets/images/placeholder.jpg"),
borderRadius: new BorderRadius.circular(8.0),
), // image
new Positioned(
child: Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
title: Text('1625 Main Street',
style: TextStyle(fontWeight: FontWeight.w500)),
subtitle: Text('My City, CA 99984'),
leading: Icon(
Icons.restaurant_menu,
color: Colors.blue[500],
),
),
],
),
) //card
)
],
)),
);
}
However it displays the card at the bottom of the image but trying to overlap it partially over the image with the help of Stack's bottom, top arguments makes the card not display all together. How can I go about it?

I think the issue is that when you're making your stack, you're not allowing it to size itself properly. A stack sizes itself to any children which are not positioned - in your case, the ClipRRect. The ClipRRect looks like it is sized to its child image, which has a defined height. So the stack will also be this size I believe (you can turn on debug painting to see).
It looks like you want the image and white to be the background for your entire page, which means that you should be letting the stack expand to the size of the page. Wrapping your image in an alignment should do this.
The next part is that you've made your card positioned, but not defined any parameters. You want to define at the least the top, but probably also the left and right.
This works for me (although I'm not using all the same widgets, but it should apply anyways):
import 'package:flutter/material.dart';
main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("Card over stack"),
),
body: Stack(
children: <Widget>[
Align(
alignment: Alignment.topCenter,
child: Container(
decoration:
BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(10.0)), color: Colors.lightBlueAccent),
height: 100,
),
),
Positioned(
top: 60,
right: 10,
left: 10,
child: Card(
child: ListTile(
title: Text('1625 Main Street', style: TextStyle(fontWeight: FontWeight.w500)),
subtitle: Text('My City, CA 99984'),
leading: Icon(
Icons.restaurant_menu,
color: Colors.blue[500],
),
),
),
)
],
),
),
);
}
}

Related

Flutter - Place Text Box without using Scaffold

I am new to flutter and having come from an android dev background, having trouble placing my widgets appropriately.
I intent to place a bunch of texts/buttons one on top of another. Below is how i want to set it in my final screen. Additionally, I want to set the entire page's background color as say Red.
Given i dont need the title bar etc, I thought I'd start by not using the Scaffold at all. So here's my code as of now. I am first trying just to the 'MYAPP' text placed correctly, and not getting it to work.
void main() {
runApp(const MaterialApp(
home: MyPage(),
));
}
And in a separate file, I define MyPage
class MyPage extends StatelessWidget {
const MyPage ({Key? key}) : super(key: key);
static const TextStyle myStyle = TextStyle(
color: AppColors.complementColor,
fontSize: 80,
);
#override
Widget build(BuildContext context) {
return Container( // -> using Container, so I can set the background color as Red.
color: Colors.Red,
child: Column(
children: [
Container( // -> using container again so I can add axis etc, and margins
child: const Text(
'MYAPP',
style: myStyle,
textDirection: TextDirection.ltr,
))
],
),
);
}
}
What happens with this is that I get my MYAPP, in the top middle of screen. What i want is for it to be in the left side (and with margin from start).
I thought of adding, mainAxisAlignment: MainAxisAlignment.center and crossAxisAlignment: CrossAxisAlignment.start, but when I add crossAxisAlignment, the text box isnt getting shown at all, and even the main background color which was originally red, changes to black.
So I've got a few questions.
How do I get my text 'MYAPP' to be placed properly using the widgets I have, and why does adding crossAxisAlignment as start removes the entire widget.
If I need to add margin/padding etc, does it make sense to do the way I am doing (i.e. have a container around column)
When I set the color of root container to Red, even then the topmost bar of my android phone (the one that is used to scroll down any notifications) is not really of red color, but an offset of red. Why is that?
Is it better if I used scaffold without appbar for this?
If i dont give any axis alignment for my column, why is the text placed in the top center and why not top left? Additionally, why does the text have some padding already from the surrounding container?
Please use this code
void main() {
runApp(const MaterialApp(
debugShowCheckedModeBanner: false,
home: MyPage(),
));
}
class MyPage extends StatelessWidget {
const MyPage({Key? key}) : super(key: key);
static const TextStyle myStyle = TextStyle(
color: Colors.black,
fontSize: 80,
);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.red,
width: double.infinity,
height: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.only(left: 20.0),
child: Text(
'MYAPP',
style: myStyle,
textDirection: TextDirection.ltr,
),
),
const Padding(
padding: EdgeInsets.only(left: 25),
child: Text(
'This is my app',
),
),
const SizedBox(
height: 30,
),
Padding(
padding: const EdgeInsets.only(left: 25.0, right: 25),
child: Row(
children: [
Expanded(
child: SizedBox(
height: 50,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Colors.transparent,
elevation: 0,
// side: const BorderSide(width: 2.0, color: Colors.black,),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
side: const BorderSide(
color: Colors.black, width: 3))),
child: const Text(
'This is my button',
style: TextStyle(color: Colors.black),
),
onPressed: () {},
),
),
),
],
),
),
],
),
),
);
}
}
Output :

How can I set two container on top of each other?

im trying to set 2 containers on top of each other. What I want is to showing a star and when user pressed the star I wanna open 5 star so he can vote . Im not really sure if im thinking the correct way . But fr that I wanna create one container and in another container directly above this stars container displaying all other buttons how's just buttons to clock normally . I hope that you understand what I trying to do. So when im putting the star container also in the same container they buttons when opening all stars the whole container will move isn't?
So heres im trying to do.
class VideoPage extends StatelessWidget {
static const route = '/Video';
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Container(
height: 100,
color: Colors.yellow[300],
),
Expanded(
child: Row(
children: [
Expanded(
child: Container(color: Colors.green[300]),
),
Container(
width: 100,
color: Colors.orange[300],
),
Container(
width: 100,
color: Colors.red[300],
),
],
),
),
Container(
height: 80,
color: Colors.blue[300],
),
],
),
);
}
}
This look like that:
And I want that the orange one is directly under the red one so there will be first one star and on pressing it open 5 stars . Is this possible what im trying ? If so please help . If not please help haha
What you need to use is the Stack Widget:
Stack(
children: <Widget>[
BottomWidget(),
MiddleWidget(),
TopWidget(),
],
),
The last widget of the children is the top layer, structure descending.
more info: https://medium.com/flutter-community/a-deep-dive-into-stack-in-flutter-3264619b3a77
EDIT:
In your case if you want the orange one underneath, you have to do some changes within your structure and split it from your Row()
Example
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Container(
height: 100,
color: Colors.yellow[300],
),
Stack(
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width *1,
height: 200,
color: Colors.orange,
),
Container(
height:200,
child: Row(
children: [
Expanded(
child: Container(color: Colors.green[300]),
),
Container(
width: 100,
color: Colors.red.withOpacity(0.5),
),
],
)),
],
),
Container(
height: 80,
color: Colors.blue[300],
),
],
),
);
}
}

Place a CircularProgressIndicator inside RaisedButton mantaining size

I want to place a CircularProgressIndicator inside a RaisedButton, but when I do it, the CircularProgressIndicator doesn't fit the button and sticks out of it.
I want to shrink the CircularProgressIndicator, so that it fits inside the RaisedButton, I know I can do it using a SizedBox but I want it to be automatic, not that I give it the size that I want. I tried FittedBox but all its options make no difference at all.
this is my button's code:
RaisedButton(
onPressed: () => print('hi'),
shape: StadiumBorder(),
color: Theme.of(context).primaryColor,
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 12, top: 6, bottom: 6),
child: CircularProgressIndicator(
backgroundColor: Colors.white,
strokeWidth: 2,
),
),
Text('Loading'),
],
),
),
And this is how it looks like:
When I Add Padding it grows the button:
Are there any ways to achieve this automatically?
EDIT:
Just to make it clear, the end effect that I want is this:
Before and after the user presses the button:
I want the height to stay the same, automatically, without magic values inside a SizedBox.
This is totally possible. You wrap the CircularProgressIndicator inside a SizedBox to constrain it to that size. Also using MainAxisSize.Min in the Row prevents the button from trying to expand to infinite size.
ElevatedButton(
onPressed: (){},
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.blue)),
child: isLoading
? Padding(
padding: const EdgeInsets.all(5.0),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text("Loading"),
SizedBox(
width: 5,
),
SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(Colors.white),
backgroundColor: Colors.blue,
strokeWidth: 3,
),
)
],
),
)
: Text("Not Loading"))
This gives you the following
Looking at the CircularProgressIndicator source code, I can find there's a hardcoded minimum size of 36.0 height/width, so there's no way of getting smaller than that automatically.
I re-created your case and was able to see the CircularProgressIndicator properly inside the RaisedButton maintaining it's size.
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Progress Button',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Progress Button'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
onPressed: () => print('hi'),
shape: StadiumBorder(),
color: Theme.of(context).primaryColor,
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 12, top: 6, bottom: 6),
child: CircularProgressIndicator(
backgroundColor: Colors.white,
strokeWidth: 2,
),
),
Text('Loading'),
],
),
),
],
),
),
);
}
}
Output (both platforms):
you can use mainAxisAlignment: MainAxisAlignment.spaceBetween to make space between your widgets in Row.
You can wrap your Row widget to a Padding widget to make padding for whole content in your button as well.
Padding(
padding: EdgeInsets.all(10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
CircularProgressIndicator(
backgroundColor: Colors.white,
strokeWidth: 2,
),
Text('Loading'),
],
),
)

Flutter Stack widget not sizing correctly when used in a ListView

I'm trying to build a List of Card's that contain a Stack widget, the Card + Stack Widget works just fine until I make it the child of a list view, and then the Stack no longer sizes automatically to its largest child. This is causing my view that should be aligned to the bottom right to only be right aligned instead since the bottom of the Stack isn't sized correctly.
I've tried wrapping all the children of the Stack with an Align, but that didn't help either. Positioned has the same results as Align.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Stack Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) => Scaffold(
body: ListView(
children: <Widget>[
_buildCard(context),
_buildCard(context),
_buildCard(context),
_buildCard(context),
_buildCard(context),
_buildCard(context),
],
),
);
Widget _buildCard(BuildContext context) => Card(
child: Stack(
children: <Widget>[
SizedBox(
height: 200,
child: Container(
color: Colors.black,
),
),
Align(
alignment: Alignment.bottomRight,
child: SizedBox(
height: 50,
width: 50,
child: Container(
color: Colors.red,
),
),
),
],
),
);
}
What I'd expect to happen, is the Stack would size correctly around the black container allowing the red container to be positioned at the bottom right of the card.
try to set the alignment on the stack too
alignment: AlignmentDirectional.bottomEnd,
like this one
Widget _buildCard(BuildContext context) => Card(
child: Stack(
alignment: AlignmentDirectional.bottomEnd,
children: <Widget>[
SizedBox(
height: 200,
child: Container(
color: Colors.black,
),
),
Align(
alignment: Alignment.bottomRight,
child: SizedBox(
height: 50,
width: 50,
child: Container(
color: Colors.red,
),
),
),
],
),
);
screenshot

Flutter Layout Row / Column - share width, expand height

I'm still having a bit of trouble with the layouting in Flutter.
Right now I want to have the available space shared between 3 widgets, in a quadrant layout.
The width is evenly shared (this works fine via 2 Expanded widgets in a Row), but now I also want the height to adjust automatically so widget3.height == widget1.height + widget2.height.
If the content of widget3 is larger, I want widget1 and widget2 to adjust their height and vice versa.
Is this even possible in Flutter?
Have a look at IntrinsicHeight; wrapping the root Row should provide the effect you're looking for:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(title: Text('Rows & Columns')),
body: RowsAndColumns(),
),
);
}
}
class RowsAndColumns extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 100.0),
child: IntrinsicHeight(
child: Row(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
Expanded(
child: Column(children: [
Container(height: 120.0, color: Colors.yellow),
Container(height: 100.0, color: Colors.cyan),
]),
),
Expanded(child: Container(color: Colors.amber)),
]),
),
);
}
}
Adjusting the heights in the containers in the column cause the container on the right to resize to match:
https://gist.github.com/mjohnsullivan/c5b661d7b3b4ca00599e8ef87ff6ac61
I think that you can set the height of the row instead, then you only must to set the height of you column containers, and with the crossAxisAlignment: CrossAxisAlignment.stretch the second row will be expand occupying his parent height:
Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: Column(
children: [
Expanded(
// If you don't have the height you can expanded with flex
flex: 1,
child: Container(
height: 50,
color: Colors.blue,
),
),
Expanded(
flex: 1,
child: Container(
height: 50,
color: Colors.red,
),
)
],
),
),
Expanded(
child: Container(
color: Colors.yellow,
),
)
],
)
Since IntrinsicHeight is considered relatively expensive, it's better to avoid it. You can use Table with verticalAlignment: TableCellVerticalAlignment.fill in the largest TableCell.
Please note that if you use .fill in all cells, the TableRow will have zero height.
Table(children: [
TableRow(children: [
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
alignment: Alignment.center,
color: Colors.blue,
child: Text('Widget 1'),
),
Container(
alignment: Alignment.center,
color: Colors.green,
child: Text('Widget 2'),
),
],
),
TableCell(
verticalAlignment: TableCellVerticalAlignment.fill,
child: Container(
alignment: Alignment.center,
color: Colors.orange,
child: Text('Widget 3'),
)),
]),
]),
I'm new to flutter. Please correct me if I'm doing something wrong here. I think It can also be achieved using setState((){}) without using IntrinsicHeight.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return MyAppState();
}
}
class MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
double h1 = 100;
double h2 = 200;
double h3;
setState(() {
h3 = h1 + h2;
});
return MaterialApp(
home: Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.only(top: 100.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
children: [
Container(
color: Colors.green,
height: h1,
),
Container(
color: Colors.orange,
height: h2,
)
],
),
),
Expanded(
child: Container(
color: Colors.yellow,
height: h3,
),
)
],
),
),
),
),
);
}
}
Find image here
Code can also be found here: https://gist.github.com/Amrit0786/9d228017c2df6cf277fbbaa4a6b20e83
if you have used the expanded inside Row and facing for full height issue then just wrap the expanded child with Align widget.