How to Make Widget Flutter Fast? - flutter

I'm trying to make my own photo codecs, I made a 512 * 512 image,
I'm just trying to build with one color and arrange in a Container in Column and Row
My Code:
SizedBox(
height: 512,
width: 512,
child: Column(
children: List.generate(512, (index) {
return Row(
children: List.generate(512, (index) {
return Container(
height: 1,
width: 1,
color: Colors.blue,
);
}),
);
}),
),
),
I tried this code, it is very slow,
So how to build flutter widget fast?

You have to use CustomPainter class to draw your own custom widget

As said by powerman23rus said, you should use a CustomPainter, here's an example of implementation based on the code you've provided:
class ImageWidget extends StatelessWidget {
final Color color;
final Size size;
const ImageWidget({
super.key,
this.color = Colors.blue,
this.size = const Size(512, 512),
});
#override
Widget build(BuildContext context) {
return CustomPaint(
painter: ImagePainter(color: color),
size: size,
);
}
}
class ImagePainter extends CustomPainter {
final Color color;
ImagePainter({required this.color});
#override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = color;
canvas.drawRect(Offset.zero & size, paint);
}
#override
bool shouldRepaint(ImagePainter oldDelegate) => false;
}
You can try the full example on DartPad to check by yourself the performance.

Related

flutter container with curve border

I want this type container with curve border, please check attach images
best solution of answer
I am Using ShapeBorder with paint.
class CustomShape extends ShapeBorder {
#override
EdgeInsetsGeometry get dimensions => EdgeInsets.zero;
#override
Path getInnerPath(Rect rect, {TextDirection? textDirection}) {
return getInnerPath(rect);
}
#override
Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
final double curveX = rect.width / 10;
Path rectPath = Path()
..addRRect(RRect.fromRectAndRadius(rect, const Radius.circular(24)));
Path curvePath = Path()
..moveTo(rect.center.dx - curveX, rect.top)
..quadraticBezierTo(
rect.center.dx,
rect.center.dy - curveX, //middle curve control
rect.center.dx + curveX,
rect.top,
);
return Path.combine(PathOperation.xor, rectPath, curvePath);
}
#override
void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) {
canvas.drawPath(
getOuterPath(rect),
Paint()
..color = Colors.red
..style = PaintingStyle.stroke);
}
#override
ShapeBorder scale(double t) => this;
}
And use
child: Container(
height: 200,
width: 500,
decoration: ShapeDecoration(
shape: CustomShape(),
),
),
Use quadraticBezierTo value to control the curve
I am pretty sure you will find something that will work here:
Flutter draw container with a curve in the center
Hope it helps.
Here is your Clip Code... and also use Shape Maker to design such layout and you will get clip code
Your Clip Container with border
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const MyStatefulWidget(),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({super.key});
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
#override
Widget build(BuildContext context) {
return Center(
child: CustomPaint(
painter: BorderPainter(),
child: Container(
height: 200,
width: 400,
child: Center(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Pakistan'),
Spacer(),
Text('VS'),
Spacer(),
Text('India'),
],
),
)
)
),
),
);
}
}
Clipping Code of Container
class BorderPainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = 2
..color = Colors.pink;
Path path0 = Path();
path0.moveTo(size.width*0.4995083,size.height*0.2401000);
path0.quadraticBezierTo(size.width*0.5840167,size.height*0.2406000,size.width*0.6666667,size.height*0.1420143);
path0.lineTo(size.width*0.9996583,size.height*0.1441000);
path0.lineTo(size.width,size.height);
path0.lineTo(0,size.height);
path0.lineTo(0,size.height*0.1422571);
path0.lineTo(size.width*0.3358333,size.height*0.1442857);
path0.quadraticBezierTo(size.width*0.4136083,size.height*0.2398857,size.width*0.4995083,size.height*0.2401000);
path0.close();
canvas.drawPath(path0, paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}

CustomPainter together with Paint()..blendMode gives weird blinking effect while scrolling

After launching the application, the green circle is not visible. In order for the circle to appear, you need to scroll up. Then if you scroll down, the circle starts blinking as long as the scrolloffset is within about 20 pixels. If the scrolloffset is larger than 20 pixels, the circle disappears and appears only when the scrolloffset is 0.
If wrap the widget in RepaintBoundary, then the circle appears on any scroll and then does not disappear.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class DemoPainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
canvas.save();
final path = Path()..addOval(Rect.fromCircle(center: Offset.zero, radius: 50));
final paint = Paint()..color = Colors.green;
paint.blendMode = BlendMode.dstOver;
canvas.drawPath(path, paint);
canvas.restore();
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
class TestWidget extends StatelessWidget {
const TestWidget({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return SizedBox(
height: 80,
child: Center(
child: CustomPaint(
painter: DemoPainter(),
child: Text(
"Child widget",
style: TextStyle(color: Colors.blue[200], fontWeight: FontWeight.bold, fontSize: 20),
),
),
));
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(primarySwatch: Colors.blue),
home: Scaffold(
body: SingleChildScrollView(
child: Column(
children: [
const TestWidget(),
const TestWidget(),
const TestWidget(),
const TestWidget(),
const RepaintBoundary(child: TestWidget()),
Container(
height: 2000,
color: Colors.red,
)
],
),
),
),
);
}
}
The link to Dartpad, but i can't reproduce the effect on the web platform.
What could be the reason for such effect and how to get rid of it?

Transparent background behind ClipPath

So, I'm pretty new to flutter, I'm trying to have a AppBar that looks like this :
And I'm actually able to do it using ClipPath like this :
class Header extends StatelessWidget {
final String page;
const Header({required this.page});
#override
Widget build(BuildContext context) {
final double topPadding = MediaQuery.of(context).padding.top;
return ClipPath(
child: HeaderContent(page: page, padding: topPadding),
clipper: Clipper(page: page));
}
}
class HeaderContent extends StatelessWidget {
final String page;
final double padding;
const HeaderContent({required this.page, required this.padding});
#override
Widget build(BuildContext context) {
final int headerHeight = page == "inscription" ? 250 : 150;
return Container(
height: headerHeight + padding,
padding: EdgeInsets.all(15),
width: double.infinity,
color: Theme.of(context).primaryColor,
child: AppBar(
title: Text("NOTIFICATIONS", style: Theme.of(context).textTheme.headline6),
centerTitle: true,
leading: Icon(
Icons.arrow_back,
size: 40,
),
),
);
}
}
class Clipper extends CustomClipper<Path> {
final String page;
const Clipper({required this.page});
#override
Path getClip(Size size) {
final Path path = Path();
selectHeaderClip(size, page, path);
return path;
}
#override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}
Where the selectHeaderClip is a custom function that return the appropriate clip for a given page.
However, when I have a scrollable page (list of notifications for example), I see the notifications hiding too early as if there was a white rectangular container underneath my appBar. So my question basically is: how can I make that container transparent ?
Ps: sorry for the bad formulation of the question, don't hesitate to edit.

Pass a class as method parameter in Dart

Suppose this class:
class SimpleRectPaint extends Object {
static CustomPaint build( ParamsBundle paramBundle ) {
return CustomPaint(
painter: SimpleRectPainter( paramBundle: paramBundle ),
child: Container(width: paramBundle.width, height: paramBundle.height)
);
}
}
I'd like to abstract the class SimpleRectPaint by means of a ClassReference parameter someClass and make the method more generic. Unfortunately, this isn't valid code:
class SimpleRectPaint extends Object {
static CustomPaint build( ParamsBundle paramBundle, ClassReference someClass ) {
return CustomPaint(
painter: someClass( paramBundle: paramBundle ),
child: Container(width: paramBundle.width, height: paramBundle.height)
);
}
}
Q: How do I have to write it instead?
You could do that by passing a CustomPainterCreator:
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
title: 'Generic Painter',
home: Scaffold(
body: MyWidget(),
),
),
);
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: SizedBox(
width: 200,
height: 200,
child: Stack(
fit: StackFit.expand,
children: [
Background(),
MyPainter(creator: RectPainter.creator, color: Color(0xFF104C91)),
MyPainter(creator: OvalPainter.creator, color: Color(0xFF1F8AC0)),
],
),
),
);
}
}
class Background extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Color(0xFFEFC9AF),
border: Border.all(width: 3.0),
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
);
}
}
typedef CustomPainterCreator(Color color);
class MyPainter extends StatelessWidget {
final CustomPainterCreator creator;
final Color color;
const MyPainter({
Key key,
this.creator,
this.color,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return CustomPaint(
painter: creator(color),
);
}
}
class OvalPainter extends CustomPainter {
static CustomPainterCreator creator = (color) => OvalPainter(color: color);
final Color color;
OvalPainter({this.color = Colors.green});
#override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = color;
canvas.drawOval(
Rect.fromLTWH(size.width * .4, size.height * .15, size.width * .5,
size.height * .5),
paint,
);
}
#override
bool shouldRepaint(OvalPainter oldDelegate) => false;
}
class RectPainter extends CustomPainter {
static CustomPainterCreator creator = (color) => RectPainter(color: color);
final Color color;
RectPainter({this.color = Colors.indigo});
#override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = color;
canvas.drawRRect(
RRect.fromRectAndRadius(
Rect.fromLTWH(size.width * .15, size.height * .25, size.width * .6,
size.height * .6),
Radius.circular(20)),
paint,
);
}
#override
bool shouldRepaint(RectPainter oldDelegate) => false;
}
But, though the exercise is interesting... Why?
Instead of:
MyPainter(creator: RectPainter.creator, color: Color(0xFF104C91)),
You can just do:
CustomPaint(painter: RectPainter(color: Color(0xFF104C91))),
If, not, what is the specific needs that would require more abstraction?

How to get offset of canvas in CustomPainter's paint function?

lets say that we have a background image:
I want to render few views with shared background:
I think that I can use CustomPainter and draw my background image translated by canvas offset, but I don't know how to get that property inside paint function:
class PanelBackgroundPainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
var rect = Offset.zero & size;
canvas.clipRect(rect);
canvas.drawImage(image, new Offset(-canvasOffsetX, -canvasOffsetY), new Paint());
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
How can I calculate canvasOffsetX and canvasOffsetY?
I am using rows and columns to lay out my panels:
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
home: new Column(
children: <Widget>[
new Expanded(
child: new Container(
color: new Color.fromARGB(255, 128, 128, 128),
),
flex: 100,
),
new Container(height: 10.0),
new Container(
color: new Color.fromARGB(255, 128, 128, 128),
height: 120.0,
),
new Container(height: 10.0),
new Expanded(
child: new Container(
child: new Row(
children: <Widget>[
new Expanded(
child: new Container(
color: new Color.fromARGB(255, 128, 128, 128),
),
flex: 50,
),
new Container(width: 10.0),
new Expanded(
child: new Container(
color: new Color.fromARGB(255, 128, 128, 128),
),
flex: 50,
),
],
),
),
flex: 200,
),
],
),
);
}
}
While I don't know that it is necessarily a great idea, it is possible. The best explanation is probably just to look at this code:
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
class SharedBackgroundPainter extends CustomPainter {
final ui.Image image;
final RenderBox renderBox;
final RenderBox parentRender;
SharedBackgroundPainter({#required this.image, #required this.renderBox, #required this.parentRender});
#override
void paint(Canvas canvas, Size size) {
var rect = ui.Offset.zero & size;
var globalThisTopLeft = renderBox.localToGlobal(new ui.Offset(0.0, 0.0), ancestor: parentRender);
canvas.clipRect(rect);
canvas.drawImage(image, new Offset(-globalThisTopLeft.dx, -globalThisTopLeft.dy), new Paint());
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
class BackgroundBuilder extends StatelessWidget {
final Size size;
final ui.Image image;
final RenderBox parentRender;
const BackgroundBuilder({Key key, #required this.size, #required this.image, #required this.parentRender})
: super(key: key);
#override
Widget build(BuildContext context) {
return new LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) {
RenderBox box = context.findRenderObject();
assert(box != null);
return new CustomPaint(
size: new Size(constraints.maxWidth, constraints.maxHeight),
painter: new SharedBackgroundPainter(image: image, renderBox: box, parentRender: parentRender),
);
});
}
}
class SharedBackgroundBuilder extends StatelessWidget {
final ui.Image toDraw;
final Size size;
final RenderBox parentRender;
const SharedBackgroundBuilder({Key key, #required this.toDraw, #required this.size, #required this.parentRender})
: super(key: key);
#override
Widget build(BuildContext context) {
return new Column(
children: <Widget>[
new Expanded(
child: new BackgroundBuilder(
image: toDraw,
size: size,
parentRender: parentRender,
),
flex: 100,
),
new SizedBox(height: 10.0),
new SizedBox(
height: 120.0,
child: new BackgroundBuilder(
image: toDraw,
size: size,
parentRender: parentRender,
),
),
new SizedBox(height: 10.0),
new Expanded(
child: new Container(
child: new Row(
children: <Widget>[
new Expanded(
child: new BackgroundBuilder(
image: toDraw,
size: size,
parentRender: parentRender,
),
flex: 50,
),
new Container(width: 10.0),
new Expanded(
child: new BackgroundBuilder(
image: toDraw,
size: size,
parentRender: parentRender,
),
flex: 50,
),
],
),
),
flex: 200,
),
],
);
}
}
/// This is to resize an image to the size of the overall structure. Note that this will
/// only work in an environment where LayoutBuilder can find constraints.
class ImageResizingPainter extends CustomPainter {
final ui.Image image;
final BoxFit boxfit;
ImageResizingPainter(this.image, this.boxfit);
#override
void paint(ui.Canvas canvas, ui.Size size) {
final ui.Rect rect = ui.Offset.zero & size;
final Size imageSize = new Size(image.width.toDouble(), image.height.toDouble());
FittedSizes sizes = applyBoxFit(boxfit, imageSize, size);
// if you don't want it centered for some reason change this.
final Rect inputSubrect = Alignment.center.inscribe(sizes.source, Offset.zero & imageSize);
final Rect outputSubrect = Alignment.center.inscribe(sizes.destination, rect);
canvas.drawImageRect(image, inputSubrect, outputSubrect, new Paint());
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
class ImageResizer extends StatelessWidget {
final ui.Image image;
final BoxFit boxFit;
// if you want a different boxfit you can set it. See BoxFit documentation.
const ImageResizer({Key key, #required this.image, this.boxFit = BoxFit.cover}) : super(key: key);
ui.Image getResizedImage(Size size) {
var pictureRecorder = new ui.PictureRecorder();
Canvas canvas = new Canvas(pictureRecorder);
CustomPainter painter = new ImageResizingPainter(image, boxFit);
painter.paint(canvas, size);
return pictureRecorder.endRecording().toImage(size.width.floor(), size.height.floor());
}
Widget build(BuildContext context) {
return new LayoutBuilder(builder: (context, constraints) {
RenderBox box = context.findRenderObject();
var size = new Size(constraints.maxWidth, constraints.maxHeight);
// This might not be a good idea to do in the build function, but we're going to ignore that
// and do it anyways.
ui.Image resizedImage = getResizedImage(size);
return new SharedBackgroundBuilder(toDraw: resizedImage, size: size, parentRender: box);
});
}
}
class ImageDrawer extends CustomPainter {
final ui.Image image;
ImageDrawer(this.image);
#override
void paint(ui.Canvas canvas, ui.Size size) {
canvas.drawImage(image, ui.Offset.zero, new Paint());
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
/// This is just to build an image to display. I'm too lazy to load one =D
class GradientPainter extends CustomPainter {
#override
void paint(ui.Canvas canvas, ui.Size size) {
Rect rect = ui.Offset.zero & size;
var linearGradient = new LinearGradient(
colors: [Colors.red, Colors.green],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
);
var shader = linearGradient.createShader(rect);
canvas.drawRect(rect, new Paint()..shader = shader);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
class GradientGetter extends StatelessWidget {
// I'm too lazy to create an image so I'm just drawing a gradient
ui.Image getGradientImage(Size size) {
var pictureRecorder = new ui.PictureRecorder();
Canvas canvas = new Canvas(pictureRecorder);
GradientPainter painter = new GradientPainter();
painter.paint(canvas, size);
return pictureRecorder.endRecording().toImage(size.width.floor(), size.height.floor());
}
#override
Widget build(BuildContext context) {
return new LayoutBuilder(builder: (context, constraints) {
var size = new Size(constraints.maxWidth, constraints.maxHeight);
// remove the / 20 to get it smooth again, it's just there to test
// out the scaling.
var changedSize = size / 20.0;
ui.Image gradientImage = getGradientImage(changedSize);
assert(gradientImage != null);
return new ImageResizer(
image: gradientImage,
);
});
}
}
/// This is just so that you can copy/paste this and have it run.
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() => MyAppState();
}
class MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
home: new Padding(
padding: const EdgeInsets.all(30.0),
child: new GradientGetter(),
));
}
}
As I went through it I realized that it needs to be more complex than I originally thought - you have to deal with scaling the image to the size of the container etc. This does all that now, although I haven't tested it super extensively.
Also note that this gives no consideration to performance - it's almost for sure bad performance-wise.