hwhub/lib/screens/about_screen.dart
Lukas Müllner 4b9cefb319 feat: multi-collection support, shared collections, invite by email
- Add collections & collection_members tables (SQL migration)
- Create CollectionService for CRUD, invite/remove members
- Add CollectionsScreen with create/manage/open flow
- Add ManageCollectionScreen (rename, invite by email, leave/delete)
- Update GarageScreen to filter by collection_id
- Update ScanTab with collection picker dropdown
- Update HomeShell: Collections tab replaces Garage tab
- Fix RLS policies (infinite recursion, owner visibility)
- Change package ID to com.dltw.derkauzigekoala.hwhub
- Fix unused variable warning in AboutScreen
2026-02-24 17:28:44 +01:00

243 lines
6.8 KiB
Dart

import 'package:flutter/material.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:url_launcher/url_launcher.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);
}
Future<void> _openUrl(String url) async {
final uri = Uri.parse(url);
await launchUrl(uri, mode: LaunchMode.externalApplication);
}
@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(
'HW Collector Hub',
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),
// ── Links section ──
Text(
'Links',
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
color: AppColors.textSecondary,
),
),
const SizedBox(height: 8),
_LinkTile(
icon: Icons.coffee,
iconColor: const Color(0xFFFFDD00),
title: 'Buy Me a Coffee',
subtitle: 'Support the development',
onTap: () =>
_openUrl('https://buymeacoffee.com/derkauzigekoala'),
),
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 HW Collector Hub',
style: theme.textTheme.bodySmall?.copyWith(
color: AppColors.textHint,
),
),
],
),
),
const SizedBox(height: 24),
],
),
);
}
}
// ── Link tile ─────────────────────────────────────────────────────────
class _LinkTile extends StatelessWidget {
final IconData icon;
final Color iconColor;
final String title;
final String subtitle;
final VoidCallback onTap;
const _LinkTile({
required this.icon,
required this.iconColor,
required this.title,
required this.subtitle,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return Card(
margin: const EdgeInsets.symmetric(vertical: 4),
child: ListTile(
leading: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: iconColor.withValues(alpha: 0.15),
borderRadius: BorderRadius.circular(10),
),
child: Icon(icon, color: iconColor, size: 22),
),
title: Text(
title,
style: const TextStyle(fontWeight: FontWeight.w500),
),
subtitle: Text(subtitle, style: const TextStyle(fontSize: 12)),
trailing: const Icon(Icons.open_in_new, color: AppColors.textHint, size: 18),
onTap: onTap,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
),
);
}
}
// ── 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),
),
],
),
);
}
}