Flutter: How to draw a star

1,989

Solution 1

You can copy paste run full code below
modified code of package https://pub.dev/packages/flutter_custom_clippers 's
StarClipper https://github.com/lohanidamodar/flutter_custom_clippers/blob/master/lib/src/star_clipper.dart

code snippet

class StarClipper extends CustomClipper<Path>
 @override
   Path getClip(Size size) {
     ...
     double radius = halfWidth / 1.3;
...
Container(
      height: 200,
      width: 200,
      child: ClipPath(
        clipper: StarClipper(14),
        child: Container(
          height: 150,
          color: Colors.green[500],
          child: Center(child: Text("+6", style: TextStyle(fontSize: 50),)),
        ),
      ),
    ),

working demo

enter image description here

full code

import 'package:flutter/material.dart';
import 'dart:math' as math;

class StarClipper extends CustomClipper<Path> {
  StarClipper(this.numberOfPoints);

  /// The number of points of the star
  final int numberOfPoints;

  @override
  Path getClip(Size size) {
    double width = size.width;
    print(width);
    double halfWidth = width / 2;

    double bigRadius = halfWidth;

    double radius = halfWidth / 1.3;

    double degreesPerStep = _degToRad(360 / numberOfPoints);

    double halfDegreesPerStep = degreesPerStep / 2;

    var path = Path();

    double max = 2 * math.pi;

    path.moveTo(width, halfWidth);

    for (double step = 0; step < max; step += degreesPerStep) {
      path.lineTo(halfWidth + bigRadius * math.cos(step),
          halfWidth + bigRadius * math.sin(step));
      path.lineTo(halfWidth + radius * math.cos(step + halfDegreesPerStep),
          halfWidth + radius * math.sin(step + halfDegreesPerStep));
    }

    path.close();
    return path;
  }

  num _degToRad(num deg) => deg * (math.pi / 180.0);

  @override
  bool shouldReclip(CustomClipper<Path> oldClipper) {
    StarClipper oldie = oldClipper as StarClipper;
    return numberOfPoints != oldie.numberOfPoints;
  }
}


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(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Container(
              height: 200,
              width: 200,
              child: ClipPath(
                clipper: StarClipper(14),
                child: Container(
                  height: 150,
                  color: Colors.green[500],
                  child: Center(child: Text("+6", style: TextStyle(fontSize: 50),)),
                ),
              ),
            ),

          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

Solution 2

I was trying to create Turkish flag for fun. Creating crescent was easy but creating star is not look easy. so I just tried to use my geometric and trigonometric knowledge to draw but no success. while continuing searching I encountered this link . Yeess I found what I look for. of course it was about different topic but functions was useful. so I reach my goal at the end of night.

So creating custom stars are possible with CustomPainter since now. Because I m sleepy and tired of being awake whole night I share all of code block with no further explanation. You can adjust offsetts according to your need. if any question. I m ready to answer. Enjoy coding.

 class TurkishFlagPaint extends CustomPainter {
      @override
      void paint(Canvas canvas, Size size) {
        // x and y coordinates of starting point 
    
        final bx = 95;
        final by = 0;
    
        final paint = Paint();
        paint.color = Colors.white;
        final innerCirclePoints = 5; //how many edges you need?
        final innerRadius = 80 / innerCirclePoints;
        final innerOuterRadiusRatio = 2.5;
        final outerRadius = innerRadius * innerOuterRadiusRatio;
    
        List<Map> points =
            calcStarPoints(bx, by, innerCirclePoints, innerRadius, outerRadius);
        var star = Path()..moveTo(points[0]['x'], points[0]['y']);
        points.forEach((point) {
          star.lineTo(point['x'], point['y']);
        });
    
        canvas.drawPath(
          Path.combine(
            PathOperation.union,
    
            //this combine for crescent
            Path.combine(
              PathOperation.difference,
              Path()..addOval(Rect.fromCircle(center: Offset(-20, 0), radius: 80)),
              Path()
                ..addOval(Rect.fromCircle(center: Offset(2, 0), radius: 60))
                ..close(),
            ),
            star,// I also combine cresscent with star
          ),
          paint,
        );
      }


      //This function is life saver. 
//it produces points for star edges inner and outer. if you need to //rotation of star edges. 
// just play with - 0.3  value in currX and currY equations.
    
      List<Map> calcStarPoints(
          centerX, centerY, innerCirclePoints, innerRadius, outerRadius) {
        final angle = ((math.pi) / innerCirclePoints);
        var angleOffsetToCenterStar = 0;
    
        var totalPoints = innerCirclePoints * 2; // 10 in a 5-points star
        List<Map> points = [];
        for (int i = 0; i < totalPoints; i++) {
          bool isEvenIndex = i % 2 == 0;
          var r = isEvenIndex ? outerRadius : innerRadius;
    
          var currY =
              centerY + math.cos(i * angle + angleOffsetToCenterStar - 0.3) * r;
          var currX =
              centerX + math.sin(i * angle + angleOffsetToCenterStar - 0.3) * r;
          points.add({'x': currX, 'y': currY});
        }
        return points;
      }
    
      @override
      bool shouldRepaint(CustomPainter oldDelegate) => true;
    }

now you can use painter in a widget;

Center(
   child: CustomPaint(
       painter: TurkishFlagPaint(),
     ),
   ),

and the result will be like this : enter image description here

Solution 3

import 'package:flutter/material.dart';
import 'dart:math' as math;

class StarClipper extends CustomClipper<Path> {
  StarClipper(this.numberOfPoints);

  /// The number of points in the star
  final int numberOfPoints;

  @override
  Path getClip(Size size) {
    double width = size.width;
    print(width);
    double halfWidth = width / 2;

    double bigRadius = halfWidth;

    double radius = halfWidth / 1.3;

    double degreesPerStep = _degToRad(360 / numberOfPoints);

    double halfDegreesPerStep = degreesPerStep / 2;

    var path = Path();

    double max = 2 * math.pi;

    path.moveTo(width, halfWidth);

    for (double step = 0; step < max; step += degreesPerStep) {
      path.lineTo(halfWidth + bigRadius * math.cos(step),
          halfWidth + bigRadius * math.sin(step));
      path.lineTo(halfWidth + radius * math.cos(step + halfDegreesPerStep),
          halfWidth + radius * math.sin(step + halfDegreesPerStep));
    }

    path.close();
    return path;
  }

  num _degToRad(num deg) => deg * (math.pi / 180.0);

  @override
  bool shouldReclip(CustomClipper<Path> oldClipper) {
    StarClipper oldie = oldClipper as StarClipper;
    return numberOfPoints != oldie.numberOfPoints;
  }
}


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(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Container(
              height: 200,
              width: 200,
              child: ClipPath(
                clipper: StarClipper(14),
                child: Container(
                  height: 150,
                  color: Colors.green[500],
                  child: Center(child: Text("+6", style: TextStyle(fontSize: 50),)),
                ),
              ),
            ),

          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}
Share:
1,989
luc
Author by

luc

Updated on December 23, 2022

Comments

  • luc
    luc over 1 year

    I am trying to custom my container shape to look like this:

    enter image description here

    I tried to do it with customPaint but I don't know very well this widget so I need help.

    How can I draw a shape like this? Is customPaint the right solution?