How to force a row to take the height of one (or the smallest) of its children - flutter-layout

I would like the row (containing the green and the red widgets) or the green widget to take the red widget's height, but without using a fixed size or a ratio anywhere.
The red widget must impose its size which depends on its content.
Is it possible (in a simple way) ?
image
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Expanded(
child: Row(
children: [
Expanded(child: MyContainer(color: Colors.red, child: Column(children: generateTexts(10)))),
Expanded(child: MyContainer(color: Colors.green, child: ListView(children: generateTexts(50))))
],
),
),
Expanded(child: MyContainer(color: Colors.blue, child: Center(child: Text('REMAINING HEIGHT OF THE COLUMN')))),
MyContainer(
height: 50,
color: Colors.yellow,
child: Center(child: Text('FIXED HEIGHT')),
)
],
));
}
}
List<Widget> generateTexts(int count) {
return List<Widget>.generate(count, (index) => Text('text $index'));
}
class MyContainer extends StatelessWidget {
const MyContainer({this.child, this.color = Colors.transparent, this.width, this.height});
final Widget? child;
final Color color;
final double? width;
final double? height;
#override
Widget build(BuildContext context) {
return Container(
child: child,
decoration: BoxDecoration(
color: color, border: Border.all(color: Colors.black), borderRadius: BorderRadius.circular(12)),
width: width,
height: height);
}
}

Related

A RenderFlex overflowed by 99761 pixels on the bottom

This is my code, I am passing the "Keyname" to the stateless widget, this widget is wrapped with Flexible widget in a different dart file.
I am getting this error:
The overflowing RenderFlex has an orientation of Axis.vertical.
The edge of the RenderFlex that is overflowing has been marked in the rendering with a yellow and black striped pattern. This is usually caused by the contents being too big for the RenderFlex.
Consider applying a flex factor (e.g. using an Expanded widget) to force the children of the RenderFlex to fit within the available space instead of being sized to their natural size.
This is considered an error condition because it indicates that there is content that cannot be seen. If the content is legitimately bigger than the available space, consider clipping it with a ClipRect widget before putting it in the flex, or using a scrollable container rather than a Flex, like a ListView.
Here is the code:
Flexible(
flex: 2,
child: Container(
padding: EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
KeyBoardButtonFirstRow(
width: deviceWidth / 6,
color: Color(0xfffdc40d),
keyName: '2nF',
),
KeyBoardButtonFirstRow(
width: deviceWidth / 6,
color: Color(0xff52e2fe),
),
KeyBoardButtonFirstRow(
width: deviceWidth / 2,
color: Color(0xffabbfc5),
),
],
),
class KeyBoardButtonFirstRow extends StatelessWidget {
final String keyName;
final double width;
final Color color;
KeyBoardButtonFirstRow({this.keyName, this.width, this.color});
#override
Widget build(BuildContext context) {
return Container(
height: 30.0,
width: width,
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(10.0),
boxShadow: [BoxShadow(
color: Colors.white.withOpacity(0.5),
blurRadius: 2.0,
)],
),
child: Text(keyName),
);
}
}
These are the changes you you need to make to your code,
class Test0 extends StatefulWidget {
#override
_Test0State createState() => _Test0State();
}
class _Test0State extends State<Test0> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Flexible(
flex: 2,
child: Container(
height: MediaQuery.of(context).size.width,
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
KeyBoardButtonFirstRow(
width: MediaQuery.of(context).size.width / 6,
color: Color(0xfffdc40d),
keyName: '2nF',
),
],
)
])
))
],
),
);
}
}
class KeyBoardButtonFirstRow extends StatelessWidget {
final String keyName;
final double width;
final Color color;
KeyBoardButtonFirstRow({this.keyName, this.width, this.color});
#override
Widget build(BuildContext context) {
return Container(
height: 30.0,
width: width,
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(10.0),
boxShadow: [
BoxShadow(
color: Colors.white.withOpacity(0.5),
blurRadius: 2.0,
)
],
),
child: Text(keyName),
);
}
}
Here I have Scaffold, in your case, it could be any constrained widget.

Visibility won't toggle in flutter?

I've got a gridView where each grid has a FlatButton inside it. The button is supposed to trigger the visibility for another button I have outside the GridView. I've set the state in onPressed to change the bool showCard for everytime the GridView button is pressed. In my print statement, it's saying that it's working, producing true and false each time the button is pressed, but it's not changing the visibility of the other button called 'CheckoutCard()'. Can anyone help me?
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
import 'package:bee/Cards/Items%20Card.dart';
import 'package:bee/Constants/Constants.dart';
import 'package:bee/MerchantCategories/My Categories.dart';
import 'package:bee/MenuButtons/Color Changer.dart';
import 'package:bee/Cards/Checkout Card.dart';
import 'package:bee/main.dart';
import 'Basket Menu.dart';
class MyMenu extends StatefulWidget {
#override
_MyMenuState createState() => _MyMenuState();
}
class _MyMenuState extends State<MyMenu> {
// bool showCard = _MyButtonState().showCard;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
brightness: Brightness.light,
leading: new IconButton(
icon: new Icon(Icons.arrow_back, color: Colors.black),
onPressed: () { Navigator.of(context).pop();},
),
backgroundColor: Colors.white,
title: Padding(
padding: const EdgeInsets.all(6.0),
child: Image(image: AssetImage('images/Merchants/My_Image.png'),),
),
elevation: 1.0,
centerTitle: true,
actions: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 15.0),
child: IconButton(icon: Icon(Icons.shopping_basket, color: Colors.blue[800],),
onPressed: (){ Navigator.push(context, MaterialPageRoute(builder: (context){return BasketMenu();})); }
),
),
],
),
body: Stack(
children: <Widget>[
Flex(
direction: Axis.vertical,
children: <Widget>[
Expanded(
flex: 1,
child: Padding(
padding: const EdgeInsets.all(5.0),
child: Container(
child: ListView(children: <Widget>[
MyCategories(categoryText: Text('Restaurants', style: categoryTextStyle),),
MyCategories(categoryText: Text('Bars', style: categoryTextStyle),),
MyCategories(categoryText: Text('Games', style: categoryTextStyle),),
],
scrollDirection: Axis.horizontal,
),
),
),
),
Expanded(
flex: 10,
child: Container(
child: GridView.count(
crossAxisCount: 2,
children: <Widget>[
ItemsCard(
categoryName: Text('Fast Food', style: secondCategoryTextStyle,),
itemText: FastFoodText,
priceText: Text('£21.67', style: priceTextStyle,),
gridOutline: MyButton(
tile: GridTile(
child: FastFoodImage,
),
),
),
ItemsCard(
itemText: SnubbText,
priceText: Text('£44.95', style: priceTextStyle,),
gridOutline: MyButton(
tile: GridTile(
child: SnubbImage,
),
),
),
ItemsCard(
itemText: FreshText,
priceText: Text('£41.23', style: priceTextStyle,),
gridOutline: MyButton(
tile: GridTile(
child: FreshImage,
),
),
),
Container(),
],
),
),
),
],
),
Visibility(visible: _MyButtonState().showCard ? _MyButtonState().showCard : !_MyButtonState().showCard, child: CheckoutCard()),
],
),
);
}
}
class MyButton extends StatefulWidget {
#override
State<StatefulWidget> createState(){
return _MyButtonState();
}
MyButton({this.tile});
final GridTile tile;
bool isVisible = false;
int itemNumber = 0;
bool showCheckoutCard(){
return isVisible = !isVisible;
}
int itemCounter(){
return itemNumber++;
}
}
class _MyButtonState extends State<MyButton> {
bool changeColor = false;
static var myNewButtonClass = MyButton();
bool showCard = myNewButtonClass.isVisible;
#override
Widget build(BuildContext context) {
return FlatButton(
shape: Border(bottom: BorderSide(color: changeColor ? Colors.blue[800] : Colors.transparent, width: 3.0)),
child: widget.tile,
onPressed: (){
setState(() {
changeColor = !changeColor;
myNewButtonClass.itemCounter();
print(myNewButtonClass.itemCounter());
setState(() {
showCard = !showCard;
print(showCard);
});
});
},
);
}
}
You are calling the setState method inside your Button. I don't think it will change the state of your MyMenu widget. I would suggest you to change your Button as following:
class MyButton extends StatelessWidget {
final Color color;
final GridTile tile;
final VoidCallback onPressed;
const MyButton({Key key, this.color, this.tile, this.onPressed})
: super(key: key);
#override
Widget build(BuildContext context) {
return FlatButton(
shape: Border(bottom: BorderSide(color: color)),
child: tile,
onPressed: onPressed,
);
}
}
After that, you need to declare two variable in your MyMenu widget as follows:
class _MyMenuState extends State<MyMenu> {
bool changeColor = false;
bool showCard = false;
#override
Widget build(BuildContext context) {
yourBuildMethod()
In your MyMenu widget you can call button like this:
MyButton(
tile: GridTile(child: SnubbImage),
color: changeColor
? Colors.blue[800]
: Colors.transparent,
onPressed: () {
setState(() {
changeColor = !changeColor;
showCard = !showCard;
});
},
),
And now check your Visibility like this:
Visibility(
visible: showCard,
child: CheckoutCard(),
)
Now your variables are in your MyMenu widget and you are calling setState function in MyMenu widget. So it will be able to update the state of your widget. I hope this will be helpful for you.
To trigger a rebuild of your view based when changing the value of a variable you need to use setState.
Where you are are changing the value of the isVisible variable, you need to surround it with a setState:
setState(() {
isVisible = !isVisible;
});

Show masked area on top of camera widget

I'm trying to implement a credit card reader (using some API). For the user to take a picture of the card, I'm presenting the camera preview widget full screen. I would like to mask out an area so the user centers the card there.
He're an illustration of what I have in mind
I was wondering how this masking could be accomplished in flutter? Currently I'm just drawing a BoxDecoration, but that's missing the semi transparent, greyed out area.
Container(
width: MediaQuery.of(context).size.width * 0.8,
height: MediaQuery.of(context).size.width * 0.8 / 1.55,
decoration: new BoxDecoration(
border: Border.all(
color: Colors.white,
width: 2.0,
style: BorderStyle.solid),
shape: BoxShape.rectangle,
borderRadius: BorderRadius.all(Radius.circular(4.0))),
)
You can try something like this for the time being but it is a very inefficient solution and I am sure there must be a better solution that I too would like to know.
#override
Widget build(BuildContext context) {
if (!controller.value.isInitialized) {
return Container();
}
return Container(
height: MediaQuery.of(context).size.height,
child: Stack(
children: <Widget>[
CustomPaint(
foregroundPainter: P(),
child: CameraPreview(controller),
),
ClipPath(
clipper: Clip(),
child: CameraPreview(controller)),
],
),
);
}
class P extends CustomPainter{
#override
void paint(Canvas canvas, Size size) {
canvas.drawColor(Colors.grey.withOpacity(0.8), BlendMode.dstOut);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
// TODO: implement shouldRepaint
return true;
}
}
class Clip extends CustomClipper<Path>{
#override
getClip(Size size) {
print(size);
Path path = Path()
..addRRect(RRect.fromRectAndRadius(Rect.fromLTWH(10, size.height/2-120, size.width-20, 240), Radius.circular(26)));
return path;
}
#override
bool shouldReclip(oldClipper) {
// TODO: implement shouldReclip
return true;
}
}
I now just went with a custom BoxPainter and first draw the background, then the card area and finally blend the layers together to 'cut out' the center part.
import 'package:flutter/widgets.dart';
class CardDecoration extends Decoration {
#override
BoxPainter createBoxPainter([VoidCallback onChanged]) {
return _CardPainter();
}
}
class _CardPainter extends BoxPainter {
#override
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
final clearPaint = new Paint()
..color = Colors.black
..style = PaintingStyle.fill
..blendMode = BlendMode.dstOut;
final bgPaint = new Paint()
..color = Color.fromARGB(150, 0, 0, 0)
..style = PaintingStyle.fill;
final borderPaint = new Paint()
..color = Colors.white.withAlpha(120)
..style = PaintingStyle.stroke
..strokeWidth = 3.0;
final rect = offset & configuration.size;
final cardWidth = 0.8*rect.width;
final cardHeight = cardWidth/1.55;
canvas.saveLayer(Rect.fromLTRB(0, 0, rect.width, rect.height), clearPaint);
canvas.drawPaint(bgPaint);
canvas.saveLayer(Rect.fromLTRB(0, 0, rect.width, rect.height), clearPaint);
canvas.drawRRect(RRect.fromLTRBR(0.1*rect.width, (rect.height-cardHeight)/2, 0.9*rect.width, (rect.height+cardHeight)/2, Radius.circular(8)), bgPaint);
canvas.restore();
canvas.restore();
canvas.drawRRect(RRect.fromLTRBR(0.1*rect.width, (rect.height-cardHeight)/2, 0.9*rect.width, (rect.height+cardHeight)/2, Radius.circular(8)), borderPaint);
}
}
Probably, you can try with columns, rows, and containers :).
Widget getMaskCard(BuildContext context) {
Color _background = Colors.grey.withOpacity(0.2);
return Column(
children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: Container(
height: MediaQuery.of(context).size.height,
width: 1,
color: _background,
),
),
Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width * 0.8,
child: Column(
children: <Widget>[
Expanded(
child: Container(
color: _background,
),
),
Container(
height: 180,
width: MediaQuery.of(context).size.width * 0.8,
color: Colors.transparent,
),
Expanded(
child: Container(
color: _background,
),
),
],
),
),
Expanded(
child: Container(
height: MediaQuery.of(context).size.height,
width: 1,
color: _background,
),
),
],
)
],
);
}
Example

How to implement a flutter layout more easily?

I have a layout effect, I don't know how to implement it more easily? I have six or N child widgets, placed in a parent widget with two child widgets per row, each widget is 50% the width of the parent widget and height is the height /rows of the parent widget.
I can use column, row expanded to do this, but I don't think it's simple enough.If my child widgets are intermediate, I don't know how to create them dynamically.
The layout effect what I want to achieve:
The way I want to do it is the following pseudocode
I can do it in Android and iOS, but I don't know how to do it with flutter.
var parentWidget = Widget()
for (var i = 0; i < 6; i++) {
var child = Widget()
parentWidget.add(child)
}
The Flutter is implemented as follows. I can use column,row expanded to do this, but I don't think it's simple enough. If my child widgets are indeterminate, I don't know how to create them dynamically.
Expanded(
child: Column(
children: <Widget>[
Expanded(
flex: 1,
child: Row(children: <Widget>[
Expanded(child: Text("1"),),
Expanded(child: Text("1"),),
],)
),
Expanded(
flex: 1,
child:Row(children: <Widget>[
Expanded(child: Text("1"),),
Expanded(child: Text("1"),),
],),
),
Expanded(
flex: 1,
child: Row(children: <Widget>[
Expanded(child: Text("1"),),
Expanded(child: Text("1"),),
],),
)
],
),
)
I've come up with a way to deal with this requirement by calculating the width and height of each child widget and then placing them in wrap to arrange themselves
class RowFixedWrap extends StatefulWidget {
final double spacing;
final double runSpacing;
final int columnCount;
final List<Widget> childern;
RowFixedWrap(
{Key key, this.spacing, this.runSpacing, this.columnCount, this.childern})
: super(key: key);
#override
State<StatefulWidget> createState() {
return _FixedWrapState();
}
}
class _FixedWrapState extends State<RowFixedWrap> {
#override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
var itemWidth =
(constraints.maxWidth - widget.spacing * (widget.columnCount - 1)) /
widget.columnCount;
var rows = widget.childern.length % widget.columnCount != 0
? (widget.childern.length ~/ widget.columnCount) + 1
: (widget.childern.length ~/ widget.columnCount);
var itemHeight =
(constraints.maxHeight - widget.runSpacing * (rows - 1)) / rows;
return Wrap(
spacing: widget.spacing,
runSpacing: widget.runSpacing,
children: widget.childern.map((widget) {
return SizedBox(
width: itemWidth,
height: itemHeight,
child: widget,
);
}).toList());
},
);
}
}
Expanded(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: RowFixedWrap(
spacing: 10,
runSpacing: 10,
columnCount: 2,
childern: <Widget>[
Container(
color: Colors.red,
),
Container(
color: Colors.red,
),
Container(
color: Colors.red,
),
Container(
color: Colors.red,
),
],
),
));

FittedBox and Text whitespace issue

I'm trying to make all Text(length varies) in a fixed width box to fit and I want them to look at the same size as the longest word's size when FittedBox applied. So What I'm doing to achieve that is filling the rest of the word with empty space to match the length to the longest word. But that doesn't quite work as you see in the following image:
Here is the code:
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,
),
routes: {
'/': (context) => HomePage(),
},
);
}
}
class HomePage extends StatelessWidget {
final String padding = " " * 5;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home'),
),
body: Center(
child: Container(
color: Colors.red,
height: 100.0,
width: 100.0,
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Center(
child: FittedBox(
fit: BoxFit.scaleDown,
child: Text(
"${padding}Demo$padding",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.black),
),
),
),
Icon(Icons.mic, size: 24.0),
],
),
),
),
);
}
}
Do you have any idea what is the issue here?
It's really strange, but it seems to work correctly if you remove alignment or set it to start. (What cause this bug - I still don't know)
child: Text('${padding}Demo$padding',
style: TextStyle(color: Colors.black, decoration: TextDecoration.underline),)
I've added decoration to see these spaces

Resources