1

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.)

scale invariant rendering

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.

image changes when scale is changed

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?

Sam
  • 563
  • 5
  • 15

2 Answers2

1

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

Kaushik Chandru
  • 15,510
  • 2
  • 12
  • 30
  • Your answer doesn't address the question at all. Maybe my question was unclear. Clarified. – Sam Jul 20 '22 at 12:18
  • Its not possible to create a scale independant painter inside a scaffold since a scaffold will scale automatically to fit different screen resolutions. I only tried to explain that. A sized box of 100 is different in small screen and different in a tablet – Kaushik Chandru Jul 20 '22 at 12:31
  • Thank you. You are missing the point. The point is that I do not just want to mimick the elementary code in every beginner's sample and use `size.height / 2` and `size.width / 2`. I want to set my own scale. – Sam Jul 20 '22 at 13:15
1

As explained in my answer here (saw your comment btw) since FittedBox does not have minimum constrains passed by Scaffold it will shrink as much as possible being the size of the SizedBox the smallest it can get thus changing with v. The solution to this is as simple as passing new constraints telling your FittedBox to expand as much as possible.

Scaffold(
    appBar: AppBar(
      title: const Text('My Title'),
    ),
    body: ConstrainedBox(
      constraints: const BoxConstraints(
        minWidth: double.infinity,
        minHeight: double.infinity,
      ),
      child: FittedBox(
        child: SizedBox(
          width: 3 * v,
          height: 2 * v,
          child: CustomPaint(painter: Painter()),
        ),
      ),
    ),
  ),
Kentukyyo
  • 371
  • 4