【问题标题】:Flutter: How to draw a starFlutter:如何画星星
【发布时间】:2020-12-21 08:08:41
【问题描述】:

我正在尝试将我的容器形状自定义为如下所示:

我尝试用 customPaint 来做,但我不太了解这个小部件,所以我需要帮助。

我怎样才能画出这样的形状? customPaint 是正确的解决方案吗?

【问题讨论】:

    标签: android ios flutter flutter-layout


    【解决方案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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-11-28
            • 2019-12-28
            • 2018-03-21
            • 1970-01-01
            • 1970-01-01
            • 2014-01-20
            相关资源
            最近更新 更多