'Flutter: How to draw a star
Solution 1:[1]
You can copy paste run full code below
modified code of package https://pub.dev/packages/flutter_custom_clippers 'sStarClipper
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:[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(),
),
),
Solution 3:[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),
),
);
}
}
Solution 4:[4]
Yes, CustomPaint is the right solution. You can calculate the Path (series of points around the Container) and then paint it with the drawPath
method of the canvas.
For example, the path of a triangle would look like this:
return Path()
..moveTo(0, y)
..lineTo(x / 2, 0)
..lineTo(x, y)
..lineTo(0, y);
The Path starts at (0,y) (top-left), then a line to (x/2,0) (bottom-center) gets added and so on. This snipped was taken from this answer.
Solution 5:[5]
This code will help you build a centered star, to use it you just have to instantiate it in a ClipPath
import 'package:flutter/material.dart';
import 'dart:math' as math;
const STAR_POINTS = 5;
class StarClipper extends CustomClipper<Path> {
@override
Path getClip(Size size) {
var centerX = size.width / 2;
var centerY = size.height / 2;
var path = Path();
var radius = size.width / 2;
var inner = radius / 2;
var rotation = math.pi / 2 * 3;
var step = math.pi / STAR_POINTS;
path.lineTo(centerX, centerY - radius);
for (var i = 0; i < STAR_POINTS; i++) {
var x = centerX + math.cos(rotation) * radius;
var y = centerY + math.sin(rotation) * radius;
path.lineTo(x, y);
rotation += step;
x = centerX + math.cos(rotation) * inner;
y = centerY + math.sin(rotation) * inner;
path.lineTo(x, y);
rotation += step;
}
path.lineTo(centerX, centerY - radius);
path.close();
return path;
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}
/// Instance
ClipPath(
clipper: StarClipper(),
child: Container(
color: Colors.red,
width: 80,
height: 80,
),
)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | chunhunghan |
Solution 2 | Bilal ÅžimÅŸek |
Solution 3 | Taha Gorme |
Solution 4 | Herry |
Solution 5 | ArmandoHackCode |