Flutter画圆/虚线圆/渐变圆/进度圆

同一颜色的圆

例子样式:

废话不多说 直接上代码,注释清楚!

1.调用该方法 如果只要一个圆,赋值completeColor, completeWidth, completePercent就可以,剩下的不用赋值 就是一个圆
2.如果需要俩圆的话直接调用赋值就行
3.如果只需要一个虚线圆的话,赋值isDividerRound = true; ,lineColor, width, 剩下的不用写,completeWidth 不得大于0
这是一个全圆就是比例是百分之百的圆,可以自行修改

/*
 * 如果只有一个圆的情况下,请设置已完成的圆  默认的圆的颜色不要设置
 */
import 'dart:math';

import 'package:flutter/cupertino.dart';

class MyPainter extends CustomPainter {
  //默认的线的背景颜色
  Color? lineColor;
  //默认的线的宽度
  double width;
  //已完成线的颜色
  Color completeColor;
  //已完成的百分比
  double completePercent;
  //已完成的线的宽度
  double completeWidth;
  // 从哪开始 1从下开始, 2 从上开始 3 从左开始 4 从右开始  默认从下开始
  double startType;
  //是不是虚线的圈
  bool isDividerRound;
  //中间的实圆 统计线条是不是渐变的圆
  bool isGradient;
  //结束的位置
  double endAngle;
  //默认的线的背景颜色
  List<Color> lineColors;
  //实心圆阴影颜色
  // Color shadowColor;
  //渐变圆  深色在下面 还是在左面  默认在下面
  bool isTransform;

  MyPainter({
    required this.lineColor,
    required this.completeColor,
    required this.completePercent,
    required this.width,
    required this.completeWidth,
    this.startType = 1,
    this.isDividerRound = false,
    this.isGradient = false,
    this.endAngle = pi * 2,
    required this.lineColors,
    this.isTransform = false,
    // this.shadowColor,
  });

  @override
  void paint(Canvas canvas, Size size) {
    Offset center = Offset(size.width / 2, size.height / 2); //  坐标中心
    double radius = min(size.width / 2, size.height / 2); //  半径

    //是否有第二层圆
    if (lineColor != null) {
      //是不是 虚线圆
      if (isDividerRound) {
        //背景的线
        Paint line = Paint()
          ..color = lineColor!
        // ..strokeCap = StrokeCap.round
          ..style = PaintingStyle.stroke
          ..isAntiAlias = true //抗锯齿
          ..strokeWidth = width;

        double i = 0.00;
        while (i < pi * 2) {
          canvas.drawArc(Rect.fromCircle(center: center, radius: radius), i,
              0.04, false, line);
          i = i + 0.08;
        }
      } else {
        //背景的线  实线
        Paint line = Paint()
          ..color = lineColor!
          ..strokeCap = StrokeCap.round
          ..style = PaintingStyle.stroke
          ..strokeWidth = width;

        canvas.drawCircle(
          //  画圆方法
            center,
            radius,
            line);
      }
    }
    //画上面的圆
    if (completeWidth > 0) {
      double arcAngle = 2 * pi * (completePercent / 100);

      // 从哪开始 1从下开始, 2 从上开始 3 从左开始 4 从右开始  默认从下开始
      double start = pi / 2;
      if (startType == 2) {
        start = -pi / 2;
      } else if (startType == 3) {
        start = pi;
      } else if (startType == 4) {
        start = pi * 2;
      }

      //创建画笔
      Paint paint = Paint()
        ..strokeCap = StrokeCap.round
        ..style = PaintingStyle.stroke
        ..strokeWidth = completeWidth;

      ///是渐变圆
      if (isGradient == true) {
        //渐变圆 深色位置偏移量  默认深色在下面
        double transform;
        if (isTransform == false) {
          //深色在下面
          transform = -pi / 2;
        } else {
          //深色在左面
          transform = pi * 2;
        }
        paint.shader = SweepGradient(
          startAngle: 0.0,
          endAngle: pi * 2,
          colors: lineColors,
          tileMode: TileMode.clamp,
          transform: GradientRotation(transform),
        ).createShader(
          Rect.fromCircle(center: center, radius: radius),
        );

        canvas.drawArc(Rect.fromCircle(center: center, radius: radius), start,
            arcAngle, false, paint);
      } else {
        ///是实体圆
        paint.color = completeColor;
        canvas.drawArc(
          Rect.fromCircle(center: center, radius: radius),
          start, //  -pi / 2,从正上方开始  pi / 2,从下方开始
          arcAngle,
          false,
          paint,
        );
      }
    }
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}
实践示例

我们用 Flutter 新建项目的例子代码来演示,如下:

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  // This widget is the home page of your application. It is stateful, meaning
  // that it has a State object (defined below) that contains fields that affect
  // how it looks.

  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked "final".

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      // This call to setState tells the Flutter framework that something has
      // changed in this State, which causes it to rerun the build method below
      // so that the display can reflect the updated values. If we changed
      // _counter without calling setState(), then the build method would not be
      // called again, and so nothing would appear to happen.
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    // This method is rerun every time setState is called, for instance as done
    // by the _incrementCounter method above.
    //
    // The Flutter framework has been optimized to make rerunning build methods
    // fast, so that you can just rebuild anything that needs updating rather
    // than having to individually change instances of widgets.
    return Scaffold(
      appBar: AppBar(
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text(widget.title),
      ),
      body: Center(
        // Center is a layout widget. It takes a single child and positions it
        // in the middle of the parent.
        child: Column(
          // Column is also a layout widget. It takes a list of children and
          // arranges them vertically. By default, it sizes itself to fit its
          // children horizontally, and tries to be as tall as its parent.
          //
          // Invoke "debug painting" (press "p" in the console, choose the
          // "Toggle Debug Paint" action from the Flutter Inspector in Android
          // Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
          // to see the wireframe for each widget.
          //
          // Column has various properties to control how it sizes itself and
          // how it positions its children. Here we use mainAxisAlignment to
          // center the children vertically; the main axis here is the vertical
          // axis because Columns are vertical (the cross axis would be
          // horizontal).
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
            CustomPaint(
              size: const Size(200, 200),

              foregroundPainter: MyPainter(
                lineColor: const Color(0xFF000000),
                lineColors: [const Color(0xFF000000), const Color(0xFFFFFFFF)],
                completeWidth: 10,
                width: 6,
                completePercent: 90,
                completeColor: const Color(0xFFFF000F),
              ),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}
渐变色的圆

代码看看就会了

import 'dart:math';

import 'package:flutter/cupertino.dart';

///渐变圆
class MyGradientPainter extends CustomPainter {
  //默认的线的背景颜色
  List<Color> lineColor;
  //默认的线的宽度
  double width;
  double endAngle;

  MyGradientPainter({
    required this.lineColor,
    required this.width,
    this.endAngle = pi * 2,
  });

  @override
  void paint(Canvas canvas, Size size) {
    Offset center = Offset(size.width / 2, size.height / 2); //  坐标中心
    double radius = min(size.width / 2, size.height / 2); //  半径

    var paint = Paint()
      ..strokeCap = StrokeCap.round
      ..style = PaintingStyle.stroke
      ..isAntiAlias = true //抗锯齿
      ..strokeWidth = width;

    paint.shader = SweepGradient(
      startAngle: 0.0,
      endAngle: pi * 2,
      colors: lineColor,
      transform: const GradientRotation(pi * 1.2),
    ).createShader(
      Rect.fromCircle(center: center, radius: radius),
    );

    canvas.drawArc(Rect.fromCircle(center: center, radius: radius), 0, 2 * pi,
        false, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}
实践示例

我们用 Flutter 新建项目的例子代码来演示,如下:

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  // This widget is the home page of your application. It is stateful, meaning
  // that it has a State object (defined below) that contains fields that affect
  // how it looks.

  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked "final".

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      // This call to setState tells the Flutter framework that something has
      // changed in this State, which causes it to rerun the build method below
      // so that the display can reflect the updated values. If we changed
      // _counter without calling setState(), then the build method would not be
      // called again, and so nothing would appear to happen.
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    // This method is rerun every time setState is called, for instance as done
    // by the _incrementCounter method above.
    //
    // The Flutter framework has been optimized to make rerunning build methods
    // fast, so that you can just rebuild anything that needs updating rather
    // than having to individually change instances of widgets.
    return Scaffold(
      appBar: AppBar(
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text(widget.title),
      ),
      body: Center(
        // Center is a layout widget. It takes a single child and positions it
        // in the middle of the parent.
        child: Column(
          // Column is also a layout widget. It takes a list of children and
          // arranges them vertically. By default, it sizes itself to fit its
          // children horizontally, and tries to be as tall as its parent.
          //
          // Invoke "debug painting" (press "p" in the console, choose the
          // "Toggle Debug Paint" action from the Flutter Inspector in Android
          // Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
          // to see the wireframe for each widget.
          //
          // Column has various properties to control how it sizes itself and
          // how it positions its children. Here we use mainAxisAlignment to
          // center the children vertically; the main axis here is the vertical
          // axis because Columns are vertical (the cross axis would be
          // horizontal).
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
            CustomPaint(
              size: const Size(200, 200),

              foregroundPainter: MyGradientPainter(
                  width: 8, lineColor: [const Color(0xFF000000),const Color(0xFFFFFFFF)]),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

参考链接


发布者

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注