feat(store): Play feature graphic (1024x500) generator
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 207 KiB |
@@ -0,0 +1,36 @@
|
|||||||
|
// lib/ui/branding/feature_graphic_painter.dart
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'app_icon_painter.dart';
|
||||||
|
|
||||||
|
/// Paints the Play feature graphic (1024×500): navy field, the brand blocks on
|
||||||
|
/// the left, wordmark + tagline on the right.
|
||||||
|
class FeatureGraphic {
|
||||||
|
static void paint(Canvas canvas, Size size) {
|
||||||
|
final rect = Offset.zero & size;
|
||||||
|
AppIconMark.paintBackground(canvas, rect);
|
||||||
|
|
||||||
|
// Blocks on the left, vertically centered.
|
||||||
|
canvas.save();
|
||||||
|
final blockArea = size.height * 0.74;
|
||||||
|
canvas.translate(size.height * 0.16, (size.height - blockArea) / 2);
|
||||||
|
AppIconMark.paintBlocks(canvas, blockArea, groupFraction: 0.92);
|
||||||
|
canvas.restore();
|
||||||
|
|
||||||
|
void text(String s, double dy, double fontSize, FontWeight w, Color c) {
|
||||||
|
final tp = TextPainter(
|
||||||
|
text: TextSpan(
|
||||||
|
text: s,
|
||||||
|
style: TextStyle(
|
||||||
|
color: c, fontSize: fontSize, fontWeight: w, letterSpacing: 0.5),
|
||||||
|
),
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
)..layout();
|
||||||
|
tp.paint(canvas, Offset(size.height * 1.02, dy));
|
||||||
|
}
|
||||||
|
|
||||||
|
text('Block Seasons', size.height * 0.34, 76, FontWeight.w900, Colors.white);
|
||||||
|
text('A new season of blocks every few weeks.', size.height * 0.50, 30,
|
||||||
|
FontWeight.w500, const Color(0xFFB9C4E6));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import 'dart:io';
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:block_seasons/ui/branding/app_icon_painter.dart';
|
import 'package:block_seasons/ui/branding/app_icon_painter.dart';
|
||||||
|
import 'package:block_seasons/ui/branding/feature_graphic_painter.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
Future<void> _writePng(String path, int size, void Function(Canvas) draw) async {
|
Future<void> _writePng(String path, int size, void Function(Canvas) draw) async {
|
||||||
@@ -41,4 +42,20 @@ void main() {
|
|||||||
expect(File('assets/icon/$f').existsSync(), isTrue, reason: f);
|
expect(File('assets/icon/$f').existsSync(), isTrue, reason: f);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('generate feature graphic', (tester) async {
|
||||||
|
await tester.runAsync(() async {
|
||||||
|
const w = 1024, h = 500;
|
||||||
|
final recorder = PictureRecorder();
|
||||||
|
final canvas = Canvas(recorder);
|
||||||
|
FeatureGraphic.paint(canvas, const Size(1024, 500));
|
||||||
|
final picture = recorder.endRecording();
|
||||||
|
final image = await picture.toImage(w, h);
|
||||||
|
final bytes = await image.toByteData(format: ImageByteFormat.png);
|
||||||
|
File('docs/store/feature_graphic.png').parent.createSync(recursive: true);
|
||||||
|
File('docs/store/feature_graphic.png')
|
||||||
|
.writeAsBytesSync(bytes!.buffer.asUint8List());
|
||||||
|
});
|
||||||
|
expect(File('docs/store/feature_graphic.png').existsSync(), isTrue);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user