1

I want to create a superellipse shape in Flutter for a widget.

I found an article about creating superellipses written in python and java, but i can't quite get the code to work.

Link to article

class SuperEllipse extends ShapeBorder {

  final BorderSide side;
  final double n;

  SuperEllipse({
    @required this.n,
    this.side = BorderSide.none,
  }) : assert(side != null);

  @override
  EdgeInsetsGeometry get dimensions => EdgeInsets.all(side.width);

  @override
  ShapeBorder scale(double t) {
    return SuperEllipse(
      side: side.scale(t),
      n: n,
    );
  }

  @override
    Path getInnerPath(Rect rect, {TextDirection textDirection}) {
    return _superEllipsePath(rect, n);
  }

  @override
    Path getOuterPath(Rect rect, {TextDirection textDirection}) {
    return _superEllipsePath(rect, n);
  }

  static Path _superEllipsePath(Rect rect, double n) {

    final int a = 200;
    List<double> points = [a + 1.0];
    Path path = new Path();
    path.moveTo(a.toDouble(), 0);

    // Calculate first quadrant.
    for (int x = a; x >= 0; x--) {
      points[x] = pow(pow(a, n) - pow(x, n), 1 / n);
      path.lineTo(x.toDouble(), -points[x]);
    }

    // Mirror to other quadrants.
    for (int x = 0; x <= a; x++) {
      path.lineTo(x.toDouble(), points[x]);
    }
    for (int x = a; x >= 0; x--) {
      path.lineTo(-x.toDouble(), points[x]);
    }
    for (int x = 0; x <= a; x++) {
      path.lineTo(-x.toDouble(), -points[x]);
    }

    return path;

  }

  @override
  void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) {
    Path path = getOuterPath(rect.deflate(side.width / 2.0), textDirection: textDirection);
    canvas.drawPath(path, side.toPaint());
  }

}

I want to return the correct shape, but instead I get an Exception: Invalid value: Only valid value is 0: 200. For some reason the variable a isn't allowed to be 200? I don't know why, and changing it to 0 doesn't produce any errors, but then there is no shape either.

Does anyone know if there is a better way of doing this?

  • see https://www.figma.com/blog/desperately-seeking-squircles/ – pskink Jan 02 '19 at 12:00
  • I'm not sure how that article is helpful... I need help with my code, I understand what a superellipse and a squircle is. – Sander Dalby Larsen Jan 02 '19 at 12:08
  • i though you really wanted the case where n >= 4 – pskink Jan 02 '19 at 12:17
  • That's not the problem, The problem is that I'm getting an Exception when I use the Shape – Sander Dalby Larsen Jan 02 '19 at 12:19
  • ok so what is you exact stacktrace (3-4 top frames)? – pskink Jan 02 '19 at 12:24
  • `I/flutter (24060): #0 List._setIndexed (dart:core/runtime/libgrowable_array.dart:151:72)` `I/flutter (24060): #1 List.[]= (dart:core/runtime/libgrowable_array.dart:148:5)` `I/flutter (24060): #2 SuperEllipse._superEllipsePath (package:superellipse_test/superellipse.dart:46:13)` `I/flutter (24060): #3 SuperEllipse.getOuterPath (package:superellipse_test/superellipse.dart:34:12)` – Sander Dalby Larsen Jan 02 '19 at 12:36
  • in `points[x] = ...` your `x` index is == 200 (since it is initialized with `int x = a`) but length of `points` array is == 1 – pskink Jan 02 '19 at 12:53
  • btw the stack trace `superellipse.dart:46:13` points out to the source of your problem: file:`superellipse.dart`, line:`46`, column:`13` – pskink Jan 02 '19 at 13:05
  • I see. I am getting a shape now. Do you have any idea how I can calculate the Y axis as well? Right now I'm only calculating with the X axis – Sander Dalby Larsen Jan 02 '19 at 13:19
  • i dont get it: you call `path.lineTo` so you have the complete `Path` - it is returned from `_superEllipsePath` method – pskink Jan 02 '19 at 13:20
  • [Image](https://imgur.com/a/LQu4jjt) I am getting a shape, but it is supposed to be centered, since it's wrapped in a 100x100 centered `Container` widget. I figured that's because I'm doing something wrong with the axis? Or maybe I'm starting the path in the wrong place. – Sander Dalby Larsen Jan 02 '19 at 13:26
  • run `flutter run lib/superellipse.dart` fron the terminal, press `p` key and take the screenshot again (or simply use [this](https://flutter.io/docs/testing/debugging#visual-debugging)) – pskink Jan 02 '19 at 13:33
  • [New image](https://imgur.com/a/UL7XNnh) – Sander Dalby Larsen Jan 02 '19 at 13:42
  • well, now it is obvious ;-) - your path is set outside your bounds – pskink Jan 02 '19 at 13:44
  • you have to use `Rect rect` parameter instead of using `final int a = 200;` – pskink Jan 02 '19 at 13:54
  • I'm trying that right now, but I can't seem to get it to work... I'm thinking the issue may be in `points[x] = pow(pow(a, n) - pow(x, n), 1 / n);` – Sander Dalby Larsen Jan 02 '19 at 14:00
  • If I change the path to start at the center of the shape, the points are still as they were before... [image](https://imgur.com/a/x3Cbp9j) – Sander Dalby Larsen Jan 02 '19 at 14:22
  • simply use `Rect rect` parameter and not fixed `200` - you are computing one quadrant so you have to divide the side by 2 and then to `shift()` your final `Path` – pskink Jan 02 '19 at 14:26
  • I am using the rect parameter. The first loop looks like this now: This is the code behind the image: `for (int x = width.toInt(); x >= 0; x--) { points[x] = pow(pow(width, n) - pow(x, n), 1 / n); path.lineTo(x.toDouble(), -points[x]); }` – Sander Dalby Larsen Jan 02 '19 at 14:35
  • It works perfectly now :) Thank you very much! – Sander Dalby Larsen Jan 02 '19 at 17:02
  • sure, your welcome – pskink Jan 02 '19 at 17:05
  • of course you have revert `paint` method to your version as i tested it with some hardcoded `Paint` – pskink Jan 02 '19 at 17:56

2 Answers2

2

there is a class for this in flutter, it is called ContinuousRectangleBorder.

    Material(
    color: Colors.red,
    shape: ContinuousRectangleBorder(
      borderRadius: BorderRadius.circular(30.0),
    ),
    child: Container(
      padding: EdgeInsets.all(40,0),
      margin: EdgeInsets.all(8.0),
      child: Center(child: Text('Hello World')),
    ),
  )

you can also use it directly in container with decoration: ShapeDecoration(shape: ContinuousRectangleBorder())

Container(
    decoration: ShapeDecoration(
      color: Colors.red,
      shape: ContinuousRectangleBorder(
        borderRadius: BorderRadius.circular(70.0),
      ),
    ),
    padding: EdgeInsets.fromLTRB(10, 40, 10, 40),
    // margin: EdgeInsets.all(8.0),
    child: Center(child: Text('Hello World')),
  )
karris7
  • 36
  • 6
0

came across this package that produces squircles

https://pub.dev/packages/cupertino_rounded_corners

i'm sure you can dig around the code to find out how it makes the shape

user433575
  • 357
  • 1
  • 4
  • 13