无法在颤动中相互堆叠 5 个圆圈

Posted

技术标签:

【中文标题】无法在颤动中相互堆叠 5 个圆圈【英文标题】:unable to stack 5 circles on each other in flutter 【发布时间】:2020-12-17 07:30:04 【问题描述】:

我想要实现的是: 我已经取得并坚持的是: 我在容器内使用墨水瓶小部件

代码:

Container(
                alignment: Alignment.center,
                child: InkWell(
                  child: new Container(
                    width: 120.0,
                    height: 120.0,
                    padding: const EdgeInsets.all(20.0),//I used some padding without fixed width and height
                    decoration: new BoxDecoration(
                      shape: BoxShape.circle,// You can use like this way or like the below line
                      //                borderRadius: new BorderRadius.circular(30.0),
                      color: Colors.white,
                    ),
                    child: new Text("", style: new TextStyle(color: Colors.black, fontSize: 30.0, fontWeight: FontWeight.bold)),
                    alignment: Alignment.center,
                    // You can add a Icon instead of text also, like below.
                    //child: new Icon(Icons.arrow_forward, size: 50.0, color: Colors.black38)),
                  ),

请帮忙。

【问题讨论】:

请不要破坏您的帖子。我们的目标是让问题及其答案对其他有相同主题问题的人具有持久的效用。删除图像会阻碍该目标并使答案为提供解决方案所做的工作无效。 【参考方案1】:

你可以注意到,实际上这是一个不同颜色的元素,它以 360/5 度角穿过圆

你可以使用
Path.combine(
          PathOperation.reverseDifference,

    import 'dart:math' as math;

    import 'package:flutter/material.dart';

    const double degrees2Radians = math.pi / 180.0;
    void main() => runApp(MyApp());

    class MyApp extends StatelessWidget 
      @override
      Widget build(BuildContext context) 
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: Scaffold(
            body: SafeArea(
              child: MyHomePage(),
            ),
          ),
        );
      
    

    class MyHomePage extends StatefulWidget 
      @override
      _MyHomePageState createState() => _MyHomePageState();
    

    class _MyHomePageState extends State<MyHomePage> 
      int _currentSliderValue = 2;

      List<Color> colors = [
        Colors.blue,
        Colors.red,
        Colors.yellow,
        Colors.green,
        Colors.purple,
        Colors.orange,
      ];
      @override
      Widget build(BuildContext context) 
        return Column(
          children: [
            Expanded(
              child: Container(
                width: double.infinity,
                child: CustomPaint(
                  painter: MyPainter(
                    colors: colors.take(_currentSliderValue).toList(),
                  ),
                ),
              ),
            ),
            Slider(
              value: _currentSliderValue.toDouble(),
              min: 2,
              max: 6,
              label: _currentSliderValue.toString(),
              onChanged: (value) 
                setState(() 
                  _currentSliderValue = value.round();
                );
              ,
            ),
          ],
        );
      
    

    class MyPainter extends CustomPainter 
      const MyPainter(
        @required this.colors,
      ) : super();

      final List<Color> colors;

      @override
      void paint(Canvas canvas, Size size) 
        var paths = <Color, Path>;
        colors.asMap().forEach((index, color) 
          var degree = 360 / colors.length * (index + 0.5);
          var radian = degree * degrees2Radians;

          var path = _onePath(radian, size);

          if (index > 0) 
            path = Path.combine(
              PathOperation.difference,
              path,
              paths[colors[index - 1]],
            );
          
          if (index == colors.length - 1) 
            paths[colors[0]] = Path.combine(
              PathOperation.reverseDifference,
              path,
              paths[colors[0]],
            );
          
          paths[color] = path;
        );

        for (final color in colors) 
          var path = paths[color];

          final fillPaint = Paint()
            ..color = color
            ..style = PaintingStyle.fill;
          canvas.drawPath(path, fillPaint);

          final strokePaint = Paint()
            ..color = Color(0xFFC4C4C4)
            ..style = PaintingStyle.stroke
            ..strokeCap = StrokeCap.butt
            ..strokeWidth = 10;

          canvas.drawPath(path, strokePaint);
        
      

      Path _onePath(double radian, Size size) 
        var circleSize = 150.0;
        var center = size.center(Offset.zero);
        var maxSize = math.min(size.height, size.width) / 4;
        var sin = math.sin(radian);
        var cos = math.cos(radian);

        var rect = Rect.fromLTWH(
          center.dx - circleSize / 2 + (sin * maxSize),
          center.dy - circleSize / 2 + (cos * maxSize),
          circleSize,
          circleSize,
        );
        return Path()
          ..addRRect(
            RRect.fromRectAndRadius(
              rect,
              Radius.circular(
                rect.height / 2,
              ),
            ),
          )
          ..close();
      

      @override
      bool shouldRepaint(CustomPainter oldDelegate) => true;
    

【讨论】:

谢谢 Kherel,你的回答很棒。 :))【参考方案2】:

也许你可以使用Alignment(x, y)ClipPath

class Demo extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return Scaffold(
      appBar: AppBar(
        title: Text('Demo'),
      ),
      body: Center(
        child: Container(
          color: Colors.grey,
          width: 200,
          height: 200,
          child: Stack(
            children: [
              Align(
                alignment: Alignment(-0.15, 0.3),
                child: buildCircle(Colors.purpleAccent),
              ),
              Align(
                alignment: Alignment(0.2, 0.3),
                child: buildCircle(Colors.blue),
              ),
              Align(
                alignment: Alignment(0.3, -0.1),
                child: buildCircle(Colors.red),
              ),
              Align(
                alignment: Alignment(0.0, -0.3),
                child: buildCircle(Colors.yellow),
              ),
              Align(
                alignment: Alignment(-0.3, -0.1),
                child: ClipPath(
                  // Clip the green circle.
                  clipper: Clipper(),
                  child: buildCircle(Colors.green),
                ),
              ),
              Align(
                child: buildCircle(Colors.white, width: 30, height: 30),
              ),
            ],
          ),
        ),
      ),
    );
  

  Container buildCircle(
    Color color, 
    double width = 50,
    double height = 50,
  ) 
    return Container(
      alignment: Alignment.center,
      width: width,
      height: height,
      child: InkWell(
        child: new Container(
          width: width,
          height: height,
          padding: const EdgeInsets.all(20.0),
          //I used some padding without fixed width and height
          decoration: new BoxDecoration(
            shape: BoxShape.circle,
            // You can use like this way or like the below line
            //                borderRadius: new BorderRadius.circular(30.0),
            color: color,
          ),
          child: new Text("",
              style: new TextStyle(
                  color: Colors.black,
                  fontSize: 30.0,
                  fontWeight: FontWeight.bold)),
          alignment: Alignment.center,
          // You can add a Icon instead of text also, like below.
          //child: new Icon(Icons.arrow_forward, size: 50.0, color: Colors.black38)),
        ),
      ),
    );
  


class Clipper extends CustomClipper<Path> 
  @override
  Path getClip(Size size) 
    return Path.combine(
      PathOperation.difference,
      Path()
        ..addOval(
          Rect.fromCircle(
            center: size.center(Offset(0, 0)),
            radius: 25.0,
          ),
        ),
      Path()
        ..addOval(
          Rect.fromCircle(
            center: size.center(Offset(10, 30)),
            radius: 25.0,
          ),
        ),
    );
  

  @override
  bool shouldReclip(CustomClipper<Path> oldClipper) => true;

【讨论】:

是的,然后使用 Media.query(context).size.width/height 在 x, y 中插入数字。这样该应用程序也适用于其他屏幕纵横比。 这就是我已经实现并坚持的目标,您是否注意到每个圆圈应该如何与下一个圆圈重叠? @JackShen 您可以通过自定义视图而不是堆栈来做到这一点。@Lilly 我已经使用ClipPath 更新了我的答案,希望对你有所帮助。@Lilly【参考方案3】:

你可以使用FractionalTranslation:

Center(
  child: Container(
    height: 400,
    child: Stack(
      children: <Widget>[
        FractionalTranslation(
          translation: Offset(-0.22, -0.1),
          child: CircleContainer(color: Colors.green),
        ),
        FractionalTranslation(
          translation: Offset(-0.15, 0.2),
          child: CircleContainer(color: Colors.purple),
        ),
        FractionalTranslation(
          translation: Offset(0.15, 0.2),
          child: CircleContainer(color: Colors.blue),
        ),
        FractionalTranslation(
          translation: Offset(0.22, -0.1),
          child: CircleContainer(color: Colors.red),
        ),
        FractionalTranslation(
          translation: Offset(0, -0.25),
          child: CircleContainer(color: Colors.yellow),
        ),
        CircleContainer(color: Colors.white, size: 130),
      ],
    ),
  ),
)
Widget CircleContainer(Color color = Colors.white, double size = 160) 
  return Container(
    alignment: Alignment.center,
    child: InkWell(
      child: new Container(
        width: size,
        height: size,
        padding: const EdgeInsets.all(
          20.0), //I used some padding without fixed width and height
        decoration: new BoxDecoration(
          shape: BoxShape
          .circle, // You can use like this way or like the below line
          //                borderRadius: new BorderRadius.circular(30.0),
          color: color,
        ),
        child: new Text("",
                        style: new TextStyle(
                          color: Colors.black,
                          fontSize: 30.0,
                          fontWeight: FontWeight.bold)),
        alignment: Alignment.center,
// You can add a Icon instead of text also, like below.
//child: new Icon(Icons.arrow_forward, size: 50.0, color: Colors.black38)),
      )));

结果:

【讨论】:

这就是我已经取得并坚持的目标,你注意到每个圆圈应该如何与下一个圆圈重叠吗?

以上是关于无法在颤动中相互堆叠 5 个圆圈的主要内容,如果未能解决你的问题,请参考以下文章

堆叠圆圈在边界半径上产生一个黑条

Skeleton Boilerplate:列相互堆叠,但我希望它们并排?

XAML ListView - 相互堆叠项目

更改代码以允许超过 3 个堆叠条

堆叠分类器无法识别 Keras

在 s-s-rS 中将条相互放在一起,但不要堆叠