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,
),
],
),
));
Related
I'm building List of questions for my Quizzler app and want to access the question one by one but can't able to add the list of my questions in Text function of my app... So how should I add my list of strings to Text to display them on screen one by one?
enter image description here
class QuizPage extends StatefulWidget {
#override
_QuizPageState createState() => _QuizPageState();
}
class _QuizPageState extends State<QuizPage> {
List<Widget> scorekeeper = [];
List<String> questions = [
'You can lead a cow down stairs but not up stairs.',
'Approximately one quarter of human bones are in the feet.',
'A slug\'s blood is green.'
];
int questionNum = 0;
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
const Expanded(
flex: 3,
child: Padding(
padding: EdgeInsets.all(10.0),
child: Center(
child: Text(
questions[questionNum],
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 25.0,
color: Colors.white,
),
),
You can use ListView.builder like this:
ListView.builder(
itemCount: questions.length,
itemBuilder: (_, i) {
return Padding(
padding: EdgeInsets.all(10.0),
child: Center(
child: Text(
questions[i],
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 25.0,
color: Colors.white,
),
)
)
);
}
);
And remove key word const.
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.
I have several modalBottomSheet in my app to show, most of them have simple widget tree except only one that has 10 DropDownBottom widgets in it
Each one of them load about 200 items, each item is a widget consist of two main widgets a text and an image
when I Press
onPressed: () {
showModalBottomSheet(context: context, builder: picksWidget, isScrollControlled: true);
}
It take about 3 seconds to open load the modalBottomSheet and it just appear into the emulator without the sliding up animation, other modalBottomSheets in the app load perfectly fine, here is an example of the code I use.
import 'package:flutter/material.dart';
import 'package:myapp/data/picked.dart';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:myapp/data/picks.dart';
import 'package:myapp/data/Icons.dart';
Widget buildPickerBottomSheet(BuildContext context) {
return Wrap(children: <Widget>[
PickerList(),
]);
}
class PickerList extends StatelessWidget {
const PickerList({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
padding: EdgeInsets.all(20),
child: Row(
children: <Widget>[
AutoSizeText(
'Pick ',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
textAlign: TextAlign.right,
maxLines: 1,
),
],
),
),
SizedBox(
height: 5,
),
PickerRow(
typeOne: 'vr',
typeTwo: 'er',
type: 'r',
),
PickerRow(
typeOne: 'vq',
typeTwo: 'eq',
type: 'q',
),
PickerRow(
typeOne: 'vw',
typeTwo: 'ew',
type: 'w',
),
PickerRow(
typeOne: 'vz',
typeTwo: 'ez',
type: 'z',
),
PickerRow(
typeOne: 'vy',
typeTwo: 'ey',
type: 'y',
),
SizedBox(
height: 10,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
RaisedButton(
child: Text('Add'),
onPressed: () async {
print('added');
},
),
RaisedButton(
child: Text('Cancel'),
onPressed: () {
Navigator.pop(context);
},
)
],
),
SizedBox(
height: 50,
)
],
);
}
}
class PickerRow extends StatelessWidget {
final String typeOne;
final String typeTwo;
final String type;
PickerRow({#required this.typeOne, #required this.typeTwo, #required this.type});
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(left: 20, right: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
DropDownMenu(
pickType: typeOne,
),
Container(
width: 2,
height: 30,
color: Colors.blue,
),
Container(
height: 30,
width: 30,
child: Image(
image: AssetImage(AppIcons.types[type]),
),
),
Container(
width: 2,
height: 30,
color: Colors.red,
),
DropDownMenu(
pickType: typeTwo,
),
],
),
);
}
}
class DropDownMenu extends StatefulWidget {
//which position will this pick for
final String pickType;
DropDownMenu({#required this.pickType});
#override
_DropDownMenuState createState() => _DropDownMenuState();
}
class _DropDownMenuState extends State<DropDownMenu> {
//get a list of the picks to display in the drop down
static List<DropdownMenuItem> getDropDownItems() {
List<DropdownMenuItem<String>> dropDownItems = [];
for (int i = 0; i < Picks.picksNames.length; i++) {
String pick = Picks.picksNames[i];
var newItem = DropdownMenuItem(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
CircleAvatar(
backgroundImage: AssetImage(AppIcons.picks[pick]),
radius: 15,
),
SizedBox(
width: 4,
),
AutoSizeText(
pick,
maxLines: 1,
),
],
),
value: pick,
);
dropDownItems.add(newItem);
}
return dropDownItems;
}
var items = getDropDownItems();
#override
Widget build(BuildContext context) {
String chosenItem = Picked.selection[widget.pickType];
return DropdownButton<String>(
value: chosenItem,
items: items,
onChanged: (value) {
setState(() {
chosenItem = value;
});
Picked.selection[widget.pickType] = value;
},
);
}
}
I am new to development in general so I appreciate if there is any resources on how to measure and improve performance on flutter apps.
Thanks.
It seems like your bottomModalSheet is too heavy. It loads only after the children have completed building.
Resize the images that you use inside the bottomModalSheet.
Usually the debug app is very slow compared to the release app.
Generate the apk of the app: flutter build apk --split-per-abi.
Install the generated apk build/app/outputs/apk/release/app-armeabi-v7a-release.apk and see if the problem still persists.
You should avoid rendering 2000 widgets at once.
First of all look at ListView widget and it's builder(). Try to refactor your code and put all your items inside this ListView. It will increase your performance a lot due to the reason that ListView.builder() allows you not to store all your widgets in memory all the time.
https://api.flutter.dev/flutter/widgets/ListView/ListView.builder.html
My problem is that : The listview does not appear at the first launch of app but after a hot reload it appears.
It's the first time I meet this bug.
That's my code :
#override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: Colors.black,
body: new ListView.builder(
itemCount: toutesLesCartes.length,
itemBuilder: (context, count) {
Carte carte = toutesLesCartes[count];
String nom = carte.nomCarte;
String image = carte.imageCarte;
return new Dismissible(
key: new Key(nom),
direction: DismissDirection.endToStart,
child: new Container(
margin: EdgeInsets.all(5.0),
child: new Card(
color: Colors.transparent,
elevation: 10.0,
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new Image.network(image, width: 150.0, height: 150.0,),
new CustomText(nom, factor: 2.0,),
],
),
),
),
background: new Container(
padding: EdgeInsets.only(right: 20.0),
color: Colors.red,
child: new Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
new CustomText("Supprimer"),
new Icon(Icons.delete, color: Colors.white,)
],
),
),
onDismissed: (direction) {
setState(() {
toutesLesCartes.removeAt(count);
nombreCarteChoisiValeur--;
});
},
);
}
),
);
}
I have an idea but it's strange : before, I using the Visibility widget so it is possible that it comes from a possible "cache" of the application?
All that will look at this post and help me, thank you very much!
Have a nice day !
Likely because toutesLesCartes.length is 0.
you can check this using the debugger or display something when the length is 0 eg
#override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: Colors.black,
body: new ListView.builder(
itemCount: toutesLesCartes.length, //* place breakpoint here
itemBuilder: (context, count) {
Carte carte = toutesLesCartes[count];
String nom = carte.nomCarte;
String image = carte.imageCarte;
if(toutesLesCartes == null || toutesLesCartes.length == 0){
return CircularProgressIndicator(); //you should see loading animation if list is empty
}
return new Dismissible(
key: new Key(nom),
direction: DismissDirection.endToStart,
child: new Container(
margin: EdgeInsets.all(5.0),
child: new Card(
color: Colors.transparent,
elevation: 10.0,
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new Image.network(image, width: 150.0, height: 150.0,),
new CustomText(nom, factor: 2.0,),
],
),
),
),
background: new Container(
padding: EdgeInsets.only(right: 20.0),
color: Colors.red,
child: new Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
new CustomText("Supprimer"),
new Icon(Icons.delete, color: Colors.white,)
],
),
),
onDismissed: (direction) {
setState(() {
toutesLesCartes.removeAt(count);
nombreCarteChoisiValeur--;
});
},
);
}
),
);
}
The solution would be to use a FutureBuilder
Word of warning: The application will run the build method multiple times, it's good practice to place anything you don't want to be repeatedly called (like database calls) outside of the method.
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