简体   繁体   中英

How can I customize / rotate a BoxDecoration for a Container widget in Flutter?

I have a Widget that builds a circular profile picture for bus stops, and as of right now it has a circular border surrounding the profile picture like such. I want to change the circular border to be dashed instead and to also animate by circling/rotating around the profile picture in its dashed form. Is there any easy way to do that? Thanks so much for any help you can provide!

圆形轮廓图片

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,
      ),
    ),
  ),
);

Unfortunately the simple answer is that there is no easy way to do that. The flutter people in their infinite wisdom have made the conclusion that dashed lines are not performant enough to be included in flutter and therefore no-one will ever need to draw a dashed line. (Yes, the logical discontinuity in that sentence is intended. Don't get me wrong - I love flutter and the devs have done a great job, but they do seem to have made a few semi-arbitrary decisions based on performance rather than functionality).

The interpretation I've seen of why is that basically the C++ version would be doing something similar to dart code (unless it were done directly on the GPU), and so they expect someone to eventually implement dashed lines in dart instead (possibly as part of a library?). See this github bug for progress and discussion... and +1 it if you want to see dashes in future. If enough people do that then eventually the flutter people may decide to implement it.

But for now, that means that there is no easy way to make CircleBorder, or any other type of border, with a dashed line.

However, with maximum effort , exactly what you want can be achieved =). Below is a quick cut of the code for you that does what you want. Be warned - it isn't very optimized, and there are probably easier ways to do it, and you could probably use a Decoration and implement the paint etc there or something... but this does work.

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);
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM