Flutter:如何画星星
Posted
技术标签:
【中文标题】Flutter:如何画星星【英文标题】:Flutter: How to draw a star 【发布时间】:2020-12-21 08:08:41 【问题描述】:我正在尝试将我的容器形状自定义为如下所示:
我尝试用 customPaint 来做,但我不太了解这个小部件,所以我需要帮助。
我怎样才能画出这样的形状? customPaint 是正确的解决方案吗?
【问题讨论】:
【参考方案1】:您可以在下面复制粘贴运行完整代码
包https://pub.dev/packages/flutter_custom_clippers的修改代码StarClipper
https://github.com/lohanidamodar/flutter_custom_clippers/blob/master/lib/src/star_clipper.dart
代码sn-p
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),)),
),
),
),
工作演示
完整代码
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),
),
);
【讨论】:
【参考方案2】:我试图创建土耳其国旗是为了好玩。创建新月很容易,但创建星星看起来并不容易。所以我只是尝试使用我的几何和三角学知识来绘制但没有成功。在继续搜索时,我遇到了这个 link 。是的,我找到了我要找的东西。当然,它是关于不同的主题,但功能很有用。所以我在深夜达到了我的目标。
因此,现在可以使用 CustomPainter 创建自定义星星。因为我困了,厌倦了整晚都醒着,所以我分享了所有的代码块,没有进一步的解释。您可以根据需要调整偏移量。如果有任何问题。我准备好回答了。享受编码。
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;
现在您可以在小部件中使用画家;
Center(
child: CustomPaint(
painter: TurkishFlagPaint(),
),
),
结果将是这样的:
【讨论】:
【参考方案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),
),
);
【讨论】:
【参考方案4】:是的,CustomPaint 是正确的解决方案。您可以计算路径(容器周围的一系列点),然后使用画布的drawPath
方法进行绘制。
例如,三角形的路径如下所示:
return Path()
..moveTo(0, y)
..lineTo(x / 2, 0)
..lineTo(x, y)
..lineTo(0, y);
路径从 (0,y)(左上角)开始,然后添加到 (x/2,0)(底部中心)的线,依此类推。此片段取自 this answer。
【讨论】:
以上是关于Flutter:如何画星星的主要内容,如果未能解决你的问题,请参考以下文章