版權聲明:本文爲博主原創文章,轉載請註明出處!git
今天跟你們分享的是用Flutter來實現的自定義餅狀圖,下面來看看效果! github
經過點擊左右兩側的按鈕,能夠實現扇形切換,被選中的扇形有個放大的效果,中間的百分比也會隨之變化。 接下來咱們就來看看是怎麼實現的吧? Flutter中有Paint、Canvas等Api,其使用方式和Android中的大同小異, 首先咱們須要自定義一個類,而後extends CustomPainter,而後在其實現方法中寫咱們的邏輯,直接上代碼吧class MyView extends CustomPainter{
//中間文字
var text='111';
bool isChange=false;
//當前選中的扇形
var currentSelect=0;
//畫筆
Paint _mPaint;
Paint TextPaint;
// 扇形大小
int mWidth, mHeight;
// 圓半徑
num mRadius, mInnerRadius,mBigRadius;
// 扇形起始弧度(Andorid中是角度)
num mStartAngle = 0;
// 矩形(扇形繪製的區域)
Rect mOval,mBigOval;
// 扇形 數據
List<PieData> mData;
PieData pieData;
// 構造函數,接受須要的參數值
MyView(this.mData,this.pieData,this.currentSelect,this.isChange);
/**
* 重寫 paint方法,在其中寫繪製餅狀圖的邏輯
*/
@override
void paint(Canvas canvas, Size size) {
// 初始化各種工具等
_mPaint = new Paint();
TextPaint = new Paint();
mHeight=100;mWidth=100;
/// 生成縱軸文字的TextPainter
TextPainter textPainter = TextPainter(
textDirection: TextDirection.ltr,
maxLines: 1,
);
// 文字畫筆 風格定義
TextPainter _newVerticalAxisTextPainter(String text) {
return textPainter
..text = TextSpan(
text: text,
style: new TextStyle(
color: Colors.black,
fontSize: 10.0,
),
);
}
// 正常半徑
mRadius = 50.0;
//加大半徑 用來繪製被選中的扇形區域
mBigRadius=55.0;
//內園半徑
mInnerRadius = mRadius * 0.50;
// 未選中的扇形繪製的矩形區域
mOval = Rect.fromLTRB(-mRadius, -mRadius, mRadius, mRadius);
// 選中的扇形繪製的矩形區域
mBigOval = Rect.fromLTRB(-mBigRadius, -mBigRadius, mBigRadius,
mBigRadius);
//當沒有數據時 直接返回
if (mData.length == null || mData.length <= 0) {
return;
}
///繪製邏輯與Android差很少
canvas.save();
// 將座標點移動到View的中心
canvas.translate(50.0, 50.0);
// 1. 畫扇形
num startAngle = 0.0;
for (int i = 0; i < mData.length; i++) {
PieData p = mData[i];
double hudu=p.percentage;
//計算當前偏移量(單位爲弧度)
double sweepAngle = 2*pi*hudu;
//畫筆的顏色
_mPaint..color = p.color;
if(currentSelect>=0 && i==currentSelect){
//若是當前爲所選中的扇形 則將其半徑加大 突出顯示
canvas.drawArc(mBigOval, startAngle, sweepAngle, true, _mPaint);
}else{
// 繪製沒被選中的扇形 正常半徑
canvas.drawArc(mOval, startAngle, sweepAngle, true, _mPaint);
}
//計算每次開始繪製的弧度
startAngle += sweepAngle ;
}
// canvas.drawRect(mOval, _mPaint); // 矩形區域
// 2.畫內圓
_mPaint..color = Colors.white;
canvas.drawCircle(Offset.zero, mInnerRadius, _mPaint);
canvas.restore();
//當前百分比值
double percentage = pieData.percentage*100;
// 繪製文字內容
var texts ='${percentage}%';
var tp = _newVerticalAxisTextPainter(texts)..layout();
// Text的繪製起始點 = 可用寬度 - 文字寬度 - 左邊距
var textLeft = 35.0;
tp.paint(canvas, Offset(textLeft, 50.0 - tp.height / 2));
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
複製代碼
這樣咱們就實現了自定義餅狀圖了,其實也不復雜對吧,canvas
而後咱們在Widget中實現它bash
import 'dart:math';
///自定義 餅狀圖
/// @author yinl
class MyCustomCircle extends StatelessWidget{
//數據源
List<PieData> datas;
//當前數據對象
PieData data;
var dataSize;
//當前選中
var currentSelect;
MyCustomCircle(this.datas,this.data,this.currentSelect);
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: MyView(datas,data,currentSelect,true)
);
}
}
複製代碼
接下來,我將餅狀圖放入到一個卡片佈局當中進行顯示less
import 'package:flutter/material.dart';
import 'MyCustomCircle.dart';
import 'PieData.dart';
import 'dart:ui';
class DemoPage extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return new DemoPageState();
}
}
class DemoPageState extends State<DemoPage> {
//數據源 下標 表示當前是PieData哪一個對象
int subscript = 0;
//數據源
List<PieData> mData;
//傳遞值
PieData pieData;
//當前選中
var currentSelect = 0;
///初始化 控制器
@override
void initState() {
// TODO: implement initState
super.initState();
//初始化 扇形 數據
initData();
}
@override
Widget build(BuildContext context) {
return _buildHeader();
}
/// 構建佈局(這裏還作了其它的嘗試,因此佈局能夠進行優化,好比按鈕處使用的Column,這裏能夠在按鈕下方再添加文字啥的,根據各自需求來改變就行)
Widget _buildHeader() {
// 卡片的中間顯示咱們自定義的餅狀圖
return new Container(
color: Color(0xfff4f4f4),
height: 200.0,
width: 200.0,
child: new Card(
margin: const EdgeInsets.all(50.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// 左側按鈕
new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
new IconButton(
icon: new Icon(Icons.arrow_left),
color: Colors.green[500],
onPressed: _left,
),
],
),
// 自定義的餅狀圖
new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
new Container(
width: 90.0,
height: 90.0,
padding: const EdgeInsets.only(bottom: 20.0),
/// 使用咱們自定義的餅狀圖 ,並傳入相應的參數
child: new MyCustomCircle(mData, pieData, currentSelect),
),
],
),
// 右側按鈕
new Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
new IconButton(
icon: new Icon(Icons.arrow_right),
color: Colors.green[500],
onPressed: _changeData,
),
],
),
//
],
),
),
);
}
///點擊按鈕時 改變顯示的內容
void _left() {
setState(() {
if (subscript > 0) {
--subscript;
--currentSelect;
}
pieData = mData[subscript];
});
}
///改變餅狀圖
void _changeData() {
setState(() {
if (subscript < mData.length) {
++subscript;
++currentSelect;
}
pieData = mData[subscript];
});
}
//初始化數據源
void initData() {
mData = new List();
PieData p1 = new PieData();
p1.name = 'A';
p1.price = 'a';
p1.percentage = 0.1932;
p1.color = Color(0xffff3333);
pieData = p1;
mData.add(p1);
PieData p2 = new PieData();
p2.name = 'B';
p2.price = 'b';
p2.percentage = 0.15;
p2.color = Color(0xffccccff);
mData.add(p2);
PieData p3 = new PieData();
p3.name = 'C';
p3.price = 'c';
p3.percentage = 0.1132;
p3.color = Color(0xffCD00CD);
mData.add(p3);
PieData p4 = new PieData();
p4.name = 'D';
p4.price = 'd';
p4.percentage = 0.0868;
p4.color = Color(0xffFFA500);
mData.add(p4);
PieData p5 = new PieData();
p5.name = 'E';
p5.price = 'e';
p5.percentage = 0.18023;
p5.color = Color(0xff40E0D0);
mData.add(p5);
PieData p6 = new PieData();
p6.name = 'F';
p6.price = 'f';
p6.percentage = 0.12888;
p6.color =Color(0xffFFFF00);
mData.add(p6);
PieData p7 = new PieData();
p7.name = 'G';
p7.price = 'g';
p7.percentage = 0.0888;
p7.color = Color(0xff00ff66);
mData.add(p7);
PieData p8 = new PieData();
p8.name = 'H';
p8.price = 'h';
p8.percentage = 0.06;
p8.color = Color(0xffD9D9D9);
mData.add(p8);
}
}
複製代碼
接下來就是PieData 的定義ide
import 'package:flutter/material.dart';
class PieData{
String name;// 名稱
Color color;// 顏色
num percentage;//百分比
var price;//成交額
}
複製代碼
好了,全部實現餅狀圖的代碼都在這啦,你們能夠根據需求來進行擴展便可,文字寫的並很少,基本上都在代碼中寫上了註釋,但願你可以喜歡,若是以爲還行,能夠點點喜歡和關注,會有更多好玩的東西帶給你哦。 後續代碼我也會上傳到Github上,若是急需,留言給我就好,我私發給你哈! 謝謝觀看!函數
代碼來啦Github傳送門 喜歡的話,麻煩點點star哦!工具