Stack: cover bottom child with top child - flutter

I want overlay a widget over the bottom widget to hide it. hence I use stack. I don't know the bottom widget size so I can't give top widget fixed size.
what I have:
what I want to achieve:
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
'Winner: ',
style: TextStyle(fontSize: 34),
),
Stack(
children: [
Text('YOU!!!', style: TextStyle(fontSize: 34)),
Container(
color: Colors.amber,
)
],
),
],
),
This is simplified version of what I want to achive. I have to use Row and assume Texts are any widget with any size.
Stack sizes its children based on the Constraint it receives from parent not based on children sizes.
how can I overlay top widget with exact size of bottom widget?

To know the size of your widget and apply things depending on this, you can use GlobalKey like this GlobalKey key;. Initialize it in your initSate and listen to it's size
GlobalKey key;
double yourWidgetWidth;
double yourWidgetHeight;
#override
initState() {
// .... other stuffs
key = GlobalKey();
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
yourWidgetWidth = containerKey.currentContext.size.width;
yourWidgetHeight = containerKey.currentContext.size.height;
}
});
}
And with yourWidgetWidth and yourWidgetHeight you can achieve what you want
Edit
Here is a concept of what am trying to explain
// In variable declaration scope
GlobalKey youTextKey = GlobalKey();
ValueNotifier widthNotifier = ValueNotifier(0);
ValueNotifier heightNotifier = ValueNotifier(0);
// In initState
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
widthNotifier.value = youTextKey.currentContext.size.width - 1.0;
heightNotifier.value = youTextKey.currentContext.size.height - 5.0;
}
});
// Your row
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
'Winner: ',
style: TextStyle(fontSize: 34),
),
Stack(
children: [
Text('YOU!!!', style: TextStyle(fontSize: 34), key: youTextKey,),
ValueListenableBuilder(
valueListenable: widthNotifier,
builder: (context, width, _) {
return ValueListenableBuilder(
valueListenable: heightNotifier,
builder: (context, height, _) {
return Container(
width: width,
height: height,
color: Colors.amber,
);
},
);
},
),
],
),
],
)

Using Positioned.Fill solved the problem:
Positioned.Fill Creates a Positioned object with [left], [top], [right], and [bottom] set to 0.0 unless a value for them is passed.
so the Positioned child get stretched to the edges of the stack's biggest child.
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
'Winner: ',
style: TextStyle(fontSize: 34),
),
Stack(
children: [
Text('YOU!!!', style: TextStyle(fontSize: 34)),
Positioned.fill(
child: Container(
color: Colors.amber,
),
)
],
),
],
),

Related

Flutter TextBaseline in Row and IntrinsicHeight doesn't work

Is there any way to achieve text baseline with VericalDivider() where the Rows() under the Column() and required IntrinsicHeight() to make the VerticalDiver() render properly.
Meanwhile, look like there is limitation around CrossAxisAlignment.baseline and IntrinsicHeight https://github.com/flutter/flutter/issues/71990#issuecomment-747141796
From the picture below, I can't use CrossAxisAlignment.baseline with IntrinsicHeight Widget so the text that has different size will look something like this. But if I remove IntrinsicHeight the VericalDivider Widget will not render. (Height = 0)
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("hey"),
IntrinsicHeight(
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(child: Text("aaaaaxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")),
VerticalDivider(
width: 50,
thickness: 50,
),
Expanded(child: Text("bbbbbb", style: TextStyle(fontSize: 30),))
],
),
),
],
),
);
}
}

How to fix "`RenderFlex children have non-zero flex but incoming height constraints are unbounded." in Flutter?

I'm very new to Flutter and frontend design in general. I've tried looking for the answer online and have tried some of the suggestions on other posts, but they don't match my situation exactly and I keep getting confused. If anyone could offer some guidance I would really appreciate it!
I'm trying to make a custom table widget composed of a title, ListView, and a row of IconButtons. I'm having trouble wrapping my head around how to limit to and fit to containers. I keep getting a error stating RenderFlex children have non-zero flex but incoming height constraints are unbounded. I know it has something to do with the boundaries and I need to use either Flexible or Expanded to fix it, but I've been at it for a while and am not getting anywhere.
#override
Widget build(BuildContext context) {
return Focus(
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Theme.of(context).colorScheme.tertiary)),
padding: const EdgeInsets.fromLTRB(0, 0, 0, 2),
margin: const EdgeInsets.fromLTRB(13, 2, 13, 2),
clipBehavior: Clip.antiAlias,
child: Column(children: [
Text(
widget.title,
style: Theme.of(context).textTheme.bodyLarge,
),
ListView(
shrinkWrap: true,
children: widget.children,
),
Flexible(
child: Row(
children: [
//PLUS BUTTON
Expanded(
child: IconButton(
onPressed: () {
setState(() {
// updating the state
widget.children.add(ReportInputTableRow(
rowIndex: widget.children.isNotEmpty
? widget.children.length - 1
: 0,
onFocus: (row, column) {
updateCurrent(row, column);
},
));
});
},
icon: Icon(
Icons.plus_one_sharp,
color: Theme.of(context).colorScheme.secondary,
),
splashRadius: 15.0,
),
)
//PLUS BUTTON
],
))
]),
));
}
EDIT:
As requested, here is the code for ReportTableInputRow
class _ReportInputTableRowState extends State<ReportInputTableRow> {
#override
Widget build(BuildContext context) {
return Focus(
child: Row(
children: [
Focus(
child: const Expanded(
child: TextInputField(
text: "Original",
size: 13,
padded: false,
),
),
onFocusChange: (hasFocus) {
if (hasFocus) widget.columnIndex = 0;
}),
Focus(
child: const Expanded(
child: TextInputField(
text: "Note",
size: 13,
padded: false,
)),
onFocusChange: (hasFocus) {
if (hasFocus) widget.columnIndex = 1;
}),
],
),
onFocusChange: (hasFocus) {
widget.onFocus != null
? widget.onFocus!(widget.rowIndex, widget.columnIndex)
: null;
},
);
}
}
EDIT:
The solution was to swap Expanded with Focus in ReportInputTableRowState.
Maybe this one can help you:
Put the ListView inside Expanded and remove the Flexible which wraps the Row beneath the ListView.
child: Column(children: [
Text(
widget.title,
style: Theme.of(context).textTheme.bodyLarge,
),
ListView(
shrinkWrap: true,
children: children,
),
Flexible(
child: Row(
children: [
//PLUS BUTTON
Expanded(
child: IconButton(
onPressed: () {
...
You should also avoid changing widget variables, because they are considered to be immutable. If you want to mutate variables, put them inside the state.
Instead of widget.children.add(...) you should call children.add(...) with children being a state variable.
Actually you may not need Expanded or Flex, since you icons should have a defined size, you can wrap it using a SizedBox with defined height.
SizedBox(
height: 32, // It can be the height you desire, no need to define width
child: Row(
children: [
... // The icons that are children of the row go here
],
),
),

Let wrap take full width

How can I make the red box to use full width and put the button to the very right of the screen when wrapping?
Also I want spaceBetween if the button is not wrapping, but it does not work:
This is what I have sofar:
Column(
children: [
Align(
alignment: Alignment.topLeft,
child: Container(
color: Colors.red,
child: Wrap(
alignment: WrapAlignment.end,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Container(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"Please wrap on small screens",
style: Theme.of(context).textTheme.bodyText1,
),
),
),
TextButton(
onPressed: () {},
child: Text("Bearbeiten"))
],
),
),
),
// ...
]
)
Updated
Finally, I manage to solve your problem. Probably is not a canonic way, but it's working.
You need to compute the total width of your content in order to compare it with the width of the screen (the width is the width of the widgets, plus space you want between widgets, plus padding from both sides).
With that, you are able to know when you need to change the layout, and you can apply the layout you want to each case.
Here I leave the code:
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final GlobalKey textKey = GlobalKey();
final GlobalKey buttonKey = GlobalKey();
double widthLimit = double.infinity;
void initState() {
super.initState();
SchedulerBinding.instance.addPostFrameCallback((timeStamp) {
final textBox = textKey.currentContext.findRenderObject() as RenderBox;
final buttonBox = buttonKey.currentContext.findRenderObject() as RenderBox;
widthLimit = textBox.size.width + buttonBox.size.width + 8 + 16 * 2;
});
}
Widget _wrap(bool spaceBetween, Widget child) => spaceBetween
? child
: Align(
alignment: Alignment.centerRight,
child: child,
);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Container(
width: double.infinity,
padding: EdgeInsets.all(16.0),
color: Colors.red,
child: Builder(builder: (context) {
final screenWidth = MediaQuery.of(context).size.width;
final spaceBetween = screenWidth >= widthLimit;
return Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
alignment: WrapAlignment.spaceBetween,
children: [
Text("Please wrap on small screen small screen", key: textKey),
_wrap(
spaceBetween,
TextButton(
key: buttonKey,
onPressed: () {},
child: Text("Bearbeiten"),
)),
],
);
}),
),
),
);
}
}
Old Answer
I am going to take a couple of premises from your screenshots:
The Wrap doesn't have to take the full width by default
You only want to go to two lines when there is not enough space in the device screen.
With that, you can make what you want with this code:
Container(
padding: EdgeInsets.all(16.0),
color: Colors.red,
child: Wrap(
spacing: 8,
crossAxisAlignment: WrapCrossAlignment.center,
alignment: WrapAlignment.end,
children: [
Text("Please wrap on small screen"),
TextButton(onPressed: () {}, child: Text("Bearbeiten")),
]),
),
I added the spacing property to the Wrap to provide space as you wanted when both widgets are at the same line. Also, I set the alignment of the Wrap to end to align as you want.
When there is not enough space on the screen, the widget is going to move to a new line and align to the end. As the first widget is longer than the wrapped one and, as I said in the premises, we don't need to take the full width of the screen, Wrap will adapt to keep things aligned properly.

Flutter: How can I make an button to show a random by me defined stateless Widget?

I have an button, and when it gets pressed, an stateless Widget consisting of an Column with a few Text and an Image.Asset is shown. So now I have a few of these stateless Widgetswith different content. When I press the button, i want him now to show a ramdom one of these stateless Widgets, maybe an option for a specific order too.
How do I do that?
Button and comamnd for showing the Widget at the moment:
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
if (isShown == true) Zauberwuerfel(),
SizedBox(
width: 150,
height: 100,
),
Container(
padding: EdgeInsets.only(bottom: 60),
width: 230,
height: 130,
child: RaisedButton(
color: Colors.red,
child: Text('Skill', style: TextStyle(fontSize: 30)),
onPressed: () {
setState(() {
isShown = true;
One of those stateless Widgets:
class SkillJonglieren extends StatelessWidget {
Widget build(BuildContext context) {
return Column(children: <Widget>[
Text(
'Jonglieren',
style: TextStyle(fontSize: 35),
),
SizedBox(
width: 20,
height: 25,
),
Image.asset('images/jonglieren.jpg', scale: 5),
SizedBox(
width: 20,
height: 30,
),
Row(mainAxisAlignment: MainAxisAlignment.center, children: <Text>[
Text(
'Schwierigkeit:',
style: TextStyle(fontSize: 25),
),
Text(
' Einfach',
style: TextStyle(fontSize: 25, color: Colors.green),
Use Random class to generate a random number.
The range of the random will always be equal to the number of StatelessWidget you have
You store a List which will keep your StatelessWidget
When the button is pressed, you get the random number, and then pick up the index based upon that random number generated
Assumptions: Let us assume you have 5 StatelessWidgets namely Widget1(), Widget2(), Widget3(), Widget4() and Widget5()
Let us quickly jump into the code
import 'dart:math';
// Each Widget is located at an index which is unique
int randomNumber;
List<Widget> _widgets = [Widget1(), Widget2(), Widget3(), Widget4(), Widget5()];
// generating random number
void _generateRandomNumber(){
var random = new Random();
// now the range works like 0 to n-1
setState(() => randomNumber = random.nextInt(_widgets.length));
}
//Triggering the function when showing the item
RaisedButton(
color: Colors.red,
child: Text('Skill', style: TextStyle(fontSize: 30)),
onPressed: () {
//calling our method
_generateRandomNumber();
setState(() => isShown = true);
}
)
Now showing the data finally
Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
if (isShown == true) _widgets[randomNumber]
....
]
)
Assign random numbers with the rand() function in dart:math to some variables. then use condition.
First of all, you need to make your main widget a StateFulWidget since you want to toggle the isShown variable and make changes to the views. Then you need to make a list of Widgets(e.g., Widget1 and Widget2) in your main widget(which has the button in):
final List<Widget> widgets = [Widget1(), Widget2()];
To select one of these widgets randomly you can use:
(widgets..shuffle()).first
You need to select a new random widget in onPressed. To show the selected widget only when isShown is true, you can use a Visibility widget(the if method that you're using is ok, too). Finally set the child of this Visibility widget to the selected widget.
Full code:
final List<Widget> widgets = [Zauberwuerfel(), SkillJonglieren()];
var _isShown = false;
Widget _selectedWidget = Zauberwuerfel();
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey,
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Visibility(
visible: _isShown,
child: _selectedWidget,
),
SizedBox(
width: 150,
height: 100,
),
Container(
padding: EdgeInsets.only(bottom: 60),
width: 230,
height: 130,
),
RaisedButton(
color: Colors.red,
child: Text('Skill', style: TextStyle(fontSize: 30)),
onPressed: () {
setState(() {
_isShown = true;
_selectedWidget = (widgets..shuffle()).first;
});
},
)
]),
));
}

Flutter : Vertically center column

How to vertically center a column in Flutter? I have used widget "new Center". I have used widget "new Center", but it does not vertically center my column ? Any ideas would be helpful....
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Thank you"),
),
body: new Center(
child: new Column(
children: <Widget>[
new Padding(
padding: new EdgeInsets.all(25.0),
child: new AnimatedBuilder(
animation: animationController,
child: new Container(
height: 175.0,
width: 175.0,
child: new Image.asset('assets/angry_face.png'),
),
builder: (BuildContext context, Widget _widget) {
return new Transform.rotate(
angle: animationController.value * 6.3,
child: _widget,
);
},
),
),
new Text('We are glad we could serve you...', style: new TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w600,
color: Colors.black87),),
new Padding(padding: new EdgeInsets.symmetric(vertical: 5.0, horizontal: 0.0)),
new Text('We appreciate your feedback ! !', style: new TextStyle(
fontSize: 13.0,
fontWeight: FontWeight.w200,
color: Colors.black87),),
],
),
),
);
}
Solution as proposed by Aziz would be:
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
//your widgets here...
],
)
It would not be in the exact center because of padding:
padding: EdgeInsets.all(25.0),
To make exactly center Column - at least in this case - you would need to remove padding.
Try:
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children:children...)
Try this one. It centers vertically and horizontally.
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: children,
),
)
With Column, use:
mainAxisAlignment: MainAxisAlignment.center
It align its children(s) to center of its parent Space vertically
You control how a row or column aligns its children using the mainAxisAlignment and crossAxisAlignment properties. For a row, the main axis runs horizontally and the cross axis runs vertically. For a column, the main axis runs vertically and the cross axis runs horizontally.
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
While using Column, use this inside the column widget :
mainAxisAlignment: MainAxisAlignment.center
It align its children(s) to the center of its parent Space is its main axis i.e. vertically
or,
wrap the column with a Center widget:
Center(
child: Column(
children: <ListOfWidgets>,
),
)
if it doesn't resolve the issue wrap the parent container with a Expanded widget..
Expanded(
child:Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: children,
),
),
)
Another Solution!
If you want to set widgets in center vertical form, you can use ListView for it.
for eg: I used three buttons and add them inside ListView which followed by
shrinkWrap: true -> With this ListView only occupies the space which needed.
import 'package:flutter/material.dart';
class List extends StatelessWidget {
#override
Widget build(BuildContext context) {
final button1 =
new RaisedButton(child: new Text("Button1"), onPressed: () {});
final button2 =
new RaisedButton(child: new Text("Button2"), onPressed: () {});
final button3 =
new RaisedButton(child: new Text("Button3"), onPressed: () {});
final body = new Center(
child: ListView(
shrinkWrap: true,
children: <Widget>[button1, button2, button3],
),
);
return new Scaffold(
appBar: new AppBar(
title: Text("Sample"),
),
body: body);
}
}
void main() {
runApp(new MaterialApp(
home: List(),
));
}
Output:
CrossAlignment.center is using the Width of the 'Child Widget' to center itself and hence gets rendered at the start of the page.
When the Column is centered within the page body's 'Center Container' , the CrossAlignment.center uses page body's 'Center' as reference and renders the widget at the center of the page
Code
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(
title:"DynamicWidgetApp",
home:DynamicWidgetApp(),
));
class DynamicWidgetApp extends StatefulWidget{
#override
DynamicWidgetAppState createState() => DynamicWidgetAppState();
}
class DynamicWidgetAppState extends State<DynamicWidgetApp>{
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
//Removing body:Center will change the reference
// and render the widget at the start of the page
child: Column(
mainAxisAlignment : MainAxisAlignment.center,
crossAxisAlignment : CrossAxisAlignment.center,
children: [
Text("My Centered Widget"),
]
),
),
floatingActionButton: FloatingActionButton(
// onPressed: ,
child : Icon(Icons.add),
),
);
}
}
For me the problem was there was was Expanded inside the column which I had to remove and it worked.
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded( // remove this
flex: 2,
child: Text("content here"),
),
],
)
You could use.
mainAxisAlignment:MainAxisAlignment.center
This will the material through the center in the column wise.
`crossAxisAlignment: CrossAxisAlignment.center'
This will align the items in the center in the row wise.
Container( alignment:Alignment.center, Child: Column () )
Simply use.
Center ( Child: Column () )
or rap with Padding widget . And adjust the Padding such the the column children are in the center.
You can also wrap the Column widget by Align.
Align(
alignment: Alignment.center,
child: Column(
children: [
Container(
width: 300,
margin: const EdgeInsets.fromLTRB(0, 70, 0, 0),
child: TextFormField(decoration: const InputDecoration(hintText: "First Name"))
),
...
]
)
)
Checkout this website for different ways of centering a widget: Link
In Addition, If you used
mainAxisAlignment: MainAxisAlignment.start
for centering all children but you still one of the children to be centered , Simply use Center() widget on the children.