【问题标题】:How can I customize / rotate a BoxDecoration for a Container widget in Flutter?如何在 Flutter 中为 Container 小部件自定义/旋转 BoxDecoration?
【发布时间】:2018-12-12 02:15:54
【问题描述】:

我有一个小部件,它为公交车站构建了一个圆形的个人资料图片,到目前为止,它有一个围绕个人资料图片的圆形边框,就像这样。我想将圆形边框改为虚线,并通过以虚线形式围绕个人资料图片旋转/旋转来制作动画。有什么简单的方法可以做到这一点吗?非常感谢您提供的任何帮助!

return new Transform(
  transform: new Matrix4.diagonal3Values(
    animation.avatarSize.value,
    animation.avatarSize.value,
    1.0,
  ),
  alignment: Alignment.center,
  child: new Container(
    width: 110.0,
    height: 110.0,
    decoration: new BoxDecoration(
      shape: BoxShape.circle,
      border: new Border.all(
        color: Colors.white30,
      ),
    ),
    margin: const EdgeInsets.only(
      top: 24.0,
      left: 16.0,
    ),
    padding: const EdgeInsets.all(3.0),
    child: new ClipOval(
      child: new Image.asset(
        stopName.avatar,
      ),
    ),
  ),
);

【问题讨论】:

    标签: dart flutter


    【解决方案1】:

    不幸的是,简单的答案是没有简单的方法可以做到这一点。 Flutter 人以其无限的智慧得出结论,虚线的性能不足以包含在 Flutter 中,因此没有人需要画虚线。 (是的,这句话中的逻辑不连续性是有意的。不要误会我的意思——我喜欢颤振,开发人员做得很好,但他们似乎确实根据性能而不是功能做出了一些半武断的决定)。

    我看到的解释是,基本上 C++ 版本会做一些类似于 dart 代码的事情(除非它直接在 GPU 上完成),所以他们希望有人最终会在 dart 中实现虚线(可能作为图书馆的一部分?)。有关进展和讨论,请参阅this github bug...如果您想在未来看到破折号,请 +1。如果有足够多的人这样做,那么最终 Flutter 的人可能会决定实施它。

    但就目前而言,这意味着没有简单的方法可以用虚线制作 CircleBorder 或任何其他类型的边框。

    但是,尽最大的努力,完全可以实现您想要的 =)。下面是为你做你想要的代码的快速剪辑。请注意 - 它不是很优化,可能有更简单的方法可以做到这一点,您可能可以使用装饰并在那里实现油漆等......但这确实有效。

    import 'dart:math';
    
    import 'package:flutter/material.dart';
    
    void main() => runApp(new MyApp());
    
    class MyApp extends StatefulWidget {
      @override
      _MyAppState createState() => _MyAppState();
    }
    
    class _MyAppState extends State<MyApp> {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: new SafeArea(
            child: Column(
              children: [
                new DashedCircle(
                  child: new ClippedDrawing(),
                )
              ],
            ),
          ),
        );
      }
    }
    
    class ClippedDrawing extends StatelessWidget {
      @override
      Widget build(BuildContext context) => new ClipOval(
            child: new Container(
              color: Colors.red,
            ),
          );
    }
    
    class DashedCircle extends StatefulWidget {
      final Widget child;
    
      const DashedCircle({Key key, this.child}) : super(key: key);
    
      @override
      DashedBorderState createState() => new DashedBorderState();
    }
    
    class DashedBorderState extends State<DashedCircle> with TickerProviderStateMixin<DashedCircle> {
      AnimationController controller;
      Animation<double> animation;
    
      @override
      void initState() {
        super.initState();
        controller = new AnimationController(vsync: this, duration: Duration(seconds: 10));
        animation = new Tween(begin: 0.0, end: pi * 2.0).animate(controller);
        controller.repeat();
      }
    
      @override
      Widget build(BuildContext context) {
        return AnimatedBuilder(
          animation: animation,
          builder: (context, child) {
            return new CustomPaint(
              painter: OvalPainter(
                  color: Colors.blue, borderWidth: 1.0, dashLength: 5.0, spaceLength: 2.0, offset: animation.value),
              child: child,
            );
          },
          child: Container(
            width: 110.0,
            height: 110.0,
            padding: EdgeInsets.all(3.0),
            child: widget.child,
          ),
        );
      }
    }
    
    class OvalPainter extends CustomPainter {
      final Color color;
      final double borderWidth;
      final double dashLength;
      final double spaceLength;
      final double offset;
    
      OvalPainter(
          {@required this.borderWidth,
          @required this.dashLength,
          @required this.spaceLength,
          @required this.offset,
          @required this.color});
    
      double lastShortestSide;
      double lastDashLength;
      double lastSpaceLength;
    
      Path lastPath;
    
      @override
      void paint(Canvas canvas, Size size) {
        Rect rect = Offset.zero & size;
    
        var radius = rect.shortestSide / 2;
    
        canvas.translate(radius, radius);
        canvas.rotate(offset);
    
        Path path;
        if (lastShortestSide == rect.shortestSide &&
            dashLength == lastDashLength &&
            spaceLength == lastSpaceLength &&
            lastPath != null) {
          path = lastPath;
        } else {
          path = _getDashedCircularPath(rect.shortestSide / 2, dashLength, spaceLength);
          lastPath = path;
          lastShortestSide = rect.shortestSide;
          lastDashLength = dashLength;
          lastSpaceLength = spaceLength;
        }
    
        canvas.drawPath(
          path,
          new Paint()
            ..style = PaintingStyle.stroke
            ..color = color
            ..strokeWidth = borderWidth,
        );
      }
    
      @override
      bool shouldRepaint(OvalPainter oldDelegate) {
        return offset != oldDelegate.offset ||
            color != oldDelegate.color ||
            borderWidth != oldDelegate.borderWidth ||
            dashLength != oldDelegate.dashLength ||
            spaceLength != oldDelegate.spaceLength;
      }
    
      static Path _getDashedCircularPathFromNumber(double radius, int numSections, double dashPercentage) {
        var tau = 2 * pi;
        var actualTotalLength = tau / numSections;
        var actualDashLength = actualTotalLength * dashPercentage;
    
        double offset = 0.0;
        Rect rect = new Rect.fromCircle(center: Offset.zero, radius: radius);
    
        Path path = new Path();
        for (int i = 0; i < numSections; ++i) {
          path.arcTo(rect, offset, actualDashLength, true);
          offset += actualTotalLength;
        }
    
        return path;
      }
    
      static Path _getDashedCircularPath(double radius, double dashLength, double spaceLength) {
        // first, find number of radians that dashlength + spacelength take
        var tau = 2 * pi;
        var circumference = radius * tau;
        var dashSpaceLength = dashLength + spaceLength;
        var num = circumference / (dashSpaceLength);
        // we'll floor the value so that it's close-ish to the same amount as requested but
        // instead will be the same all around
        var closeNum = num.floor();
    
        return _getDashedCircularPathFromNumber(radius, closeNum, dashLength / dashSpaceLength);
      }
    }
    

    【讨论】:

    • 非常感谢您的详细回复!您在前一周回答了我的另一个 Flutter 问题——感谢您的社区贡献!
    猜你喜欢
    • 2022-06-24
    • 2020-08-22
    • 2020-12-07
    • 2019-11-25
    • 2018-10-01
    • 2019-09-26
    • 2019-04-01
    • 2020-12-12
    • 1970-01-01
    相关资源
    最近更新 更多