同一颜色的圆
例子样式:
废话不多说 直接上代码,注释清楚!
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. ); } }