Why is CustomPaint affected by whether it is under a Scaffold? - flutter

App〈 MaterialApp〈 MyWidget : all is good
Whether I draw the figure on a canvas with a small or a large scale—by replacing v = 100000.0 with v = 1000.0 in the following code, the image doesn't change, as expected.
(If you are not familiar with the idea of using a SizedBox inside a FittedBox, it is explained here.)
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
const v = 10000.0;
class Painter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
final mypaint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = 0.012 * v
..color = Colors.blue;
canvas.drawCircle(
Offset(1.5 * v, 1 * v),
1 * v, mypaint);
}
#override
bool shouldRepaint(Painter oldDelegate) => false;
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "My App",
home: FittedBox(
child: SizedBox(
width: 3 * v,
height: 2 * v,
child: CustomPaint(
painter: Painter()
),
),
),
);
}
}
App〈 Scaffold〈 MaterialApp〈 MyWidget : My chosen size in SizedBox is ignored; why?
But if I put MaterialApp inside a Scaffold, I still get the image on the left for v = 10000.0, but with v = 100.0, I get instead the image on the right.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
const v = 10000.0;
class Painter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
final mypaint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = 0.012 * v
..color = Colors.blue;
canvas.drawCircle(
Offset(1.5 * v, 1 * v),
1 * v, mypaint);
}
#override
bool shouldRepaint(Painter oldDelegate) => false;
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "My App",
home: Scaffold(
appBar: AppBar(
title: const Text('My Title'),
),
body: FittedBox(
child: SizedBox(
width: 3 * v,
height: 2 * v,
child: CustomPaint(
painter: Painter()
),
),
),
),
);
}
}
How do I get a scale invariant CustomPaint with a Scaffold?

The custom paint is independant of the size. It takes the size sent from it's caller. If you set the sizedbox's size to 200x200 it will paint in that space. Now 10k or 1k will give same result since there is no space left on the device to paint something since width of device is less than 1k. But 10, 100 and 1k will show different sized circles. The only factor that defines the scale here would be the size of the SizedBox
The draw circle can also be written as
canvas.drawCircle(Offset(size.height / 2, size.width / 2), size.width/2);
Now if you pass a sized box of 100x100 it will draw a circle of diameter 100

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;
}

Custom shape tappable area with CustomPaint widget on Flutter

I’ve seen some posts with things similar to this question, but they’re not what I’m looking for. I want to create a button with a custom shape in Flutter. For that I use a CustomPaint widget inside a GestureDetector widget. The problem is that I don’t want invisible areas to be tappable. And that's exactly what happens with the GestureDetector. In others words, I just want my created shape to be tappable. But right now it seems that there's an invisible square where my custom shape is, and that is also tappable. I don't want that. The most similar issue I found in this post:
Flutter - Custom button tap area
however, in my case I’m dealing with custom shapes and not with squares or circles.
Let me share with you the code and an example image of a possible button. You could just copy it and paste it direct on your main. It should be easy to replicate my problem.
import 'package:flutter/material.dart';
import 'dart:ui' as ui;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Custom Shapes',
theme: ThemeData.dark(),
home: MyHomePage(title: 'Custom Shapes App'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
backgroundColor: Colors.white24,
body: Center(
child: GestureDetector(
child: CustomPaint(
size: Size(300,300), //You can Replace this with your desired WIDTH and HEIGHT
painter: RPSCustomPainter(),
),
onTap: (){
print("Working?");
},
),
),
);
}
}
class RPSCustomPainter extends CustomPainter{
#override
void paint(Canvas canvas, Size size) {
Paint paint_0 = new Paint()
..color = Color.fromARGB(255, 33, 150, 243)
..style = PaintingStyle.fill
..strokeWidth = 1;
paint_0.shader = ui.Gradient.linear(Offset(0,size.height*0.50),Offset(size.width,size.height*0.50),[Color(0xffffed08),Color(0xffffd800),Color(0xffff0000)],[0.00,0.34,1.00]);
Path path_0 = Path();
path_0.moveTo(0,size.height*0.50);
path_0.lineTo(size.width*0.33,size.height*0.33);
path_0.lineTo(size.width*0.50,0);
path_0.lineTo(size.width*0.67,size.height*0.33);
path_0.lineTo(size.width,size.height*0.50);
path_0.lineTo(size.width*0.67,size.height*0.67);
path_0.lineTo(size.width*0.50,size.height);
path_0.lineTo(size.width*0.33,size.height*0.67);
path_0.lineTo(0,size.height*0.50);
path_0.close();
canvas.drawPath(path_0, paint_0);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
I'd try that the star is the only tappable thing, and no other invisible place on the screen.
Thanks in advance!
With help of this issue https://github.com/flutter/flutter/issues/60143 and the hint given that I should use a RaisedButton with a custom shape I was able solve the problem. I did some changes to the code posted on github. Mine wasn't the best to start with. There are other better options than using a CustomPaint widget with a GestureDetector.
Here you have the code. You should be able to see that if you tap anywhere outside the shape given, the print statement will not be triggered.
import 'package:flutter/material.dart';
void main() {
runApp(App());
}
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(body: Center(child: BuyTicketButton(100.0, ()=>{})))
);
}
}
class BuyTicketButton extends StatelessWidget {
final double cost;
final Function onTap;
const BuyTicketButton(this.cost, this.onTap, {Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: Container(
height: 400,
child: RaisedButton(
shape: CustomBorder(),
onPressed: (){
print("this works");
},
),
),
);
}
}
class CustomBorder extends OutlinedBorder {
const CustomBorder({
BorderSide side = BorderSide.none
}) : assert(side != null), super(side: side);
Path customBorderPath(Rect rect) {
Path path = Path();
path.moveTo(0, 0);
path.lineTo(rect.width, 0);
path.lineTo(rect.width, rect.height);
path.lineTo(0, rect.height);
double diameter = rect.height / 3;
double radius = diameter / 2;
path.lineTo(0, diameter * 2);
path.arcToPoint(
Offset(0, diameter),
radius: Radius.circular(radius),
clockwise: false,
);
path.lineTo(0, 0);
return path;
}
#override
OutlinedBorder copyWith({BorderSide side}) {
return CustomBorder(side: side ?? this.side);
}
#override
EdgeInsetsGeometry get dimensions => EdgeInsets.all(side.width);
#override
Path getInnerPath(Rect rect, {TextDirection textDirection}) {
return customBorderPath(rect);
}
#override
Path getOuterPath(Rect rect, {TextDirection textDirection}) {
return customBorderPath(rect);
}
#override
void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) {
switch (side.style) {
case BorderStyle.none:
break;
case BorderStyle.solid:
canvas.drawPath(customBorderPath(rect), Paint()
..style = PaintingStyle.stroke
..color = Colors.black
..strokeWidth = 1.0
);
}
}
#override
ShapeBorder scale(double t) => CustomBorder(side: side.scale(t));
}
This is the image you will be seeing. There, if you now tap on the half empty circle, you'll see nothing will happen. That's what I was expecting.
Nonetheless, I would recommend reading my other answer, which is for me so far better than this one.
There are for now two solutions so far, the first one is just by overriding hitTest of CustomPainter class, however the behavior is not the most desired. Because you don't have any splashcolor or similar when tapping. So here is the first one:
import 'package:flutter/material.dart';
import 'dart:ui' as ui;
void main() {
runApp(App());
}
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.black54,
body: Center(
child: TappableStarButton(),
),
),
);
}
}
class TappableStarButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: GestureDetector(
child: CustomPaint(
size: Size(300, 300),
painter: RPSCustomPainter(),
),
onTap: () {
print("This works");
},
),
);
}
}
class RPSCustomPainter extends CustomPainter {
Path path_0 = Path();
#override
void paint(Canvas canvas, Size size) {
Paint paint_0 = new Paint()
..color = Color.fromARGB(255, 33, 150, 243)
..style = PaintingStyle.fill
..strokeWidth = 1;
paint_0.shader = ui.Gradient.linear(
Offset(10, 150),
Offset(290, 150),
[Color(0xffff1313), Color(0xffffbc00), Color(0xffffca00)],
[0.00, 0.69, 1.00]);
path_0.moveTo(150, 10);
path_0.lineTo(100, 100);
path_0.lineTo(10, 150);
path_0.lineTo(100, 200);
path_0.lineTo(150, 290);
path_0.lineTo(200, 200);
path_0.lineTo(290, 150);
path_0.lineTo(200, 100);
canvas.drawPath(path_0, paint_0);
}
#override
bool hitTest(Offset position) {
bool hit = path_0.contains(position);
return hit;
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
And it works. The problem is that you don't see any behavior when you tap on the "button".
The other solution, and way better, is by using Material with an Inkwell for your button. And for your shape, ShapeBorder class.
Without null safety
Here it is:
import 'package:flutter/material.dart';
import 'dart:ui' as ui;
void main() {
runApp(App());
}
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.black38,
body: StarButton(),
),
);
}
}
class StarButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: Container(
height: 300,
width: 300,
child: Material(
shape: StarShape(),
color: Colors.orange,
child: InkWell(
splashColor: Colors.yellow,
onTap: () => print('it works'),
),
),
),
);
}
}
class StarShape extends ShapeBorder {
#override
EdgeInsetsGeometry get dimensions => null;
#override
Path getInnerPath(Rect rect, {ui.TextDirection textDirection}) => null;
#override
void paint(Canvas canvas, Rect rect, {ui.TextDirection textDirection}) =>
null;
#override
ShapeBorder scale(double t) => null;
#override
Path getOuterPath(Rect rect, {ui.TextDirection textDirection}) {
return Path()
..moveTo(150, 10)
..lineTo(100, 100)
..lineTo(10, 150)
..lineTo(100, 200)
..lineTo(150, 290)
..lineTo(200, 200)
..lineTo(290, 150)
..lineTo(200, 100)
..close();
}
}
With null safety:
import 'package:flutter/material.dart';
import 'dart:ui' as ui;
void main() {
runApp(const App());
}
class App extends StatelessWidget {
const App({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
backgroundColor: Colors.black38,
body: StarButton(),
),
);
}
}
class StarButton extends StatelessWidget {
const StarButton({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: SizedBox(
height: 300,
width: 300,
child: Material(
shape: StarShape(),
color: Colors.orange,
child: InkWell(
customBorder: StarShape(),
splashColor: Colors.yellow,
onTap: () => print('it works'),
),
),
),
);
}
}
class StarShape extends ShapeBorder {
#override
void paint(Canvas canvas, Rect rect, {ui.TextDirection? textDirection}) {
return;
}
#override
Path getOuterPath(Rect rect, {ui.TextDirection? textDirection}) {
return Path()
..moveTo(150, 10)
..lineTo(100, 100)
..lineTo(10, 150)
..lineTo(100, 200)
..lineTo(150, 290)
..lineTo(200, 200)
..lineTo(290, 150)
..lineTo(200, 100)
..close();
}
#override
EdgeInsetsGeometry get dimensions => EdgeInsets.zero;
#override
Path getInnerPath(Rect rect, {TextDirection? textDirection}) => Path();
#override
ShapeBorder scale(double t) => StarShape();
}
It looks like this:
A GestureDetector is hit (and start detecting) when its child says it is hit (unless you chance its behavior property).
To specify when CustomPaint is hit, CustomPainter has a hitTest(Offset) method that you can override. It should return whether the Offset should be consider inside your shape. Unfortunately, the method doesn’t have a size parameter. (That’s a bug which solution has hit some inertia, see https://github.com/flutter/flutter/issues/28206)
The only good solution is to make a custom render object in which you override the paint and hitTestSelf methods (in the latter, you can use the objects size property).
For example:
class MyCirclePainter extends LeafRenderObjectWidget {
const MyCirclePainter({#required this.radius, Key key}) : super(key: key);
// radius relative to the widget's size
final double radius;
#override
RenderObject createRenderObject(BuildContext context) => RenderMyCirclePainter()..radius = radius;
#override
void updateRenderObject(BuildContext context, RenderMyCirclePainter renderObject) => renderObject.radius = radius;
}
class RenderMyCirclePainter extends RenderBox {
double radius;
#override
void performLayout() {
size = constraints.biggest;
}
#override
void performResize() {
size = constraints.biggest;
}
#override
void paint(PaintingContext context, Offset offset) {
final center = size.center(offset);
final r = 1.0 * radius * size.width;
final backgroundPaint = Paint()
..color = const Color(0x88202020)
..style = PaintingStyle.fill;
context.canvas.drawCircle(center, r, backgroundPaint);
}
#override
bool hitTestSelf(Offset position) {
final center = size.center(Offset.zero);
return (position - center).distance < size.width * radius;
}
}
Note that the top-left of the widget is at the offset parameter in the paint method, instead of the Offset.zero it is in CustomPainter.
You probably want to construct the path once and use path_0.contains(position) in hitTestSelf.

Canvas.drawRect() with Rect.fromLTRB() not working

In my flutter app, I tried to use CustomPainter, to draw custom shapes.
Here, I want to draw 4 small squares, in different colors, in a bigger square (here in yellow).
I expected to see the 4 squares, but for some reason, the 4 squares are on the top left of the CustomPainter.
Here is my sample app:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: Center(
child: Container(
color: Colors.amber,
width: 180,
height: 180,
child: CustomPaint(
painter: TestCustomPainter(),
),
),
),
);
}
}
class TestCustomPainter extends CustomPainter {
Paint _paintWhite = Paint()
..color = Colors.white
..isAntiAlias = true;
Paint _paintBlue = Paint()
..color = Colors.blue
..isAntiAlias = true;
Paint _paintRed = Paint()
..color = Colors.red
..isAntiAlias = true;
Paint _paintGreen = Paint()
..color = Colors.green
..isAntiAlias = true;
#override
void paint(Canvas canvas, Size size) {
canvas.drawRect(Rect.fromLTRB(0, 0, 90, 90), _paintWhite);
canvas.drawRect(Rect.fromLTRB(90, 0, 0, 90), _paintBlue);
canvas.drawRect(Rect.fromLTRB(0, 90, 90, 0), _paintRed);
canvas.drawRect(Rect.fromLTRB(90, 90, 0, 0), _paintGreen);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
And here is what I get:
What did I do wrong?
Thanks.
I think this is what you are looking for
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: Center(
child: Container(
color: Colors.amber,
width: 180,
height: 180,
child: CustomPaint(
painter: TestCustomPainter(),
),
),
),
);
}
}
class TestCustomPainter extends CustomPainter {
Paint _paintWhite = Paint()
..color = Colors.white
..isAntiAlias = true;
Paint _paintBlue = Paint()
..color = Colors.blue
..isAntiAlias = true;
Paint _paintRed = Paint()
..color = Colors.red
..isAntiAlias = true;
Paint _paintGreen = Paint()
..color = Colors.green
..isAntiAlias = true;
#override
void paint(Canvas canvas, Size size) {
canvas.drawRect(Rect.fromLTRB(90, 0, 180, 90), _paintWhite);
canvas.drawRect(Rect.fromLTRB(90, 0, 0, 90), _paintBlue);
canvas.drawRect(Rect.fromLTRB(0, 90, 90, 180), _paintRed);
canvas.drawRect(Rect.fromLTRB(180, 180, 90,90), _paintGreen);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
since
Rect.fromLTRB(double left, double top, double right, double bottom)
Construct a rectangle from its left, top, right, and bottom edges.
more info about this over here in docs https://api.flutter.dev/flutter/dart-ui/Rect-class.html

how to create a styling in button in flutter?

How make this styling for a button in flutter. Since I'm a newbie, I have no Idea on how to do it.
In image the triangle is somewhat irregular, I want that to be matched with the button box.
You can use the CustomPaint widget to get that effect. You can easily auto-generate the CustomPaint code from Flutter Shape Maker. Please see the code below. You can also run the code on DartPad at the following URL https://dartpad.dev/0171e4b838b740ea23f896f3b0be1f8e :
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: MyWidget(),
);
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
width: 360,
height: 100,
color: Colors.black,
child: CustomPaint(
size: Size(1200, 700),
painter: FlagPainter(),
child: Text(
"text",
style: TextStyle(color: Colors.black, fontSize:24),
),
),
),
),
);
}
}
class FlagPainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
Paint paint_0 = new Paint()
..color = Color.fromARGB(255, 255, 255, 255)
..style = PaintingStyle.fill
..strokeWidth = 1;
Path path_0 = Path();
path_0.moveTo(0, 0);
path_0.lineTo(0, size.height * 2.40);
path_0.lineTo(0, size.height * 0.72);
path_0.lineTo(size.width * 0.28, 0);
canvas.drawPath(path_0, paint_0);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}

Get position and size of Widget during build

How can I get the size and position of a widget during the build() method? In the following code, I want to draw rectangle in the CustomPaint widget that is centered and is a certain percentage of the remaining area of the viewport, which is everything below below the Text widget, marked (A). I can get the width from MediaQuery, but how would I get the remaining height after (A)? I would have to know the position of the Text widget.
This answer does not work because Flutter throws an exception when attempting to retrieve the RenderBox during build().
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('App')),
body: Column(children: [
Text('Test'), // (A) Get position of this
CustomPaint(
painter: DrawArea(MediaQuery.of(context).size),
size: Size(1000, 800),
),
]),
);
}
}
class DrawArea extends CustomPainter {
final Size viewportSize;
DrawArea(this.viewportSize);
#override
void paint(Canvas canvas, Size size) {
var paint = Paint()
..color = Colors.black
..strokeWidth = 1
..style = PaintingStyle.stroke;
double yOffset = (this.viewportSize.width - this.viewportSize.width * .95) / 2;
canvas.drawRect(Rect.fromLTWH(yOffset, 0, this.viewportSize.width * .95, 800), paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
The second argument of the paint method is the size of the CustomPaint widget. You just need to make sure your widget gets the right size.
To make the CustomPaint take the rest of the space in the Column, you will need to wrap it in an Expanded widget:
body: Column(children: [
Text('Test'),
Expanded(
child: CustomPaint(
painter: DrawArea(),
size: Size.infinite, // make it as large as possible
),
),
]),
Then you can just use the passed size parameter in your custom painter:
class DrawArea extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
var paint = Paint()
..color = Colors.black
..strokeWidth = 1
..style = PaintingStyle.stroke;
double xOffset = size.width * 0.025
double yOffset = size.height * 0.025
canvas.drawRect(Rect.fromLTWH(xOffset, yOffset, size.width * .95, size.height * .95), paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}