Skip to content

Instantly share code, notes, and snippets.

@hukusuke1007
Last active August 25, 2023 23:56
Show Gist options
  • Select an option

  • Save hukusuke1007/3ba8f2b29a0c8195d0e51f9b6754b166 to your computer and use it in GitHub Desktop.

Select an option

Save hukusuke1007/3ba8f2b29a0c8195d0e51f9b6754b166 to your computer and use it in GitHub Desktop.
タコメーター
import 'package:flutter/material.dart';
import 'dart:math';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: EdgeInsets.all(16),
child: AnimatedEngineMeter(targetValue: 0.3),
),
);
}
}
class AnimatedEngineMeter extends StatelessWidget {
const AnimatedEngineMeter({required this.targetValue});
final double targetValue;
@override
Widget build(BuildContext context) {
return TweenAnimationBuilder<double>(
tween: Tween(begin: 0.0, end: targetValue),
duration: const Duration(seconds: 2),
builder: (context, value, child) {
return Tachometer(rpmValue: value);
},
);
}
}
class Tachometer extends StatelessWidget {
const Tachometer({required this.rpmValue});
final double rpmValue; // 0.0から1.0までの値。例:0.5は50%のRPMを示す
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: TachometerPainter(rpmValue),
size: const Size(300, 200), // こちらのサイズは適宜調整してください
);
}
}
class TachometerPainter extends CustomPainter {
const TachometerPainter(this.rpmValue);
final double rpmValue;
@override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height);
final radius = size.width / 2 - 20;
// 半円背景の描画
final bgPaint = Paint()..color = Colors.black;
canvas.drawArc(
Rect.fromCircle(center: center, radius: radius),
pi,
pi,
false,
bgPaint,
);
// 数値メモリとラベルの描画
final memPaint = Paint()
..color = Colors.white
..strokeWidth = 2.0;
final textPainter = TextPainter(
textDirection: TextDirection.ltr,
);
const totalMarks = 9;
const startAngle = 1.1 * pi; // 開始角度を調整
const endAngle = 1.9 * pi; // 終了角度を調整
for (int i = 0; i <= totalMarks; i++) {
// メモリの線を描画
var angle = startAngle + (endAngle - startAngle) * (i / totalMarks);
final start = Offset(
center.dx + (radius - 10) * cos(angle),
center.dy + (radius - 10) * sin(angle),
);
final end = Offset(
center.dx + radius * cos(angle),
center.dy + radius * sin(angle),
);
canvas.drawLine(start, end, memPaint);
// メモリの数値ラベルの描画
textPainter.text = TextSpan(
text: '${i * 20}',
style: const TextStyle(color: Colors.white, fontSize: 12.0),
);
textPainter.layout();
textPainter.paint(
canvas,
Offset(
center.dx + (radius - 30) * cos(angle) - textPainter.width / 2,
center.dy + (radius - 30) * sin(angle) - textPainter.height / 2,
),
);
}
// 針の描画
final angle = startAngle + (endAngle - startAngle) * rpmValue;
final needleEnd = Offset(
center.dx + (radius - 20) * cos(angle),
center.dy + (radius - 20) * sin(angle),
);
final needlePaint = Paint()
..color = Colors.red
..style = PaintingStyle.stroke
..strokeWidth = 5.0;
canvas.drawLine(center, needleEnd, needlePaint);
}
@override
bool shouldRepaint(TachometerPainter oldDelegate) {
print("oldDelegate.rpmValue ${oldDelegate.rpmValue}");
return oldDelegate.rpmValue != rpmValue;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment