CustomPainter
화면을 직접 그릴 때 사용한다. 기존의 UI로 만들기 어려운 화면을 만들고 싶을 때 유용하다.
Canvas : 그림을 그리는 동작이 기록된다.
Paint : 그림의 특성들을 저장한다.
CustomPainter : Canvas, Paint를 담아낸다.
CustomPaint : CustomPainter의 상위 위젯이다.
CustomPainter 구현
class MyPainter extends CustomPainter{
@override
void paint(Canvas canvas, Size size) {
Paint paint = Paint() // Paint 클래스는 어떤 식으로 화면을 그릴지 정할 때 사용
..color = Colors.deepPurpleAccent // 선의 색
..strokeCap = StrokeCap.round // 선의 끝 모양
..strokeWidth = 4.0; // 선의 굵기
Offset p1 = const Offset(0.0, 0.0); // 선을 그리기 위한 좌표값
Offset p2 = Offset(size.width, size.height);
canvas.drawLine(p1, p2, paint); // 선을 그림
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}
CustomPainter는 paint와 shouldRepaint를 구현해야한다.
paint는 화면을 그릴 때 사용하고 shouldRepaint는 화면을 새로 그릴지 말지 정한다.
canvas 객체를 사용하여 화면을 그린다.
CustomPaint 위젯
class PainterPage extends StatelessWidget {
const PainterPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('custom painter'),
),
body: CustomPaint(
size: const Size(200, 200), // 위젯의 크기를 정한다.
painter: MyPainter(), // painter에 그리기를 담당할 클래스를 넣는다.
),
);
}
}
CustomPaint는 위젯의 크기를 정하고 어떤 painter를 사용할지 결정한다.
painter의 종류로 foregroundPainter, painter가 있다.
두 painter는 그려지는 순서의 차이가 있다.
painter : painter -> child 순으로 그려진다. (나중에 그려지는 위젯이 맨앞에 존재)
class PainterPage extends StatelessWidget {
const PainterPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('custom painter'),
),
body: CustomPaint(
size: const Size(200, 200), // 위젯의 크기를 정한다.
painter: MyPainter(), // painter에 그리기를 담당할 클래스를 넣는다.
child: Container(
width: 200,
height: 200,
color: Colors.red,
),
),
);
}
}
painter가 먼저 그려지므로 child 위젯에 가려져서 painter가 그리는 선이 안보인다.
foregroundPainter : child -> foregroundPainter 순으로 그려진다.
CustomPaint(
size: const Size(200, 200), // 위젯의 크기를 정한다.
foregroundPainter: MyPainter(), // painter에 그리기를 담당할 클래스를 넣는다.
child: Container(
width: 200,
height: 200,
color: Colors.red,
),
),
foregroundPainter가 늦게 그려지므로 앞에 존재한다.
CustomPaint 위젯의 크기는 부모 -> 자식 -> CustomPaint 속성의 size 순으로 따라간다.
간단한 차트 그려보기
bar_chart.dart
class BarChart extends CustomPainter{
double percentage = 0;
Color fillColor;
Color remainColor;
BarChart({required this.percentage, required this.fillColor, this.remainColor = Colors.grey});
@override
void paint(Canvas canvas, Size size) {
double totalLength = size.width;
double fillLength = size.width*(percentage/100);
drawLines(canvas, 0, fillLength, fillColor);
drawLines(canvas, fillLength, totalLength, remainColor);
drawText(canvas, '$percentage', fillLength);
}
void drawLines(Canvas canvas, double startX, double endX, Color lineColor){
Paint paint = Paint()
..color = lineColor
..strokeWidth = 20
..style = PaintingStyle.fill;
Offset p1 = Offset(startX, 0);
Offset p2 = Offset(endX, 0);
canvas.drawLine(p1, p2, paint);
}
void drawText(Canvas canvas, String text, double endX) {
TextSpan sp = TextSpan(style: TextStyle(fontSize: 15, color: Colors.white), text: text); // TextSpan은 Text위젯과 거의 동일하다.
TextPainter tp = TextPainter(text: sp, textDirection: TextDirection.ltr);
tp.layout(); // 필수! 텍스트 페인터에 그려질 텍스트의 크기와 방향를 정함.
double dx = endX - tp.width;
double dy = - tp.height / 2;
Offset offset = Offset(dx, dy);
tp.paint(canvas, offset);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}
circle_chart.dart
class CircleChart extends CustomPainter {
List list;
final Paint _paint = Paint()..isAntiAlias = true;
CircleChart({required this.list});
@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);
double startRadian = -pi / 2;
for (var i = 0; i < list.length; i++) {
var item = list[i];
double sweepRadian = (item['rate'] / 100) * 2 * pi;
_paint.color = item['color'];
canvas.drawArc(Rect.fromCircle(center: center, radius: radius),
startRadian, sweepRadian, true, _paint);
startRadian += sweepRadian;
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}
home_page.dart
- bar_chart 관련
Widget _barGraphLabel(String text){
return Text(text);
}
Widget _nutrientContainer(){
List _list = [
{"name": '탄수화물', "rate": 24.00, "color": Colors.indigo},
{"name": '단백질', "rate": 65.00, "color": Colors.indigoAccent},
{"name": '지방', "rate": 51.00, "color": Colors.blueAccent},
{"name": '총 식이섬유', "rate": 24.00, "color": Colors.teal},
{"name": '콜레스테롤', "rate": 48.00, "color": Colors.cyan},
{"name": '총 포화 지방산', "rate": 48.00, "color": Colors.cyanAccent},
];
return SizedBox(
width: double.infinity,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('다량영양소'),
const SizedBox(height: 24),
...List.generate(_list.length, (index) => _barGraph(_list[index]['name'], _list[index]['rate'], _list[index]['color'])),
],
),
);
}
Widget _barGraph(String label, double percentage, Color fillColor){
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_barGraphLabel(label),
const SizedBox(height: 5),
_barGraphRow(percentage: percentage, fillColor: fillColor),
const SizedBox(height: 10),
],
);
}
Widget _barGraphRow({required double percentage, required fillColor}){
return Row(
children: [
Expanded(
child: CustomPaint(
painter: BarChart(percentage: percentage, fillColor: fillColor),
)
),
const SizedBox(width: 10),
const Text('100%')
],
);
}
- circle_chart 관련
Widget _circleChart(){
List _list = [
{"name": '탄수화물', "rate": 28.30, "color": Colors.orange},
{"name": '단백질', "rate": 35.85, "color": Colors.purple},
{"name": '지방', "rate": 35.85, "color": Colors.green},
];
return CustomPaint(
size: const Size(150, 150),
painter: CircleChart(list: _list),
);
}
아직 circle_chart에 text는 그리지 않았다... 텍스트가 그려질 좌표를 어떻게 할지 생각중
'flutter' 카테고리의 다른 글
BoxConstraints forces an infinite width (0) | 2022.05.26 |
---|---|
IntrinsicWidth, UnconstrainedBox (0) | 2022.05.23 |
TextStyle 정의하기 (0) | 2022.05.19 |
flutter + firebase 연동하기 (0) | 2022.05.13 |
flutter listview (flutter codlabs) (0) | 2022.05.10 |