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的修改代码StarClipperhttps://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:如何画星星的主要内容,如果未能解决你的问题,请参考以下文章

Xmas!送你Flutter Animation小星星!

Flutter:Google Map Plugin如何在地图内的两个坐标之间画一条线

flutter实现画中国地图

flutter实现画中国地图

flutter实现画中国地图

flutter实现画中国地图