How to make a Flutter view with an item below a center in parent item? - flutter

I am trying to create a view that looks like this in Flutter. But, there is no concept of relative position in Flutter.
Here is what I want to achieve.
The red rectangle is in the center of the screen, and the green rectangle is right below the red one.
In Android, I can easily do this with Constraint Layout,
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="#+id/view"
android:layout_width="200dp"
android:layout_height="200dp"
android:background="#ff0000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:layout_width="20dp"
android:layout_height="20dp"
android:background="#00ff00"
app:layout_constraintEnd_toEndOf="#+id/view"
app:layout_constraintTop_toBottomOf="#+id/view" />
</android.support.constraint.ConstraintLayout>
Can anyone suggest a good solution to this in Flutter?

#cynw
Since both widgets are Texts and you don't know their heights until they are rendered, we have to calculate the height and re-render (rebuild) the widget.
In my approach below, I add a SizedBox above the big Text to push it down to make sure it is laid out at the center. The bad thing in this approach is we have to re-build the widget.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
SystemChrome.setEnabledSystemUIOverlays([]);
runApp(MyStatefulApp());
}
class MyStatefulApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyStatefulAppState();
}
}
class _MyStatefulAppState extends State<MyStatefulApp> {
GlobalKey _smallTextKey = GlobalKey();
double _bigTextMarginTop = 0;
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
}
_afterLayout(_) {
_getSizes();
}
_getSizes() {
final RenderBox renderBoxRed =
_smallTextKey.currentContext.findRenderObject();
final smallTextSize = renderBoxRed.size;
setState(() {
_bigTextMarginTop = smallTextSize.height;
});
}
#override
Widget build(BuildContext context) {
print('Margin = $_bigTextMarginTop');
return MaterialApp(
title: 'Flutter',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
SizedBox(
height: _bigTextMarginTop,
),
Text(
'Hello',
style: TextStyle(
fontSize: 120,
backgroundColor: Colors.red,
color: Colors.white),
),
Text(
'Hello',
style: TextStyle(
fontSize: 40,
backgroundColor: Colors.green,
color: Colors.white),
key: _smallTextKey,
),
],
)),
),
);
}
}
Hope it help!

Flutter Positioned class is your friend.
A Positioned widget must be a descendant of a Stack, and the path from the Positioned widget to its enclosing Stack must contain only StatelessWidgets or StatefulWidgets (not other kinds of widgets, like RenderObjectWidgets).

Try this
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width * 0.7,
height: MediaQuery.of(context).size.width * 0.7,
color: Colors.red,
child: Text("Some text"),
),
Container(
color: Colors.green,
child: Text("text here"),
)
],
),
)
you can remove the height/width parameter so it adjust the according to the content inside it.

one way of doing this is: if you center a column and give it horizontal padding symmetric horizontal padding, building 2 Container underneath each other, and position them.
home: Scaffold(
body: Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 30.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
color: Colors.black,
height: 300,
width: 300,
),
Align(
alignment: Alignment.bottomRight,
child: Container(
color: Colors.red,
height: 30,
width: 30,
),
),
],
),
),
),
)

Related

Flutter Column children exceed constrained box parent

The following flutter example results in the green box and text actually exceeding the constraints of the parent container. Why does this happen in this case? Basically the column is exceeding its incoming parent constrained height of 400, and seems to be using the top parent container height when making decisions on where to layout its children. Also the black border is not visible?
I have also tried using sizedbox, constrainedbox and the result is the same.
This is purely to understand the reason why this happens in more detail.
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 Container(
width: double.infinity,
height: double.infinity,
color: Colors.red,
child: Container(
height: 400,
decoration: BoxDecoration(border: Border.all(color: Colors.black)),
constraints: BoxConstraints(maxHeight: 400),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: 50,
width: 50,
child: Container(
color: Colors.green,
)
),
Text("Test")
]
)
)
);
}
}
I know it's over a year since this issue was raised, but I recently ran into a very similar issue, and (by experimenting) I found a solution
My problem can be better demonstrated by having the Column contain multiple Text entries that are multiple lines long, and the overflow marker appears, but the text continues on the screen
The actual solution seems quite simple but not immediately obvious... wrap the Text entries in a Flexible!
SizedBox(
height: 131,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
text1,
),
Flexible(
child: Text(
text2,
style: const TextStyle(
fontWeight: FontWeight.w400,
fontSize: 10,
),
),
),
],
),
);

I am getting an error, The method '*' was called on null

I have created a button class that extends a stateless widget for creating a customized button widget. I have used this button to create buttons in a class and it worked perfectly fine. But, when i tried to create a button in an another class, using the same button widget, i got an error,'The method '*' was called on null and RenderFlex overflowed' which i could not get why is it appearing. Can somebody help me, what and where did i do the blunder.
This one is the button widget
import 'package:flutter/material.dart';
import 'package:group_project/ui/size_config.dart';
import 'package:group_project/widgets/responsive_widget.dart';
// Button Widget
class Button extends StatelessWidget {
final IconData icon;
final String btnName;
final double height;
final double width;
final Color buttonColor;
final Color iconColor;
final double iconSize;
final Color textColor;
final double btnTextSize;
Border border;
BorderRadius btnBorderRadius;
MainAxisAlignment mainAxisAlignment;
CrossAxisAlignment crossAxisAlignment;
Button({
#required this.icon,
#required this.btnName,
this.height,
this.width,
this.buttonColor,
this.iconColor,
this.textColor,
this.border,
this.mainAxisAlignment,
this.crossAxisAlignment,
this.btnTextSize,
this.iconSize,
this.btnBorderRadius,
});
#override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
height: height,
width: width,
decoration: BoxDecoration(
color: buttonColor,
border: border,
borderRadius: btnBorderRadius,
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: mainAxisAlignment,
children: [
Text(
btnName,
style: TextStyle(
fontSize: btnTextSize * SizeConfig.textMultiplier,
color: textColor,
),
),
Icon(
icon,
color: iconColor,
size: iconSize * SizeConfig.imageSizeMultiplier,
),
],
),
),
);
}
}
This is the class where I got the error.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:eva_icons_flutter/eva_icons_flutter.dart';
import 'package:group_project/ui/size_config.dart';
import 'package:group_project/widgets/widgets.dart';
// import 'package:group_project/data/data.dart';
// import 'package:group_project/widgets/product_carousel_widget.dart';
class ProductsPage extends StatefulWidget {
#override
_ProductsPageState createState() => _ProductsPageState();
}
class _ProductsPageState extends State<ProductsPage> {
Size size;
bool visible = true;
void isVisible() {
if (visible == true) {
visible = false;
} else {
visible = true;
}
}
#override
Widget build(BuildContext context) {
size = MediaQuery.of(context).size;
return Scaffold(
backgroundColor: Color(0xfff0f0f0),
body: ListView(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
children: [
Container(
height: 233.33 * SizeConfig.heightMultiplier,
width: double.infinity,
color: Colors.blue,
child: Image(
image: AssetImage('images/jacket.jpg'),
fit: BoxFit.cover,
),
),
Stack(
children: [
// Products description
Visibility(
visible: visible,
child: Padding(
padding: const EdgeInsets.only(top: 40.0),
child: Container(
padding: EdgeInsets.fromLTRB(10.0, 40.0, 10.0, 10.0),
height: 400,
width: double.infinity,
decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(20.0),
// border: Border.all(color: Colors.blue),
color: Colors.blue,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Iphone Pro Max'
),
Text(
'Rs. 125000'
),
],
),
SizedBox(
height: 20.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'(Used)'
),
Text(
'Condition: Good'
),
],
),
SizedBox(
height: 20.0,
),
Text(
'This is iphone 11 pro max, 64 GB variant. The size of the mobile phone is 6.5 inches. Released 2019, September ',
),
SizedBox(
height: 20.0,
),
Row(
children: [
// This is the button where exactly I am getting the error
Button(
height: 25 * SizeConfig.heightMultiplier,
width: 80,
icon: Icons.shopping_cart,
btnName: 'Add',
),
],
),
],
),
),
),
),
FlatButton(
padding: EdgeInsets.all(0),
onPressed: () {
setState(() {
isVisible();
});
},
child: Button(
icon: EvaIcons.chevronDown,
btnName: 'Show Description',
height: 25.0 * SizeConfig.heightMultiplier,
width: double.infinity,
buttonColor: Colors.blue,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
btnTextSize: 8.0,
iconSize: 20,
textColor: Colors.white,
iconColor: Colors.white,
// btnBorderRadius: BorderRadius.circular(0),
),
),
],
),
],
),
],
),
],
),
);
}
}
Wrap it with Expanded Widget when using inside a Row or Column
Expanded(
child: Button(
height: 25 * SizeConfig.heightMultiplier,
width: 80,
icon: Icons.shopping_cart,
btnName: 'Add',
),
),
Your Row has no defined height or width. Try wrapping your row in an Expanded widget.
This also why you're getting the null reference exception. You're sizeConfig (Which I assume is based off of MediaQuery.of(context).size) Gets it's context from it's parent widget and since a Row widget doesn't define (It is a flexible widget) height or width, it will return null for size. Expanded will tell the Row to set its size to all available space.
#override
Widget build(BuildContext context) {
return SingleChildScrollView(child: Container(...));}
surround your code with SingleChildScrollView that's why render flex error is showing
for the '*' is called on null error click on the run button at the bottom of android studio and look at the log and click the link with ProductPage.dart and it will take you to the line where the error happens
Try giving it a width or wrapping with a Expanded
It is given infinite width because MediaQuery is always given the context of it's parent, and in this scenario it gets it from Row widget which doesn't have a defined width. Neither does a Column widget have a defined height. That's why it's giving the error.
Wrapping it with Expanded widget will make the Row take all of free space.

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

Overlay a card partially on an image

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

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.