AgendaTasks/tool/generate_icon.dart
m3mo 2683960fac Replace app icon with custom checkmark design
- Add purple checkmark icon matching login screen style
- Generate Android adaptive icons with proper foreground/background
- Update iOS app icons
- Include icon generator tool for future updates
2026-02-03 14:23:06 +01:00

119 lines
3.7 KiB
Dart

import 'dart:io';
import 'dart:math';
import 'package:image/image.dart' as img;
/// Generates app icon PNG files for Agenda Tasks
/// Run with: cd tool && dart pub get && dart run generate_icon.dart
void main() async {
print('Generating app icons...');
// Generate main icon (1024x1024)
final mainIcon = generateIcon(1024, withBackground: true);
final mainPng = img.encodePng(mainIcon);
await File('../assets/icon/app_icon.png').writeAsBytes(mainPng);
print('Created: assets/icon/app_icon.png (${mainPng.length} bytes)');
// Generate adaptive icon foreground (1024x1024)
final foregroundIcon = generateIcon(1024, withBackground: false);
final fgPng = img.encodePng(foregroundIcon);
await File('../assets/icon/app_icon_foreground.png').writeAsBytes(fgPng);
print('Created: assets/icon/app_icon_foreground.png (${fgPng.length} bytes)');
print('Done! Now run from project root: flutter pub run flutter_launcher_icons');
}
/// Generate an icon with a checkmark design
img.Image generateIcon(int size, {required bool withBackground}) {
final image = img.Image(width: size, height: size);
final center = size / 2;
final radius = size * 0.42;
// Colors - Material Purple
final purple = img.ColorRgba8(103, 80, 164, 255); // #6750A4
final white = img.ColorRgba8(255, 255, 255, 255);
final transparent = img.ColorRgba8(0, 0, 0, 0);
// Checkmark stroke width
final checkStroke = size * 0.09;
// Checkmark points - classic check shape
// Point 1: Start of short leg (left side)
final p1x = center - radius * 0.40;
final p1y = center + radius * 0.05;
// Point 2: Corner/bottom of check
final p2x = center - radius * 0.05;
final p2y = center + radius * 0.40;
// Point 3: End of long leg (top right)
final p3x = center + radius * 0.50;
final p3y = center - radius * 0.35;
// Fill image
for (int y = 0; y < size; y++) {
for (int x = 0; x < size; x++) {
final dx = x - center;
final dy = y - center;
final distance = sqrt(dx * dx + dy * dy);
img.Color pixelColor = transparent;
if (withBackground) {
// Draw filled purple circle
if (distance <= radius) {
pixelColor = purple;
}
// Draw white checkmark on top
final dist1 = distanceToSegment(x.toDouble(), y.toDouble(), p1x, p1y, p2x, p2y);
final dist2 = distanceToSegment(x.toDouble(), y.toDouble(), p2x, p2y, p3x, p3y);
if (dist1 <= checkStroke || dist2 <= checkStroke) {
pixelColor = white;
}
} else {
// Foreground only - purple circle outline and checkmark
final ringOuter = radius;
final ringInner = radius * 0.88;
// Circle outline
if (distance <= ringOuter && distance >= ringInner) {
pixelColor = purple;
}
// Purple checkmark
final dist1 = distanceToSegment(x.toDouble(), y.toDouble(), p1x, p1y, p2x, p2y);
final dist2 = distanceToSegment(x.toDouble(), y.toDouble(), p2x, p2y, p3x, p3y);
if (dist1 <= checkStroke || dist2 <= checkStroke) {
pixelColor = purple;
}
}
image.setPixel(x, y, pixelColor);
}
}
return image;
}
/// Calculate distance from point (px, py) to line segment (x1,y1)-(x2,y2)
double distanceToSegment(double px, double py, double x1, double y1, double x2, double y2) {
final dx = x2 - x1;
final dy = y2 - y1;
final lengthSq = dx * dx + dy * dy;
if (lengthSq == 0) {
return sqrt((px - x1) * (px - x1) + (py - y1) * (py - y1));
}
var t = ((px - x1) * dx + (py - y1) * dy) / lengthSq;
t = t.clamp(0.0, 1.0);
final nearestX = x1 + t * dx;
final nearestY = y1 + t * dy;
return sqrt((px - nearestX) * (px - nearestX) + (py - nearestY) * (py - nearestY));
}