m3_lightmeter/lib/screens/timer/screen_timer.dart
2024-04-30 16:32:01 +02:00

211 lines
5.5 KiB
Dart

import 'dart:math';
import 'package:flutter/material.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/res/dimens.dart';
import 'package:material_color_utilities/material_color_utilities.dart';
class TimerScreen extends StatelessWidget {
const TimerScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
centerTitle: true,
elevation: 0,
title: Text(
'Test',
style: TextStyle(
color: Theme.of(context).colorScheme.onSurface,
fontSize: Dimens.grid24,
),
),
actions: [const CloseButton()],
),
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox.fromSize(
size: Size.square(MediaQuery.sizeOf(context).width - Dimens.paddingL * 4),
child: _Timer(
remainingSeconds: 5,
timerLength: 124,
),
),
],
),
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: FloatingActionButton(
onPressed: () {},
),
);
}
}
class _Timer extends StatelessWidget {
final int remainingSeconds;
final int timerLength;
const _Timer({
required this.remainingSeconds,
required this.timerLength,
});
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: _TimelinePainter(
backgroundColor: ElevationOverlay.applySurfaceTint(
Theme.of(context).cardTheme.color!,
Theme.of(context).cardTheme.surfaceTintColor,
Theme.of(context).cardTheme.elevation!,
),
progressColor: Theme.of(context).colorScheme.primary,
progress: remainingSeconds / timerLength,
),
willChange: true,
child: Center(
child: Text(
parseSeconds(),
style: Theme.of(context).textTheme.headlineLarge,
),
),
);
}
String parseSeconds() {
String addZeroIfNeeded(int value) {
if (value == 0) {
return '00';
} else if (value < 10) {
return '0$value';
} else {
return '$value';
}
}
final buffer = StringBuffer();
int remainingSeconds = this.remainingSeconds;
// longer than 1 hours
if (timerLength >= 3600) {
final hours = remainingSeconds ~/ 3600;
buffer.writeAll([addZeroIfNeeded(hours), ':']);
remainingSeconds -= hours * 3600;
}
// longer than 1 minute
if (timerLength >= 60 || timerLength == 0) {
final minutes = remainingSeconds ~/ 60;
buffer.writeAll([addZeroIfNeeded(minutes), ':']);
remainingSeconds -= minutes * 60;
}
// longer than 1 second
buffer.write(addZeroIfNeeded(remainingSeconds));
return buffer.toString();
}
}
class _TimelinePainter extends CustomPainter {
final Color progressColor;
final Color backgroundColor;
final double progress;
late final double timelineEdgeRadius = strokeWidth / 2;
late final double radiansProgress = 2 * pi * progress;
static const double radiansQuarterTurn = -pi / 2;
static const double strokeWidth = Dimens.grid8;
_TimelinePainter({
required this.progressColor,
required this.backgroundColor,
required this.progress,
});
@override
void paint(Canvas canvas, Size size) {
final radius = size.height / 2;
final timerCenter = Offset(radius, radius);
final timelineSegmentPath = Path.combine(
PathOperation.difference,
Path()
..arcTo(
Rect.fromCenter(
center: timerCenter,
height: size.height,
width: size.width,
),
radiansQuarterTurn,
radiansProgress,
false,
)
..lineTo(radius, radius)
..lineTo(radius, 0),
Path()
..addOval(
Rect.fromCircle(
center: timerCenter,
radius: radius - strokeWidth,
),
),
);
final smoothEdgesPath = Path.combine(
PathOperation.union,
Path()
..addOval(
Rect.fromCircle(
center: Offset(radius, timelineEdgeRadius),
radius: timelineEdgeRadius,
),
),
Path()
..addOval(
Rect.fromCircle(
center: Offset(
(radius - timelineEdgeRadius) * cos(radiansProgress + radiansQuarterTurn) + radius,
(radius - timelineEdgeRadius) * sin(radiansProgress + radiansQuarterTurn) + radius,
),
radius: timelineEdgeRadius,
),
),
);
canvas.drawPath(
Path.combine(
PathOperation.difference,
Path()
..addOval(
Rect.fromCircle(
center: timerCenter,
radius: radius,
),
),
Path()
..addOval(
Rect.fromCircle(
center: timerCenter,
radius: radius - strokeWidth,
),
),
),
Paint()..color = backgroundColor,
);
canvas.drawPath(
Path.combine(
PathOperation.union,
timelineSegmentPath,
smoothEdgesPath,
),
Paint()..color = progressColor,
);
}
@override
bool shouldRepaint(_TimelinePainter oldDelegate) => oldDelegate.progress != progress;
}