how to use SvgPicture.string as a imageProvider flutter - flutter

im using flutter_svg package for svg. and now i want to use a svg inside a container as decoration like this,
Container(
decoration: BoxDecoration(
image: DecorationImage(
image: SvgPicture.string(
'''<svg viewBox="...">...</svg>'''
),
),
),
)
but the problem is DecorationImage peram expecting 'ImageProvider'. how can i do this ?
i tried flutter_svg_provider but its also not working. i found this solution, but dont know how to use.

use a custom Decoration like this:
class SvgDecoration extends Decoration {
SvgDecoration.string(String rawSvg, {this.key})
: rawSvgFuture = Future.value(rawSvg);
SvgDecoration.file(File file, {this.key})
: rawSvgFuture = file.readAsString();
SvgDecoration.asset(String asset, {this.key})
: rawSvgFuture = rootBundle.loadString(asset);
final Future<String> rawSvgFuture;
final String? key;
#override
BoxPainter createBoxPainter([ui.VoidCallback? onChanged]) {
return _SvgDecorationPainter(rawSvgFuture, onChanged, key);
}
}
class _SvgDecorationPainter extends BoxPainter {
_SvgDecorationPainter(this.rawSvgFuture, ui.VoidCallback? onChanged, String? key) {
rawSvgFuture
.then((rawSvg) => svg.fromSvgString(rawSvg, key ?? '(no key)'))
.then((d) {
drawable = d;
onChanged?.call();
});
}
final Future<String> rawSvgFuture;
DrawableRoot? drawable;
#override
void paint(ui.Canvas canvas, ui.Offset offset, ImageConfiguration configuration) {
if (drawable != null) {
canvas
..save()
..translate(offset.dx, offset.dy);
drawable!
..scaleCanvasToViewBox(canvas, configuration.size!)
..draw(canvas, offset & configuration.size!);
canvas.restore();
}
}
}
as you can see there are 3 constructors: SvgDecoration.string, SvgDecoration.file and SvgDecoration.asset but of course you can add some other custom constructors (like SvgDecoration.network for example)

The SvgPicture is a Widget, not an Image, which is why it can not be used as DecorationImage here. The way you can use the SvgPicture behind your Container is a Stack:
Stack(
children: [
SvgPicture.string(
'''<svg viewBox="...">...</svg>''',
(... width, height etc.)
),
Container(
child: (..., foreground widget)
),
],
)
Obviously, you have to make sure that both have the same size if you need it. But that depends on your usecase.

Related

How can I change color with GestureDetector of svg image and Flutter?

I need your help, I'am developing an app where the user will select any SVG image previously converted with flutterShapeMaker to get and set several classes as you see below (fragment example, I have 20 classes)
class PathUno extends CustomPainter{
final Color color;
PathUno(this.color);
#override
void paint(Canvas canvas, Size size) {
Path path_1 = Path();
path_1.moveTo(size.width*0.7138889,size.height*0.3569444);
path_1.cubicTo(size.width*0.7259804,size.height*0.3599747,size.width*0.7367647,size.height*0.3650253,size.width*0.7468954,size.height*0.3705808);
path_1.cubicTo(size.width*0.7586601,size.height*0.3771465,size.width*0.7694444,size.height*0.3844697,size.width*0.7761438,size.height*0.3949495);
path_1.cubicTo(size.width*0.7823529,size.height*0.4047980,size.width*0.7794118,size.height*0.4148990,size.width*0.7638889,size.height*0.4190657);
path_1.cubicTo(size.width*0.7485294,size.height*0.4231061,size.width*0.7339869,size.height*0.4217172,size.width*0.7187908,size.height*0.4193182);
path_1.cubicTo(size.width*0.7040850,size.height*0.4169192,size.width*0.6892157,size.height*0.4148990,size.width*0.6750000,size.height*0.4111111);
path_1.cubicTo(size.width*0.6684641,size.height*0.4093434,size.width*0.6620915,size.height*0.4073232,size.width*0.6552288,size.height*0.4061869);
path_1.cubicTo(size.width*0.6517974,size.height*0.4055556,size.width*0.6500000,size.height*0.4053030,size.width*0.6517974,size.height*0.4088384);
path_1.cubicTo(size.width*0.6647059,size.height*0.4327020,size.width*0.6648693,size.height*0.4574495,size.width*0.6612745,size.height*0.4825758);
path_1.cubicTo(size.width*0.6583333,size.height*0.5037879,size.width*0.6514706,size.height*0.5243687,size.width*0.6449346,size.height*0.5450758);
path_1.cubicTo(size.width*0.6436275,size.height*0.5491162,size.width*0.6431373,size.height*0.5532828,size.width*0.6421569,size.height*0.5574495);
path_1.cubicTo(size.width*0.6415033,size.height*0.5599747,size.width*0.6424837,size.height*0.5606061,size.width*0.6457516,size.height*0.5606061);
path_1.cubicTo(size.width*0.6566993,size.height*0.5606061,size.width*0.6648693,size.height*0.5648990,size.width*0.6709150,size.height*0.5715909);
path_1.cubicTo(size.width*0.6844771,size.height*0.5866162,size.width*0.6910131,size.height*0.6037879,size.width*0.6968954,size.height*0.6212121);
path_1.cubicTo(size.width*0.7052288,size.height*0.6462121,size.width*0.7102941,size.height*0.6715909,size.width*0.7142157,size.height*0.6972222);
path_1.cubicTo(size.width*0.7200980,size.height*0.7352273,size.width*0.7227124,size.height*0.7733586,size.width*0.7248366,size.height*0.8114899);
path_1.cubicTo(size.width*0.5620915,size.height*0.8372475,size.width*0.5602941,size.height*0.8338384,size.width*0.5599673,size.height*0.8303030);
path_1.cubicTo(size.width*0.5596405,size.height*0.8271465,size.width*0.5578431,size.height*0.8270202,size.width*0.5545752,size.height*0.8270202);
path_1.cubicTo(size.width*0.5460784,size.height*0.8271465,size.width*0.5377451,size.height*0.8271465,size.width*0.5292484,size.height*0.8271465);
path_1.cubicTo(size.width*0.5276144,size.height*0.8271465,size.width*0.5259804,size.height*0.8271465,size.width*0.5245098,size.height*0.8268939);
path_1.cubicTo(size.width*0.5163399,size.height*0.8252525,size.width*0.5160131,size.height*0.8251263,size.width*0.5147059,size.height*0.8316919);
path_1.cubicTo(size.width*0.5114379,size.height*0.8479798,size.width*0.4944444,size.height*0.8568182,size.width*0.4725490,size.height*0.8595960);
path_1.cubicTo(size.width*0.4442810,size.height*0.8631313,size.width*0.4156863,size.height*0.8623737,size.width*0.3870915,size.height*0.8607323);
path_1.cubicTo(size.width*0.3813725,size.height*0.8603535,size.width*0.3756536,size.height*0.8597222,size.width*0.3699346,size.height*0.8584596);
path_1.cubicTo(size.width*0.3537582,size.height*0.8547980,size.width*0.3470588,size.height*0.8459596,size.width*0.3511438,size.height*0.8343434);
path_1.cubicTo(size.width*0.6258170,size.height*0.4420455,size.width*0.6251634,size.height*0.4385101,size.width*0.6210784,size.height*0.4417929);
path_1.cubicTo(size.width*0.6168301,size.height*0.4426768,size.width*0.6125817,size.height*0.4435606,size.width*0.6083333,size.height*0.4444444);
path_1.cubicTo(size.width*0.6075163,size.height*0.4454545,size.width*0.6060458,size.height*0.4464646,size.width*0.6058824,size.height*0.4474747);
path_1.cubicTo(size.width*0.6055556,size.height*0.4508838,size.width*0.6042484,size.height*0.4522727,size.width*0.6000000,size.height*0.4502525);
path_1.cubicTo(size.width*0.5967320,size.height*0.4494949,size.width*0.5982026,size.height*0.4453283,size.width*0.5931373,size.height*0.4453283);
path_1.cubicTo(size.width*0.5754902,size.height*0.4452020,size.width*0.5584967,size.height*0.4428030,size.width*0.5418301,size.height*0.4375000);
path_1.cubicTo(size.width*0.5294118,size.height*0.4335859,size.width*0.5174837,size.height*0.4291667,size.width*0.5058824,size.height*0.4238636);
path_1.cubicTo(size.width*0.5029412,size.height*0.4226010,size.width*0.5001634,size.height*0.4210859,size.width*0.4973856,size.height*0.4196970);
path_1.cubicTo(size.width*0.4913399,size.height*0.4165404,size.width*0.4859477,size.height*0.4125000,size.width*0.4787582,size.height*0.4106061);
path_1.cubicTo(size.width*0.4805556,size.height*0.4078283,size.width*0.4846405,size.height*0.4075758,size.width*0.4875817,size.height*0.4063131);
path_1.cubicTo(size.width*0.4995098,size.height*0.4178030,size.width*0.5339869,size.height*0.4214646,size.width*0.5500000,size.height*0.4135101);
path_1.cubicTo(size.width*0.5580065,size.height*0.4094697,size.width*0.5637255,size.height*0.4003788,size.width*0.5560458,size.height*0.3925505);
path_1.cubicTo(size.width*0.5537582,size.height*0.3902778,size.width*0.5508170,size.height*0.3883838,size.width*0.5488562,size.height*0.3853535);
path_1.cubicTo(size.width*0.5503268,size.height*0.3852273,size.width*0.5511438,size.height*0.3849747,size.width*0.5521242,size.height*0.3849747);
path_1.cubicTo(size.width*0.5683007,size.height*0.3848485,size.width*0.5843137,size.height*0.3842172,size.width*0.6003268,size.height*0.3813131);
path_1.cubicTo(size.width*0.6169935,size.height*0.3782828,size.width*0.6333333,size.height*0.3750000,size.width*0.6477124,size.height*0.3672980);
path_1.cubicTo(size.width*0.6532680,size.height*0.3642677,size.width*0.6576797,size.height*0.3604798,size.width*0.6609477,size.height*0.3560606);
path_1.cubicTo(size.width*0.6650327,size.height*0.3506313,size.width*0.6635621,size.height*0.3434343,size.width*0.6570261,size.height*0.3402778);
path_1.cubicTo(size.width*0.6452614,size.height*0.3344697,size.width*0.6316993,size.height*0.3319444,size.width*0.6181373,size.height*0.3339646);
path_1.cubicTo(size.width*0.6083333,size.height*0.3353535,size.width*0.5985294,size.height*0.3363636,size.width*0.5887255,size.height*0.3377525);
path_1.cubicTo(size.width*0.5885621,size.height*0.3366162,size.width*0.5895425,size.height*0.3359848,size.width*0.5905229,size.height*0.3353535);
path_1.cubicTo(size.width*0.5919935,size.height*0.3345960,size.width*0.5933007,size.height*0.3337121,size.width*0.5949346,size.height*0.3330808);
path_1.cubicTo(size.width*0.6163399,size.height*0.3246212,size.width*0.6362745,size.height*0.3143939,size.width*0.6547386,size.height*0.3023990);
path_1.cubicTo(size.width*0.6637255,size.height*0.2965909,size.width*0.6718954,size.height*0.2904040,size.width*0.6774510,size.height*0.2823232);
path_1.cubicTo(size.width*0.6833333,size.height*0.2736111,size.width*0.6800654,size.height*0.2698232,size.width*0.6718954,size.height*0.2671717);
path_1.cubicTo(size.width*0.7246732,size.height*0.2135101,size.width*0.7400327,size.height*0.2083333,size.width*0.7532680,size.height*0.2001263);
path_1.cubicTo(size.width*0.7563725,size.height*0.1982323,size.width*0.7609477,size.height*0.1964646,size.width*0.7599673,size.height*0.1925505);
path_1.cubicTo(size.width*0.7589869,size.height*0.1886364,size.width*0.7539216,size.height*0.1878788,size.width*0.7498366,size.height*0.1867424);
path_1.cubicTo(size.width*0.7486928,size.height*0.1864899,size.width*0.7475490,size.height*0.1862374,size.width*0.7464052,size.height*0.1861111);
path_1.cubicTo(size.width*0.7354575,size.height*0.1848485,size.width*0.7246732,size.height*0.1842172,size.width*0.7135621,size.height*0.1849747);
path_1.cubicTo(size.width*0.6928105,size.height*0.1863636,size.width*0.6723856,size.height*0.1881313,size.width*0.6524510,size.height*0.1934343);
path_1.cubicTo(size.width*0.6364379,size.height*0.1976010,size.width*0.6204248,size.height*0.2012626,size.width*0.6057190,size.height*0.2075758);
path_1.cubicTo(size.width*0.6037582,size.height*0.2061869,size.width*0.6032680,size.height*0.2042929,size.width*0.6032680,size.height*0.2022727);
path_1.cubicTo(size.width*0.6122549,size.height*0.1993687,size.width*0.6215686,size.height*0.1968434,size.width*0.6303922,size.height*0.1936869);
path_1.cubicTo(size.width*0.6433007,size.height*0.1888889,size.width*0.6575163,size.height*0.1877525,size.width*0.6709150,size.height*0.1845960);
path_1.cubicTo(size.width*0.6901961,size.height*0.1800505,size.width*0.7098039,size.height*0.1791667,size.width*0.7295752,size.height*0.1789141);
path_1.cubicTo(size.width*0.7364379,size.height*0.1787879,size.width*0.7431373,size.height*0.1800505,size.width*0.7498366,size.height*0.1809343);
path_1.cubicTo(size.width*0.7557190,size.height*0.1816919,size.width*0.7602941,size.height*0.1847222,size.width*0.7640523,size.height*0.1880051);
path_1.cubicTo(size.width*0.7673203,size.height*0.1907828,size.width*0.7674837,size.height*0.1943182,size.width*0.7648693,size.height*0.1976010);
path_1.cubicTo(size.width*0.7622549,size.height*0.2007576,size.width*0.7593137,size.height*0.2040404,size.width*0.7549020,size.height*0.2060606);
path_1.cubicTo(size.width*0.7459150,size.height*0.2101010,size.width*0.7374183,size.height*0.2151515,size.width*0.7276144,size.height*0.2180556);
path_1.cubicTo(size.width*0.7263072,size.height*0.2184343,size.width*0.7245098,size.height*0.2186869,size.width*0.7251634,size.height*0.2202020);
path_1.cubicTo(size.width*0.7258170,size.height*0.2215909,size.width*0.7276144,size.height*0.2208333,size.width*0.7287582,size.height*0.2205808);
path_1.cubicTo(size.width*0.7455882,size.height*0.2176768,size.width*0.7627451,size.height*0.2156566,size.width*0.7799020,size.height*0.2156566);
path_1.cubicTo(size.width*0.7947712,size.height*0.2155303,size.width*0.8096405,size.height*0.2161616,size.width*0.8236928,size.height*0.2203283);
path_1.cubicTo(size.width*0.8387255,size.height*0.2248737,size.width*0.8434641,size.height*0.2315657,size.width*0.8406863,size.height*0.2440657);
path_1.cubicTo(size.width*0.8406863,size.height*0.2443182,size.width*0.8408497,size.height*0.2446970,size.width*0.8408497,size.height*0.2449495);
path_1.cubicTo(size.width*0.8385621,size.height*0.2455808,size.width*0.8377451,size.height*0.2470960,size.width*0.8374183,size.height*0.2487374);
path_1.cubicTo(size.width*0.8297386,size.height*0.2550505,size.width*0.8214052,size.height*0.2604798,size.width*0.8112745,size.height*0.2643939);
path_1.cubicTo(size.width*0.8076797,size.height*0.2657828,size.width*0.8034314,size.height*0.2665404,size.width*0.8001634,size.height*0.2683081);
path_1.cubicTo(size.width*0.7888889,size.height*0.2744949,size.width*0.7750000,size.height*0.2760101,size.width*0.7629085,size.height*0.2805556);
path_1.cubicTo(size.width*0.7580065,size.height*0.2824495,size.width*0.7526144,size.height*0.2835859,size.width*0.7475490,size.height*0.2849747);
path_1.cubicTo(size.width*0.7620915,size.height*0.2872475,size.width*0.7767974,size.height*0.2839646,size.width*0.7906863,size.height*0.2888889);
path_1.cubicTo(size.width*0.7954248,size.height*0.2905303,size.width*0.8011438,size.height*0.2902778,size.width*0.8062092,size.height*0.2912879);
path_1.cubicTo(size.width*0.8155229,size.height*0.2931818,size.width*0.8246732,size.height*0.2958333,size.width*0.8333333,size.height*0.2992424);
path_1.cubicTo(size.width*0.8418301,size.height*0.3025253,size.width*0.8501634,size.height*0.3060606,size.width*0.8560458,size.height*0.3118687);
path_1.cubicTo(size.width*0.8627451,size.height*0.3184343,size.width*0.8647059,size.height*0.3267677,size.width*0.8517974,size.height*0.3323232);
path_1.cubicTo(size.width*0.8408497,size.height*0.3369949,size.width*0.8292484,size.height*0.3391414,size.width*0.8169935,size.height*0.3401515);
path_1.cubicTo(size.width*0.8050654,size.height*0.3411616,size.width*0.7933007,size.height*0.3397727,size.width*0.7812092,size.height*0.3416667);
path_1.cubicTo(size.width*0.7689542,size.height*0.3435606,size.width*0.7560458,size.height*0.3421717,size.width*0.7433007,size.height*0.3441919);
path_1.cubicTo(size.width*0.7300654,size.height*0.3462121,size.width*0.7165033,size.height*0.3467172,size.width*0.7035948,size.height*0.3503788);
path_1.cubicTo(size.width*0.7024510,size.height*0.3507576,size.width*0.7001634,size.height*0.3501263,size.width*0.7003268,size.height*0.3516414);
path_1.cubicTo(size.width*0.7004902,size.height*0.3534091,size.width*0.7027778,size.height*0.3534091,size.width*0.7045752,size.height*0.3532828);
path_1.cubicTo(size.width*0.7066993,size.height*0.3563131,size.width*0.7114379,size.height*0.3549242,size.width*0.7138889,size.height*0.3569444);
path_1.close();
path_1.moveTo(size.width*0.6977124,size.height*0.3010101);
path_1.cubicTo(size.width*0.7045752,size.height*0.2939394,size.width*0.7142157,size.height*0.2902778,size.width*0.7232026,size.height*0.2863636);
path_1.cubicTo(size.width*0.7320261,size.height*0.2825758,size.width*0.7421569,size.height*0.2806818,size.width*0.7516340,size.height*0.2775253);
path_1.cubicTo(size.width*0.7640523,size.height*0.2733586,size.width*0.7772876,size.height*0.2708333,size.width*0.7898693,size.height*0.2665404);
path_1.cubicTo(size.width*0.8013072,size.height*0.2625000,size.width*0.8119281,size.height*0.2578283,size.width*0.8223856,size.height*0.2525253);
path_1.cubicTo(size.width*0.8269608,size.height*0.2502525,size.width*0.8303922,size.height*0.2468434,size.width*0.8333333,size.height*0.2434343);
path_1.cubicTo(size.width*0.8380719,size.height*0.2381313,size.width*0.8361111,size.height*0.2314394,size.width*0.8289216,size.height*0.2281566);
path_1.cubicTo(size.width*0.8194444,size.height*0.2238636,size.width*0.8088235,size.height*0.2222222,size.width*0.7982026,size.height*0.2208333);
path_1.cubicTo(size.width*0.7794118,size.height*0.2183081,size.width*0.7607843,size.height*0.2213384,size.width*0.7423203,size.height*0.2239899);
path_1.cubicTo(size.width*0.7192810,size.height*0.2272727,size.width*0.6978758,size.height*0.2335859,size.width*0.6771242,size.height*0.2419192);
path_1.cubicTo(size.width*0.6656863,size.height*0.2464646,size.width*0.6550654,size.height*0.2518939,size.width*0.6441176,size.height*0.2571970);
path_1.cubicTo(size.width*0.6413399,size.height*0.2584596,size.width*0.6395425,size.height*0.2604798,size.width*0.6369281,size.height*0.2622475);
path_1.cubicTo(size.width*0.6390523,size.height*0.2631313,size.width*0.6405229,size.height*0.2627525,size.width*0.6419935,size.height*0.2625000);
path_1.cubicTo(size.width*0.6498366,size.height*0.2609848,size.width*0.6578431,size.height*0.2607323,size.width*0.6660131,size.height*0.2611111);
path_1.cubicTo(size.width*0.6805556,size.height*0.2618687,size.width*0.6887255,size.height*0.2689394,size.width*0.6862745,size.height*0.2789141);
path_1.cubicTo(size.width*0.6851307,size.height*0.2834596,size.width*0.6816993,size.height*0.2875000,size.width*0.6782680,size.height*0.2912879);
path_1.cubicTo(size.width*0.6656863,size.height*0.3044192,size.width*0.6490196,size.height*0.3140152,size.width*0.6313725,size.height*0.3227273);
path_1.cubicTo(size.width*0.6279412,size.height*0.3243687,size.width*0.6253268,size.height*0.3263889,size.width*0.6212418,size.height*0.3289141);
path_1.cubicTo(size.width*0.6295752,size.height*0.3292929,size.width*0.6369281,size.height*0.3290404,size.width*0.6442810,size.height*0.3301768);
path_1.cubicTo(size.width*0.6598039,size.height*0.3325758,size.width*0.6715686,size.height*0.3412879,size.width*0.6696078,size.height*0.3521465);
path_1.cubicTo(size.width*0.6684641,size.height*0.3582071,size.width*0.6643791,size.height*0.3630051,size.width*0.6593137,size.height*0.3670455);
path_1.cubicTo(size.width*0.6478758,size.height*0.3758838,size.width*0.6331699,size.height*0.3806818,size.width*0.6181373,size.height*0.3842172);
path_1.cubicTo(size.width*0.6022876,size.height*0.3880051,size.width*0.5859477,size.height*0.3895202,size.width*0.5694444,size.height*0.3898990);
path_1.cubicTo(size.width*0.5648693,size.height*0.3900253,size.width*0.5622549,size.height*0.3904040,size.width*0.5650327,size.height*0.3946970);
path_1.cubicTo(size.width*0.5679739,size.height*0.3992424,size.width*0.5673203,size.height*0.4040404,size.width*0.5645425,size.height*0.4087121);
path_1.cubicTo(size.width*0.5593137,size.height*0.4172980,size.width*0.5498366,size.height*0.4215909,size.width*0.5385621,size.height*0.4234848);
path_1.cubicTo(size.width*0.5336601,size.height*0.4243687,size.width*0.5285948,size.height*0.4243687,size.width*0.5230392,size.height*0.4248737);
path_1.cubicTo(size.width*0.5413399,size.height*0.4340909,size.width*0.5617647,size.height*0.4373737,size.width*0.5820261,size.height*0.4402778);
path_1.cubicTo(size.width*0.5923203,size.height*0.4417929,size.width*0.6029412,size.height*0.4409091,size.width*0.6133987,size.height*0.4382576);
path_1.cubicTo(size.width*0.6297386,size.height*0.4339646,size.width*0.6348039,size.height*0.4250000,size.width*0.6264706,size.height*0.4133838);
path_1.cubicTo(size.width*0.6217320,size.height*0.4066919,size.width*0.6153595,size.height*0.4008838,size.width*0.6081699,size.height*0.3958333);
path_1.cubicTo(size.width*0.6065359,size.height*0.3946970,size.width*0.6040850,size.height*0.3938131,size.width*0.6042484,size.height*0.3916667);
path_1.cubicTo(size.width*0.6066993,size.height*0.3906566,size.width*0.6075163,size.height*0.3926768,size.width*0.6093137,size.height*0.3929293);
path_1.cubicTo(size.width*0.6274510,size.height*0.3953283,size.width*0.6454248,size.height*0.3983586,size.width*0.6629085,size.height*0.4031566);
path_1.cubicTo(size.width*0.6728758,size.height*0.4059343,size.width*0.6834967,size.height*0.4071970,size.width*0.6936275,size.height*0.4099747);
path_1.cubicTo(size.width*0.7142157,size.height*0.4155303,size.width*0.7351307,size.height*0.4179293,size.width*0.7566993,size.height*0.4161616);
path_1.cubicTo(size.width*0.7638889,size.height*0.4155303,size.width*0.7712418,size.height*0.4141414,size.width*0.7732026,size.height*0.4073232);
path_1.cubicTo(size.width*0.7745098,size.height*0.4029040,size.width*0.7722222,size.height*0.3991162,size.width*0.7696078,size.height*0.3954545);
path_1.cubicTo(size.width*0.7619281,size.height*0.3845960,size.width*0.7495098,size.height*0.3773990,size.width*0.7364379,size.height*0.3713384);
path_1.cubicTo(size.width*0.7210784,size.height*0.3641414,size.width*0.7044118,size.height*0.3584596,size.width*0.6864379,size.height*0.3560606);
path_1.cubicTo(size.width*0.6830065,size.height*0.3555556,size.width*0.6802288,size.height*0.3546717,size.width*0.6774510,size.height*0.3526515);
path_1.cubicTo(size.width*0.7006536,size.height*0.3452020,size.width*0.7240196,size.height*0.3396465,size.width*0.7488562,size.height*0.3380051);
path_1.cubicTo(size.width*0.7727124,size.height*0.3363636,size.width*0.7968954,size.height*0.3383838,size.width*0.8205882,size.height*0.3345960);
path_1.cubicTo(size.width*0.8303922,size.height*0.3329545,size.width*0.8405229,size.height*0.3324495,size.width*0.8495098,size.height*0.3286616);
path_1.cubicTo(size.width*0.8540850,size.height*0.3267677,size.width*0.8560458,size.height*0.3237374,size.width*0.8544118,size.height*0.3202020);
path_1.cubicTo(size.width*0.8532680,size.height*0.3176768,size.width*0.8513072,size.height*0.3154040,size.width*0.8483660,size.height*0.3135101);
path_1.cubicTo(size.width*0.8344771,size.height*0.3041667,size.width*0.8173203,size.height*0.3001263,size.width*0.8001634,size.height*0.2964646);
path_1.cubicTo(size.width*0.7808824,size.height*0.2924242,size.width*0.7609477,size.height*0.2914141,size.width*0.7410131,size.height*0.2934343);
path_1.cubicTo(size.width*0.7312092,size.height*0.2944444,size.width*0.7212418,size.height*0.2952020,size.width*0.7120915,size.height*0.2989899);
path_1.cubicTo(size.width*0.7076797,size.height*0.2998737,size.width*0.7031046,size.height*0.3012626,size.width*0.6977124,size.height*0.3010101);
path_1.close();
path_1.moveTo(size.width*0.5959150,size.height*0.6977273);
path_1.cubicTo(size.width*0.5967320,size.height*0.6873737,size.width*0.5952614,size.height*0.6747475,size.width*0.5929739,size.height*0.6623737);
path_1.cubicTo(size.width*0.5885621,size.height*0.6376263,size.width*0.5812092,size.height*0.6133838,size.width*0.5705882,size.height*0.5897727);
path_1.cubicTo(size.width*0.5645425,size.height*0.5762626,size.width*0.5568627,size.height*0.5632576,size.width*0.5503268,size.height*0.5500000);
path_1.cubicTo(size.width*0.5486928,size.height*0.5468434,size.width*0.5465686,size.height*0.5462121,size.width*0.5426471,size.height*0.5483586);
path_1.cubicTo(size.width*0.5351307,size.height*0.5523990,size.width*0.5271242,size.height*0.5560606,size.width*0.5179739,size.height*0.5575758);
path_1.cubicTo(size.width*0.5133987,size.height*0.5583333,size.width*0.5135621,size.height*0.5601010,size.width*0.5148693,size.height*0.5625000);
path_1.cubicTo(size.width*0.5223856,size.height*0.5762626,size.width*0.5284314,size.height*0.5902778,size.width*0.5334967,size.height*0.6046717);
path_1.cubicTo(size.width*0.5410131,size.height*0.6262626,size.width*0.5454248,size.height*0.6483586,size.width*0.5465686,size.height*0.6705808);
path_1.cubicTo(size.width*0.5473856,size.height*0.6875000,size.width*0.5462418,size.height*0.7042929,size.width*0.5433007,size.height*0.7212121);
path_1.cubicTo(size.width*0.5397059,size.height*0.7420455,size.width*0.5333333,size.height*0.7622475,size.width*0.5248366,size.height*0.7821970);
path_1.cubicTo(size.width*0.5232026,size.height*0.7858586,size.width*0.5207516,size.height*0.7893939,size.width*0.5204248,size.height*0.7933081);
path_1.cubicTo(size.width*0.5197712,size.height*0.8011364,size.width*0.5191176,size.height*0.8090909,size.width*0.5178105,size.height*0.8169192);
path_1.cubicTo(size.width*0.5174837,size.height*0.8193182,size.width*0.5186275,size.height*0.8204545,size.width*0.5220588,size.height*0.8208333);
path_1.cubicTo(size.width*0.5344771,size.height*0.8220960,size.width*0.5468954,size.height*0.8217172,size.width*0.5593137,size.height*0.8208333);
path_1.cubicTo(size.width*0.5620915,size.height*0.8207071,size.width*0.5632353,size.height*0.8194444,size.width*0.5640523,size.height*0.8178030);
path_1.cubicTo(size.width*0.5707516,size.height*0.8046717,size.width*0.5772876,size.height*0.7916667,size.width*0.5821895,size.height*0.7780303);
path_1.cubicTo(size.width*0.5913399,size.height*0.7523990,size.width*0.5960784,size.height*0.7265152,size.width*0.5959150,size.height*0.6977273);
path_1.close();
path_1.moveTo(size.width*0.5230392,size.height*0.7681818);
path_1.cubicTo(size.width*0.5240196,size.height*0.7665404,size.width*0.5251634,size.height*0.7654040,size.width*0.5256536,size.height*0.7641414);
path_1.cubicTo(size.width*0.5334967,size.height*0.7411616,size.width*0.5388889,size.height*0.7178030,size.width*0.5401961,size.height*0.6940657);
path_1.cubicTo(size.width*0.5416667,size.height*0.6688131,size.width*0.5383987,size.height*0.6439394,size.width*0.5312092,size.height*0.6191919);
path_1.cubicTo(size.width*0.5254902,size.height*0.5994949,size.width*0.5179739,size.height*0.5803030,size.width*0.5071895,size.height*0.5619949);
path_1.cubicTo(size.width*0.5063725,size.height*0.5606061,size.width*0.5060458,size.height*0.5594697,size.width*0.5035948,size.height*0.5597222);
path_1.cubicTo(size.width*0.4995098,size.height*0.5601010,size.width*0.4954248,size.height*0.5603535,size.width*0.4915033,size.height*0.5617424);
path_1.cubicTo(size.width*0.4885621,size.height*0.5627525,size.width*0.4882353,size.height*0.5637626,size.width*0.4893791,size.height*0.5659091);
path_1.cubicTo(size.width*0.4936275,size.height*0.5746212,size.width*0.4968954,size.height*0.5835859,size.width*0.5001634,size.height*0.5924242);
path_1.cubicTo(size.width*0.5124183,size.height*0.6253788,size.width*0.5179739,size.height*0.6592172,size.width*0.5209150,size.height*0.6933081);
path_1.cubicTo(size.width*0.5230392,size.height*0.7170455,size.width*0.5236928,size.height*0.7407828,size.width*0.5215686,size.height*0.7645202);
path_1.cubicTo(size.width*0.5214052,size.height*0.7656566,size.width*0.5214052,size.height*0.7669192,size.width*0.5230392,size.height*0.7681818);
path_1.close();
path_1.moveTo(size.width*0.6352941,size.height*0.2631313);
path_1.lineTo(size.width*0.6351307,size.height*0.2632576);
path_1.lineTo(size.width*0.6354575,size.height*0.2632576);
path_1.lineTo(size.width*0.6352941,size.height*0.2631313);
path_1.close();
Paint paint_1_fill = Paint()..style=PaintingStyle.fill;
paint_1_fill.color = color;
canvas.drawPath(path_1,paint_1_fill);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
everything is good, I send the color and it draws well,
return Stack(
children: <Widget>[
CustomPaint(
painter: PathUno(Color(0xff000000)),
child: SizedBox(
width: MediaQuery.of(context).size.width, height: 400)),
CustomPaint(
painter: PathDos(Color(0xffffffff)),
child: SizedBox(
width: MediaQuery.of(context).size.width, height: 400)),
],
);
but I need to know how can I set a GestureDetector in the right way, because when I put it, any part of the screen gave me that ontap, the idea is: when the user touch that class or path change color,
Thank you!
If you want the gesture to only react to gestures on the filled part of the custom paint, you need to override hitTest on your CustomPainter classes:
Path? _path;
#override
void paint(Canvas canvas, Size size) {
Path path_1 = Path();
//...
_path = path_1;
}
#override
bool? hitTest(Offset offset) {
return _path?.contains(offset) ?? false;
}
Here's a dartpad showing it in action. Also see this previous thread on the same question.

Call setState outside of a StatefulWidget (with a setter)

I have a lot of experience with Javascript and now I want to start with Flutter. So maybe my problem is that I see the code as JS developer and the Flutter solution would be different. But here is my problem:
I want to create a simple card game. A playing card is a StatefulWidget:
class GameCard extends StatefulWidget {
int _value;
String _suit;
bool _visible;
String _imagePath;
// void setVisible() {
// How to execute setState here??
// }
GameCard({int value, String suit, bool visible = false}) {
...
this._visible = visible;
this._value = value;
this._suit = suit;
}
#override
_GameCardState createState() => _GameCardState();
}
class _GameCardState extends State<GameCard> {
String getSuit() => widget._suit;
int getValue() => widget._value;
bool isVisible() => widget._visible;
#override
Widget build(BuildContext context) {
...
return ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Container(
width: width,
height: width * 1.5,
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: AssetImage(widget._imagePath),
...
The idea is that the imagePath in the state class should be a image of a card back when the card is not visible.
Now I want to have a setVisible function in my StatefulWidget which runs setState() followed by an update of the imagePath. After that imagePath should be a image of a playing card like for example hearts king.
So finally I want to create an instance of GameCard somewhere else and change the image with a setter:
GameCard spadeFour = GameCard(value: 4, suit: 'spade', visible: false),
spadeFour.setVisible(true)
How can I do that? I read about Globalkey and other stuff but it can't be that complicated cause this is a very common use case.
Generally you should avoid instantiating a widget and then modifying it through properties. This is also unnecessary since you already correctly use parameters to change the appearance of the widget.
To get the widget to redraw with different parameters, instead of calling setState within that widget, call it in the parent widget.
class _YourParentPageState extends State<YourParentPage> {
bool visible = false;
_changeVisibility(bool v) {
setState(() {
visible = v;
});
}
#override
Widget build(BuildContext context) {
return GameCard(value: 4, suit: 'spade', visible: visible);
}
}
Calling _changeVisibility will redraw your GameCard with the updated visible parameter.

Flutter ShaderMask using an ImageShader

I'm trying to get a ShaderMask that uses an Image as a Shader. I'm reading the image from memory, so my image is a File. How do I create an ImageShader using an image from memory?
File imageFile;
Image image = Image.file(imageFile)
ShaderMask(
shaderCallback: (bounds) {
Float64List matrix4 = new Matrix4.identity().storage; // <--- DO I NEED THIS OR THE BOUNDS?
return ImageShader(image, TileMode.mirror, TileMode.mirror, matrix4);
},
child: child
)
There's an error with ImageShader as image is the wrong type (I need ui.Image, which I don't understand how to create).
How do I create the ImageShader from a File image?
PS: is matrix4 correct or should I use the bounds somehow?
The important thing to know is that ImageShader uses an Image from the dart:ui package and not an instance of the Image widget. There is no direct operation or constructor available to create an instance of ui.Image from a network location, a file or asset, so you need some code to get it done.
The best generic solution i came up with after looking up many resources and digging into the code how the Image widget is loading raw images, is to use an ImageProvider as source. The abstract ImageProvider has implementations like NetworkImage, FileImage, ExactAssetImage and MemoryImage to load your image from any resource you want.
First you get an ImageStream using the ImageProvider.resolve method. The resolve method takes an argument of type ImageConfiguration, that should be filled with as many information as you have available at the code location. You can use the global createLocalImageConfiguration function in most cases, but be aware that this will not work when you create the shader in the initState method of a StatefulWidget.
On the resolved ImageStream you can attach an ImageStreamListener, that takes an ImageListener callback as first parameter. Once the image is loaded, the callback will be called with an ImageInfo, which provides the requested image on the image property.
You can construct the ImageShader with both tile modes as TileMode.clamp and a simple identity matrix, which you can either create by hand or take that one offered by the Matrix4 class. If you need the image shader to be smaller than the size of the provided image, you can wrap your provider in an ResizeProvider and specify the desired width and height.
Below my implementation of an ImageMask widget as a reference, which can be used to mask widgets of any kind.
class ImageMask extends StatefulWidget {
final ImageProvider image;
final double width;
final double height;
final Widget child;
const ImageMask({#required this.image, this.width, this.height, #required this.child});
#override
_ImageMaskState createState() => _ImageMaskState();
}
class _ImageMaskState extends State<ImageMask> {
Future<Shader> _shader;
#override
void initState() {
super.initState();
_shader = _loadShader(context);
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _shader,
builder: (_, AsyncSnapshot<Shader> snapshot) {
return snapshot.connectionState != ConnectionState.done || snapshot.hasError
? SizedBox(width: widget.width, height: widget.height)
: ShaderMask(
blendMode: BlendMode.dstATop,
shaderCallback: (bounds) => snapshot.data,
child: widget.child,
);
},
);
}
Future<Shader> _loadShader(BuildContext context) async {
final completer = Completer<ImageInfo>();
// use the ResizeImage provider to resolve the image in the required size
ResizeImage(widget.image, width: widget.width.toInt(), height: widget.height.toInt())
.resolve(ImageConfiguration(size: Size(widget.width, widget.height)))
.addListener(ImageStreamListener((info, _) => completer.complete(info)));
final info = await completer.future;
return ImageShader(
info.image,
TileMode.clamp,
TileMode.clamp,
Float64List.fromList(Matrix4.identity().storage),
);
}
}

Caching Image on flutter web, is it required?

Due to CachedNetworkImage not working on flutter web, upon porting, I have tried to use this, but my question is do we really need this? Or we just use Image. Network and the browser and service worker will handle the cache part (which is then set by server's response header through, for example, cache-control= "max-age=43200, public"
This is used on the food delivery project I am working on, https://www.santaiyamcha.com
Below are the classes I use to replace CachedNetworkImage which doesn't seem to work well.
import 'package:flutter/material.dart';
import 'package:http/http.dart';
import 'package:http_extensions_cache/http_extensions_cache.dart';
import 'package:http_extensions/http_extensions.dart';
/// Builds a widget when the connectionState is none and waiting
typedef LoadingBuilder = Widget Function(BuildContext context);
/// Builds a if some error occurs
typedef ErrorBuilder = Widget Function(BuildContext context, Object error);
class MeetNetworkImage extends StatelessWidget {
/// Image url that you want to show in your app.
final String imageUrl;
/// When image data loading from the [imageUrl],
/// you can build specific widgets with [loadingBuilder]
final LoadingBuilder loadingBuilder;
/// When some error occurs,
/// you can build specific error widget with [errorBuilder]
final ErrorBuilder errorBuilder;
final double scale;
final double width;
final double height;
final Color color;
final FilterQuality filterQuality;
final BlendMode colorBlendMode;
final BoxFit fit;
final AlignmentGeometry alignment;
final ImageRepeat repeat;
final Rect centerSlice;
final bool matchTextDirection;
/// Whether to continue showing the old image (true), or briefly show nothing
/// (false), when the image provider changes.
final bool gaplessPlayback;
final String semanticLabel;
final bool excludeFromSemantics;
MeetNetworkImage({
#required this.imageUrl,
this.loadingBuilder = null,
this.errorBuilder = null,
this.scale = 1.0,
this.height,
this.width,
this.color = const Color(0xFDFFFF),
this.fit = BoxFit.fill,
this.alignment = Alignment.center,
this.repeat = ImageRepeat.noRepeat,
this.semanticLabel,
this.centerSlice,
this.colorBlendMode,
this.excludeFromSemantics = false,
this.filterQuality = FilterQuality.low,
this.matchTextDirection = false,
this.gaplessPlayback = false,
}) : assert(imageUrl != null),
assert(alignment != null),
assert(repeat != null),
assert(matchTextDirection != null);
Future<Response> getUrlResponse() {
/*
//The caching part I tried, does not seems working
final client = ExtendedClient(
inner: Client(),
extensions: [
CacheExtension(
//logger: Logger("Cache"),
defaultOptions: CacheOptions(
expiry: const Duration(hours: 168),
// The duration after the cached result of the request will be expired.
//forceUpdate: false, // Forces to request a new value, even if an valid cache is available
//forceCache: false, // Forces to return the cached value if available (even if expired).
//ignoreCache: true, //Indicates whether the request should bypass all caching logic
//returnCacheOnError: true, //If [true], on error, if a value is available in the store if is returned as a successful response (even if expired).
keyBuilder: (request) => "${request.method}_${imageUrl.toString()}",
// Builds the unqie key used for indexing a request in cache.
store: MemoryCacheStore(),
// The store used for caching data.
shouldBeSaved: (response) =>
response.statusCode >= 200 && response.statusCode < 300,
),
)
],
);
return client.get(imageUrl);
*/
return get(imageUrl);
}
Widget getLoadingWidget(BuildContext context) {
if (loadingBuilder != null) {
return loadingBuilder(context);
} else
return Container(
height: height, width: width,
child: Center(
child: CircularProgressIndicator()
)
/*Image.asset(
'assets/img/loading4.gif',
height: height,
width: width,
fit: BoxFit.contain,
),*/
);
}
Widget getErrorWidget(BuildContext context, String error) {
if (errorBuilder != null) {
return errorBuilder(context, error);
} else
return Center(child: Icon(Icons.error));
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getUrlResponse(),
builder: (BuildContext context, AsyncSnapshot<Response> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return getLoadingWidget(context);
case ConnectionState.active:
case ConnectionState.done:
if (snapshot.hasError)
return getErrorWidget(context, snapshot.error);
if (!snapshot.hasData)
return getErrorWidget(context, snapshot.error);
//return getLoadingWidget(context);
return Image.memory(
snapshot.data.bodyBytes,
scale: scale,
height: height,
width: width,
color: color,
fit: fit,
alignment: alignment,
repeat: repeat,
centerSlice: centerSlice,
colorBlendMode: colorBlendMode,
excludeFromSemantics: excludeFromSemantics,
filterQuality: filterQuality,
gaplessPlayback: gaplessPlayback,
matchTextDirection: matchTextDirection,
semanticLabel: semanticLabel,
);
}
return Container();
},
);
}
}
What do you suggest?
I am using FadeInImage.memoryNetwork. It is working fine. The browser handles the cache part.
No. This is an issue with Google Chrome which prefers "cache-control" over E-Tags or Last Modified Headers. In my case I am using Firefox which caches on the basis of E-Tags. So here is the thing.
If you are using setState((){}) or the flutter engine calls the setState due to some reason, the images are rebuilt and if the images are not cached, it is again fetched. To prevent it, use the header cache-control: max-age=<any value greater than 0> and it will work fine in Chrome.
Or just build the app using web renderer canvaskit - flutter build web --web-renderer canvaskit.
I have come to this conclusion based on my experience and I could not find it anywhere, Hope it helps :)
Try to update your flutter version if you are using old one, and use from image providers interface.
And the browser will take care of the rest
Example:
Create a container add a decoration in the decoration add an image decoration
Then add from memory or network image.

How to Dynamically Size a CustomPainter

I need to render my custom object inside of a ListTile with custom painter in order to draw some custom text.
ListTile(
title: CustomPaint(
painter: RowPainter.name(
_titleFontSelected,
_titleFont,
text,
index,
MediaQuery.of(context),
currentRow,
),
),
);
Inside my RowPainter I draw the text with the font selected.
When the row is too large, it automatically wraps and get drawn outside the given paint size.
void paint(Canvas canvas, Size size)
I like this behavior, but how can I resize the height of my paint area? Because this is a problem since this overlaps the next List row.
I know that the CustomPaint has a property Size settable, but I know the text dimension only inside my paint function using the TextPainter getBoxesForSelection but it's too late.
How can I "resize" my row painter height dynamically if the text wraps?
TL;DR
You cannot dynamically size a custom painter, however, your problem can be solved using a CustomPaint.
I will first elaborate on the dynamic sizing and then explain how to solve this problem using a constant size.
Dynamic size
This is essentially, where CustomPaint has its limits because it does not provide a way for you to size the painter based on the content.
The proper way of doing this is implementing your own RenderBox and overriding performLayout to size your render object based on the contents.
The RenderBox documentation is quite detailed on this, however, you might still find it difficult to get into it as it is quite different from building widgets.
Constant size
All of the above should not be needed in your case because you do not have a child for your custom paint.
You can simply supply the size parameter to your CustomPaint and calculate the required height in the parent widget.
You can use a LayoutBuilder to get the available width:
LayoutBuilder(
builder: (context, constraints) {
final maxWidth = constraints.maxWidth;
...
}
)
Now, you can simply use a TextPainter to retrieve the required size before even entering your custom paint:
builder: (context, constraints) {
...
final textPainter = TextPainter(
text: TextSpan(
text: 'Your text',
style: yourTextStyle,
),
textDirection: TextDirection.ltr,
);
textPainter.layout(maxWidth: maxWidth); // This will make the size available.
return CustomPaint(
size: textPainter.size,
...
);
}
Now, you can even pass your textPainter to your custom painter directly instead of passing the style arguments.
Your logic might be a bit more complicated, however, the point is that you can calculate the size before creating the CustomPaint, which allows you to set the size.
If you need something more complicated, you will likely have to implement your own RenderBox.
I haven't tested it out, but this might work:
First of all, you wrap the CustomPaint into a stateful widget (called e.g. DynamicCustomPaint), to manipulate your widget dynamically.
You give your CustomPainter a function onResize, which will give you the new size of the canvas when you know it.
You call this function once you know the exact size the Canvas has to be. By using, for example, this technique where you won't have to draw the text to know what size it will be.
When the onResize function will be called, you get the new size for the canvas and call setState in the DynamicCustomPaint state.
This might look like this:
class DynamicCustomPaint extends StatefulWidget {
#override
_DynamicCustomPaintState createState() => _DynamicCustomPaintState();
}
class _DynamicCustomPaintState extends State<DynamicCustomPaint> {
Size canvasSize;
#override
Widget build(BuildContext context) {
// Set inital size, maybe move this to initState function
if (canvasSize == null) {
// Decide what makes sense in your use-case as inital size
canvasSize = MediaQuery.of(context).size;
}
return CustomPaint(
size: canvasSize,
painter: RowPainter.name(_titleFontSelected, _titleFont, text, index, currentRow, onResize: (size) {
setState(() {
canvasSize = size;
});
}),
);
}
}
typedef OnResize = void Function(Size size);
class RowPainter extends CustomPainter {
RowPainter.name(
this._titleFontSelected,
this._titleFont,
this.text,
this.index,
this.currentRow,
{ this.onResize },
);
final FontStyle _titleFontSelected;
final FontStyle _titleFont;
final String text;
final int index;
final int currentRow;
final OnResize onResize;
#override
void paint(Canvas canvas, Size size) {
// TODO: implement paint
// call onResize somewhere in here
// onResize(newSize);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
Use SingleChildRenderObjectWidget and RenderBox instead. Full simple example with dynamic resizing.
DartPad
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: Scaffold(
body: Center(
child: Column(
children: [
SizedBox(height: 100,),
Text('I am above'),
MyWidget(),
Text('I am below')
],
),
),
),
));
}
class MyWidget extends SingleChildRenderObjectWidget {
#override
MyRenderBox createRenderObject(BuildContext context) {
return MyRenderBox();
}
}
class MyRenderBox extends RenderBox {
double myHeight = 200;
#override
void paint(PaintingContext context, Offset offset) {
Paint paint = Paint()
..color = Colors.black..style = PaintingStyle.fill;
context.canvas.drawRect(
Rect.fromLTRB(offset.dx, offset.dy,
offset.dx + size.width, offset.dy + size.height,), paint);
}
#override
void performLayout() {
size = Size(
constraints.constrainWidth(200),
constraints.constrainHeight(myHeight),
);
}
// Timer just an example to show dynamic behavior
MyRenderBox(){
Timer.periodic(Duration(seconds: 2), handleTimeout);
}
void handleTimeout(timer) {
myHeight += 40;
markNeedsLayoutForSizedByParentChange();
layout(constraints);
}
}
CustomPainter will only size to its children's size or initial value passed to the constructor. Documentation:
Custom painters normally size themselves to their child. If they do not have a child, they attempt to size themselves to the size, which defaults to Size.zero. size must not be null.
Basics of RenderBox
https://programmer.group/the-operation-instruction-of-flutter-s-renderbox-principle-analysis.html
https://api.flutter.dev/flutter/rendering/RenderBox-class.html