hwhub/lib/screens/about_screen.dart
Lukas Müllner d8dbc2573a feat(v3.0): rebrand to car64 and align app flow with TPB schema
- Rename installed app display name to car64 (Android/iOS/Web titles)
- Remove Buy Me a Coffee link from About screen
- Update app version to 3.0.0+1
- Migrate scanner workflow to catalog-first flow:
  - look up in global_cars
  - show Found in Catalog bottom sheet for known cars
  - show New Discovery bottom sheet and insert into global_cars + car_votes
  - insert only into hotwheels for collection entries
- Align garage reads to TPB schema by joining global_cars
- Switch image field usage from image_url to user_image_url
- Implement private-storage image service:
  - upload path auth.uid()/hotwheels.id.jpg
  - signed URL generation (1h) for display
  - in-app compression pipeline targeting <=500KB and max 1080px width
- Use local brand fallback image assets/img/icon_bg_removed.png for missing car photos
- Restrict edit dialog to personal notes (hotwheels) instead of global car metadata
- Ensure first login auto-creates a default collection and owner membership
2026-03-04 10:12:12 +01:00

171 lines
4.6 KiB
Dart

import 'package:flutter/material.dart';
import 'package:package_info_plus/package_info_plus.dart';
import '../theme/app_colors.dart';
/// Full-page About screen with app info, version, and links.
class AboutScreen extends StatefulWidget {
const AboutScreen({super.key});
@override
State<AboutScreen> createState() => _AboutScreenState();
}
class _AboutScreenState extends State<AboutScreen> {
PackageInfo? _packageInfo;
@override
void initState() {
super.initState();
_loadPackageInfo();
}
Future<void> _loadPackageInfo() async {
final info = await PackageInfo.fromPlatform();
if (mounted) setState(() => _packageInfo = info);
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final version = _packageInfo != null
? 'v${_packageInfo!.version} (${_packageInfo!.buildNumber})'
: '';
return Scaffold(
appBar: AppBar(
title: const Text('About'),
),
body: ListView(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
children: [
const SizedBox(height: 16),
// ── App icon + name ──
Center(
child: Container(
width: 96,
height: 96,
decoration: const BoxDecoration(
gradient: AppColors.brandGradient,
shape: BoxShape.circle,
),
child: const Icon(
Icons.directions_car_filled,
color: Colors.white,
size: 52,
),
),
),
const SizedBox(height: 16),
Center(
child: Text(
'car64',
style: theme.textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(height: 4),
Center(
child: Text(
version,
style: theme.textTheme.bodyMedium?.copyWith(
color: AppColors.textSecondary,
),
),
),
const SizedBox(height: 8),
Center(
child: Text(
'Track and manage your Hot Wheels collection.',
style: theme.textTheme.bodyMedium?.copyWith(
color: AppColors.textHint,
),
textAlign: TextAlign.center,
),
),
const SizedBox(height: 32),
const Divider(),
const SizedBox(height: 16),
// ── Technical details ──
Text(
'Technical',
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
color: AppColors.textSecondary,
),
),
const SizedBox(height: 8),
_InfoRow(label: 'Version', value: _packageInfo?.version ?? ''),
_InfoRow(
label: 'Build',
value: _packageInfo?.buildNumber ?? '',
),
_InfoRow(
label: 'Package',
value: _packageInfo?.packageName ?? '',
),
const SizedBox(height: 32),
// ── Powered by ──
Center(
child: Column(
children: [
Text(
'Powered by Flutter & Supabase',
style: theme.textTheme.bodySmall?.copyWith(
color: AppColors.textHint,
),
),
const SizedBox(height: 4),
Text(
'© 2026 car64',
style: theme.textTheme.bodySmall?.copyWith(
color: AppColors.textHint,
),
),
],
),
),
const SizedBox(height: 24),
],
),
);
}
}
// ── Info row (label : value) ──────────────────────────────────────────
class _InfoRow extends StatelessWidget {
final String label;
final String value;
const _InfoRow({required this.label, required this.value});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: Row(
children: [
Text(
label,
style: const TextStyle(
color: AppColors.textSecondary,
fontWeight: FontWeight.w500,
),
),
const Spacer(),
Text(
value,
style: const TextStyle(color: AppColors.textHint),
),
],
),
);
}
}