Flutter: How to draw a star
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
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 :
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),
),
);
}
}