focus-flow/apps/focus_flow_flutter/lib/widgets/timeline/reward_icon.dart

97 lines
3.6 KiB
Dart

// SPDX-FileCopyrightText: 2026 FocusFlow contributors
// SPDX-License-Identifier: AGPL-3.0-only
/// Renders Reward Icon pieces for the FocusFlow Flutter timeline.
library;
import 'package:flutter/material.dart';
import '../../theme/focus_flow_tokens.dart';
/// Sparkle-style reward indicator for a timeline task.
class RewardIcon extends StatelessWidget {
/// Creates a reward icon.
const RewardIcon({
this.color = FocusFlowTokens.accentMagenta,
this.size = 24,
super.key,
});
/// Fill color used by the icon painter.
final Color color;
/// Square size of the painted icon.
final double size;
/// Builds the widget subtree for this component.
/// The method translates the current widget configuration and state into Flutter UI without mutating scheduler domain data.
@override
Widget build(BuildContext context) {
return CustomPaint(
size: Size.square(size),
painter: _RewardIconPainter(color),
);
}
}
/// Private implementation type for `_RewardIconPainter` in this library.
/// It keeps supporting state and behavior scoped to the owning file so the public API stays focused on the scheduler concepts that callers use directly.
class _RewardIconPainter extends CustomPainter {
/// Creates a `_RewardIconPainter` instance with the values required by its invariants.
/// Callers should pass already-normalized scheduler data because validation and defaulting belong at the model or service boundary, not in arbitrary UI code.
const _RewardIconPainter(this.color);
/// Stores the `color` value for this object.
/// This field is part of the explicit state that downstream scheduling, persistence, or UI code reads when composing behavior.
final Color color;
/// Performs the `paint` behavior for this scheduler component.
/// The method provides a named entry point for this operation so callers do not need to know the lower-level state or persistence steps involved.
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = color
..style = PaintingStyle.fill;
_drawSparkle(
canvas,
paint,
size.center(Offset.zero),
size.shortestSide * 0.44,
);
_drawSparkle(
canvas,
paint,
Offset(size.width * 0.78, size.height * 0.24),
size.shortestSide * 0.18,
);
_drawSparkle(
canvas,
paint,
Offset(size.width * 0.24, size.height * 0.78),
size.shortestSide * 0.14,
);
}
/// Runs the `_drawSparkle` helper used inside this library.
/// The helper centralizes a small piece of transformation or validation so higher-level scheduler flow remains easier to read.
void _drawSparkle(Canvas canvas, Paint paint, Offset center, double radius) {
final path = Path()
..moveTo(center.dx, center.dy - radius)
..lineTo(center.dx + radius * 0.24, center.dy - radius * 0.24)
..lineTo(center.dx + radius, center.dy)
..lineTo(center.dx + radius * 0.24, center.dy + radius * 0.24)
..lineTo(center.dx, center.dy + radius)
..lineTo(center.dx - radius * 0.24, center.dy + radius * 0.24)
..lineTo(center.dx - radius, center.dy)
..lineTo(center.dx - radius * 0.24, center.dy - radius * 0.24)
..close();
canvas.drawPath(path, paint);
}
/// Performs the `shouldRepaint` behavior for this scheduler component.
/// The method provides a named entry point for this operation so callers do not need to know the lower-level state or persistence steps involved.
@override
bool shouldRepaint(covariant _RewardIconPainter oldDelegate) {
return oldDelegate.color != color;
}
}