hwhub/lib/theme/app_theme.dart
Lukas Müllner 91f25e0cef feat(v1.1): complete UI overhaul with branded theme, garage, and image uploads
- Custom Material 3 theme with Hot Wheels branding (orange/red gradient, navy accents)
- Locally bundled Poppins font (Regular, Medium, SemiBold, Bold)
- Redesigned login screen with full-bleed background image and frosted glass form
- Bottom navigation shell: My Garage / Scan / Profile tabs
- My Garage screen with grid view, search, stats, detail bottom sheet
- Edit and delete car details from the detail sheet
- Skip button for quick-add with minimal info
- Camera photo upload to Supabase Storage (UUID-based unguessable paths)
- Change/add photo from car detail view
- Profile screen with change password, about dialog, sign out
- Scan tab with camera scanner and manual entry
- Styled scanner screen with crosshair overlay and gradient buttons
- Custom app icon with transparent background and adaptive icon support
- Native splash screen with brand colors
- Auto-refresh garage on tab switch
2026-02-24 07:46:06 +01:00

347 lines
11 KiB
Dart

import 'package:flutter/material.dart';
import 'app_colors.dart';
/// Central theme definition for HW Collector Hub.
class AppTheme {
AppTheme._();
// ─────────────────────────── LIGHT ───────────────────────────────────
static ThemeData get light {
final colorScheme = ColorScheme.fromSeed(
seedColor: AppColors.orange,
primary: AppColors.orange,
secondary: AppColors.navy,
surface: AppColors.cardLight,
error: AppColors.error,
);
return ThemeData(
useMaterial3: true,
colorScheme: colorScheme,
fontFamily: 'Poppins',
scaffoldBackgroundColor: AppColors.backgroundLight,
// ── AppBar ──
appBarTheme: const AppBarTheme(
backgroundColor: Colors.transparent,
foregroundColor: AppColors.textPrimary,
elevation: 0,
centerTitle: true,
titleTextStyle: TextStyle(
fontFamily: 'Poppins',
fontWeight: FontWeight.w600,
fontSize: 20,
color: AppColors.textPrimary,
),
),
// ── Cards ──
cardTheme: CardThemeData(
color: AppColors.cardLight,
elevation: 2,
shadowColor: Colors.black12,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
),
// ── Elevated buttons ──
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.navy,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
textStyle: const TextStyle(
fontFamily: 'Poppins',
fontWeight: FontWeight.w600,
fontSize: 16,
),
),
),
// ── Outlined buttons ──
outlinedButtonTheme: OutlinedButtonThemeData(
style: OutlinedButton.styleFrom(
foregroundColor: AppColors.navy,
side: const BorderSide(color: AppColors.navy),
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
textStyle: const TextStyle(
fontFamily: 'Poppins',
fontWeight: FontWeight.w500,
fontSize: 14,
),
),
),
// ── Text buttons ──
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
foregroundColor: AppColors.navy,
textStyle: const TextStyle(
fontFamily: 'Poppins',
fontWeight: FontWeight.w500,
),
),
),
// ── Input fields ──
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: Colors.white,
contentPadding:
const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide: BorderSide(color: Colors.grey.shade300),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide: BorderSide(color: Colors.grey.shade300),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide:
const BorderSide(color: AppColors.navy, width: 2),
),
labelStyle: const TextStyle(
fontFamily: 'Poppins',
color: AppColors.textSecondary,
),
hintStyle: const TextStyle(
fontFamily: 'Poppins',
color: AppColors.textHint,
),
),
// ── Bottom Navigation ──
navigationBarTheme: NavigationBarThemeData(
backgroundColor: Colors.white,
indicatorColor: AppColors.orange.withValues(alpha: 0.15),
labelTextStyle: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return const TextStyle(
fontFamily: 'Poppins',
fontWeight: FontWeight.w600,
fontSize: 12,
color: AppColors.orange,
);
}
return const TextStyle(
fontFamily: 'Poppins',
fontWeight: FontWeight.w400,
fontSize: 12,
color: AppColors.textSecondary,
);
}),
iconTheme: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return const IconThemeData(color: AppColors.orange);
}
return const IconThemeData(color: AppColors.textSecondary);
}),
),
// ── FAB ──
floatingActionButtonTheme: const FloatingActionButtonThemeData(
backgroundColor: AppColors.orange,
foregroundColor: Colors.white,
shape: StadiumBorder(),
),
// ── Dialogs ──
dialogTheme: DialogThemeData(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
titleTextStyle: const TextStyle(
fontFamily: 'Poppins',
fontWeight: FontWeight.w600,
fontSize: 20,
color: AppColors.textPrimary,
),
),
// ── Snackbar ──
snackBarTheme: SnackBarThemeData(
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
contentTextStyle: const TextStyle(
fontFamily: 'Poppins',
fontWeight: FontWeight.w400,
),
),
// ── Divider ──
dividerTheme: DividerThemeData(
color: Colors.grey.shade200,
thickness: 1,
),
);
}
// ─────────────────────────── DARK ────────────────────────────────────
static ThemeData get dark {
final colorScheme = ColorScheme.fromSeed(
seedColor: AppColors.orange,
brightness: Brightness.dark,
primary: AppColors.orange,
secondary: AppColors.navyLight,
surface: AppColors.cardDark,
error: AppColors.error,
);
return ThemeData(
useMaterial3: true,
colorScheme: colorScheme,
fontFamily: 'Poppins',
scaffoldBackgroundColor: AppColors.backgroundDark,
appBarTheme: const AppBarTheme(
backgroundColor: Colors.transparent,
foregroundColor: Colors.white,
elevation: 0,
centerTitle: true,
titleTextStyle: TextStyle(
fontFamily: 'Poppins',
fontWeight: FontWeight.w600,
fontSize: 20,
color: Colors.white,
),
),
cardTheme: CardThemeData(
color: AppColors.cardDark,
elevation: 4,
shadowColor: Colors.black26,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.orange,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
textStyle: const TextStyle(
fontFamily: 'Poppins',
fontWeight: FontWeight.w600,
fontSize: 16,
),
),
),
outlinedButtonTheme: OutlinedButtonThemeData(
style: OutlinedButton.styleFrom(
foregroundColor: AppColors.orange,
side: const BorderSide(color: AppColors.orange),
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
),
),
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
foregroundColor: AppColors.orange,
),
),
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: AppColors.surfaceDark,
contentPadding:
const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide: BorderSide.none,
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide: BorderSide.none,
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide:
const BorderSide(color: AppColors.orange, width: 2),
),
labelStyle: const TextStyle(
fontFamily: 'Poppins',
color: AppColors.textHint,
),
),
navigationBarTheme: NavigationBarThemeData(
backgroundColor: AppColors.cardDark,
indicatorColor: AppColors.orange.withValues(alpha: 0.2),
labelTextStyle: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return const TextStyle(
fontFamily: 'Poppins',
fontWeight: FontWeight.w600,
fontSize: 12,
color: AppColors.orange,
);
}
return const TextStyle(
fontFamily: 'Poppins',
fontWeight: FontWeight.w400,
fontSize: 12,
color: AppColors.textHint,
);
}),
iconTheme: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return const IconThemeData(color: AppColors.orange);
}
return const IconThemeData(color: AppColors.textHint);
}),
),
floatingActionButtonTheme: const FloatingActionButtonThemeData(
backgroundColor: AppColors.orange,
foregroundColor: Colors.white,
shape: StadiumBorder(),
),
dialogTheme: DialogThemeData(
backgroundColor: AppColors.cardDark,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
titleTextStyle: const TextStyle(
fontFamily: 'Poppins',
fontWeight: FontWeight.w600,
fontSize: 20,
color: Colors.white,
),
),
snackBarTheme: SnackBarThemeData(
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
dividerTheme: const DividerThemeData(
color: AppColors.surfaceDark,
thickness: 1,
),
);
}
}