The magic widget of Flutter tips

Keywords: Android REST github

It's just to share a little skill of flutter. Of course, it's not only applicable to flutter, but also to other devices like Android. In order to quickly realize the requirements of xx design, we can take out the system's own widget s and change them into our own, with speed and style, but men can't say no!

First, look at the figure. Figure 1 shows the effect of the system. Figure 2 shows what we need to achieve. It's relatively simple. At that time, it was the loading effect on our company's web page. We need to use the shuttle web to realize the reality. We are in a hurry to directly change the control of the system. In other words, we have changed several things, but we can do it with simple pictures and switching states. It's a pity that we didn't use them A kind of

Figure 1
Figure two
Because it's recording screen to gif, the effect is not so good, please wait.

step

  1. Analyze the source code. The circular progress indicator of the system is also drawn by inheriting the CustomPainter. Here is the key code
//In general, use drawArc to draw the figure, and then use animation to change the initial angle and sector
@override
  void paint(Canvas canvas, Size size) {
    final Paint paint = Paint()
      ..color = valueColor
      ..strokeWidth = strokeWidth
      ..style = PaintingStyle.stroke;
    if (backgroundColor != null) {
      final Paint backgroundPaint = Paint()
        ..color = backgroundColor
        ..strokeWidth = strokeWidth
        ..style = PaintingStyle.stroke;
      canvas.drawArc(Offset.zero & size, 0, _sweep, false, backgroundPaint);
    }

    if (value == null) // Indeterminate
      paint.strokeCap = StrokeCap.square;
    canvas.drawArc(Offset.zero & size, arcStart, arcSweep, false, paint);
  }
  1. Analysis of target structure: see the figure above, our goal is that four dots are rotating. OK, let's draw four dots first.
@override
  void paint(Canvas canvas, Size size) {
    valueColorPaint = Paint()
      ..color = valueColor
      ..style = PaintingStyle.fill;

    if (backgroundColor != null) {
      valueColorPaint = Paint()
        ..color = backgroundColor
        ..style = PaintingStyle.fill;
    }

    if (oneLeadColor != null) {
      oneLeadCirclePaint = Paint()
        ..color = oneLeadColor
        ..style = PaintingStyle.fill;
    } else {
      oneLeadCirclePaint = valueColorPaint;
    }

    double dx1 = size.width + size.width * math.cos(arcSweepOne * math.pi / 180);
    double dy1 = size.height + size.width * math.sin(arcSweepOne * math.pi / 180);

    double dx2 = size.width + size.width * math.cos(arcSweepTwo * math.pi / 180);
    double dy2 = size.height + size.width * math.sin(arcSweepTwo * math.pi / 180);

    double dx3 = size.width + size.width * math.cos(arcSweepThree * math.pi / 180);
    double dy3 = size.height + size.width * math.sin(arcSweepThree * math.pi / 180);

    double dx4 = size.width + size.width * math.cos(arcSweepFour * math.pi / 180);
    double dy4 = size.height + size.width * math.sin(arcSweepFour * math.pi / 180);

    canvas.drawCircle(Offset(dx1, dy1), size.width / 2, valueColorPaint);
    canvas.drawCircle(Offset(dx2, dy2), size.width / 2, oneLeadCirclePaint);
    canvas.drawCircle(Offset(dx3, dy3), size.width / 2, valueColorPaint);
    canvas.drawCircle(Offset(dx4, dy4), size.width / 2, valueColorPaint);
  }

Three paints are used to distinguish colors. Because we are drawing circles, we need to determine the center coordinates. This is the key code

double dx1 = size.width + size.width * math.cos(arcSweepOne * math.pi / 180);
double dy1 = size.height + size.width * math.sin(arcSweepOne * math.pi / 180);

Come on, you're welcome. Take a look at the picture first
The square is an auxiliary figure, which determines the position of four circles at the initial position. At this time, the position of the circle is fixed, and then the coordinate of the center of the circle is (x,y) , the position has been confirmed, it's time for the small circle to move, and the path of the center of the small circle is the big circle in the figure. The logic diagram of Shenma has been analyzed, and the rest is mathematical calculation. We calculate based on half of the square, and the path of the center of the circle is the path of the radius of the big circle, the angle of the radius of the big circle If the degree is a variable, you can easily make the small circle move, so the center coordinate of the first quadrant circle is (arcsweetone is the angle that the big circle sweeps)

double dx1 = size.width + size.width * math.cos(arcSweepOne * math.pi / 180);
double dy1 = size.height + size.width * math.sin(arcSweepOne * math.pi / 180);

Then combine Tween animation, dynamically transfer the changed coordinates in, and the position of the small circle will change in real time
Take a look at the complete code, and don't post GitHub, which is a simple small example.

import 'dart:math' as math;

import 'package:flutter/material.dart';

const double _kMinCircularProgressIndicatorSize = 8.0;

class FlowerLoadingIndicator extends ProgressIndicator {
  /// Creates a Flower progress indicator.
  ///
  /// {@macro flutter.material.progressIndicator.parameters}
  const FlowerLoadingIndicator({
    Key key,
    double value,
    Color backgroundColor,
    Animation<Color> valueColor,
    this.oneLeadColor,
    this.milliseconds,
    String semanticsLabel,
    String semanticsValue,
  }) : super(
          key: key,
          value: value,
          backgroundColor: backgroundColor,
          valueColor: valueColor,
          semanticsLabel: semanticsLabel,
          semanticsValue: semanticsValue,
        );

  final Color oneLeadColor;
  final int milliseconds;

  Color getBackgroundColor(BuildContext context) => backgroundColor ?? Theme.of(context).backgroundColor;
  Color getValueColor(BuildContext context) => valueColor?.value ?? Theme.of(context).accentColor;
  Color getOneLeadColor(BuildContext context) => oneLeadColor ?? Theme.of(context).primaryColorLight;

  Widget _buildSemanticsWrapper({
    @required BuildContext context,
    @required Widget child,
  }) {
    String expandedSemanticsValue = semanticsValue;
    if (value != null) {
      expandedSemanticsValue ??= '${(value * 100).round()}%';
    }
    return Semantics(
      label: semanticsLabel,
      value: expandedSemanticsValue,
      child: child,
    );
  }

  @override
  State<StatefulWidget> createState() => _FlowerLoadingIndicator();
}

final Tween<double> _kRotationTweenOne = new Tween(begin: 0.0, end: 360.0);
final Tween<double> _kRotationTweenTwo = new Tween(begin: -90.0, end: 270.0);
final Tween<double> _kRotationTweenThree = new Tween(begin: 90, end: 450.0);
final Tween<double> _kRotationTweenFour = new Tween(begin: 180, end: 540.0);

class _FlowerLoadingIndicator extends State<FlowerLoadingIndicator> with SingleTickerProviderStateMixin {
  AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: Duration(milliseconds: widget.milliseconds ?? 1500),
      vsync: this,
    );
    _controller.repeat();
  }

  @override
  void didUpdateWidget(FlowerLoadingIndicator oldWidget) {
    super.didUpdateWidget(oldWidget);
    _controller.repeat();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return _buildAnimation();
  }

  Widget _buildIndicator(BuildContext context, double arcSweepOne, double arcSweepTwo, double arcSweepThree, double arcSweepFour) {
    return widget._buildSemanticsWrapper(
      context: context,
      child: Container(
        constraints: const BoxConstraints(
          minWidth: _kMinCircularProgressIndicatorSize,
          minHeight: _kMinCircularProgressIndicatorSize,
        ),
        child: CustomPaint(
          painter: _FlowerLoadingIndicatorPainter(
            backgroundColor: widget.backgroundColor,
            valueColor: widget.getValueColor(context),
            oneLeadColor: widget.getOneLeadColor(context),
            arcSweepOne: arcSweepOne,
            arcSweepTwo: arcSweepTwo,
            arcSweepThree: arcSweepThree,
            arcSweepFour: arcSweepFour,
          ),
        ),
      ),
    );
  }

  Widget _buildAnimation() {
    return AnimatedBuilder(
      animation: _controller,
      builder: (BuildContext context, Widget child) {
        return _buildIndicator(
          context,
          _kRotationTweenOne.evaluate(_controller),
          _kRotationTweenTwo.evaluate(_controller),
          _kRotationTweenThree.evaluate(_controller),
          _kRotationTweenFour.evaluate(_controller),
        );
      },
    );
  }
}

class _FlowerLoadingIndicatorPainter extends CustomPainter {
  _FlowerLoadingIndicatorPainter({
    this.backgroundColor,
    this.valueColor,
    this.arcSweepOne,
    this.arcSweepTwo,
    this.arcSweepThree,
    this.arcSweepFour,
    this.oneLeadColor,
  });

  final Color backgroundColor;
  final Color valueColor;
  final Color oneLeadColor;
  final double arcSweepOne;
  final double arcSweepTwo;
  final double arcSweepThree;
  final double arcSweepFour;
  Paint valueColorPaint;
  Paint oneLeadCirclePaint;

  @override
  void paint(Canvas canvas, Size size) {
    valueColorPaint = Paint()
      ..color = valueColor
      ..style = PaintingStyle.fill;

    if (backgroundColor != null) {
      valueColorPaint = Paint()
        ..color = backgroundColor
        ..style = PaintingStyle.fill;
    }

    if (oneLeadColor != null) {
      oneLeadCirclePaint = Paint()
        ..color = oneLeadColor
        ..style = PaintingStyle.fill;
    } else {
      oneLeadCirclePaint = valueColorPaint;
    }

    double dx1 = size.width + size.width * math.cos(arcSweepOne * math.pi / 180);
    double dy1 = size.height + size.width * math.sin(arcSweepOne * math.pi / 180);

    double dx2 = size.width + size.width * math.cos(arcSweepTwo * math.pi / 180);
    double dy2 = size.height + size.width * math.sin(arcSweepTwo * math.pi / 180);

    double dx3 = size.width + size.width * math.cos(arcSweepThree * math.pi / 180);
    double dy3 = size.height + size.width * math.sin(arcSweepThree * math.pi / 180);

    double dx4 = size.width + size.width * math.cos(arcSweepFour * math.pi / 180);
    double dy4 = size.height + size.width * math.sin(arcSweepFour * math.pi / 180);

    canvas.drawCircle(Offset(dx1, dy1), size.width / 2, valueColorPaint);
    canvas.drawCircle(Offset(dx2, dy2), size.width / 2, oneLeadCirclePaint);
    canvas.drawCircle(Offset(dx3, dy3), size.width / 2, valueColorPaint);
    canvas.drawCircle(Offset(dx4, dy4), size.width / 2, valueColorPaint);
  }

  @override
  bool shouldRepaint(_FlowerLoadingIndicatorPainter oldPainter) {
    return true;
  }
}

A little white And growing up on the road of Technology

26 original articles published, 7 praised, 20000 visitors+
Private letter follow

Posted by johnpdmccall on Tue, 03 Mar 2020 00:18:03 -0800