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
Related
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);
}
}
I'm trying to design in Flutter. I don't know how to design this arrow in container.
You can use CustomPainer or ClipPath for this. You can also check this answer for this kind of layout.
class PriceTagPaint extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..color = Colors.red
..strokeCap = StrokeCap.round
..style = PaintingStyle.fill;
Path path = Path();
path
..lineTo(size.width * .85, 0) // .85 amount of right gap
..lineTo(size.width, size.height / 2)
..lineTo(size.width * .85, size.height)
..lineTo(0, size.height)
..lineTo(0, 0)
..close();
canvas.drawPath(path, paint);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
And use
SizedBox(
height: 100,
width: 250,
child: CustomPaint(
painter: PriceTagPaint(),
child: Align(
alignment: Alignment(-.2, 0), //litle left
child: Text(
"-11% Off",
style: TextStyle(
fontSize: 44,
color: Colors.white,
fontWeight: FontWeight.bold),
),
),
),
),
Result
More about CustomPaint
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
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,
),
],
),
));