feat(ui): apply branded headers and refresh splash/loading icon assets

- Use login background image in garage, collections, and profile headers
- Show app logo in About screen and add support mail action
- Configure native splash image assets in pubspec
- Regenerate flutter_native_splash and launcher icons for Android/iOS/Web
This commit is contained in:
Lukas Müllner 2026-03-04 17:01:45 +01:00
parent b67483db4e
commit ed541e046c
73 changed files with 220 additions and 68 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View file

@ -3,4 +3,7 @@
<item>
<bitmap android:gravity="fill" android:src="@drawable/background"/>
</item>
<item>
<bitmap android:gravity="center" android:src="@drawable/splash"/>
</item>
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 458 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 458 KiB

View file

@ -3,4 +3,7 @@
<item>
<bitmap android:gravity="fill" android:src="@drawable/background"/>
</item>
<item>
<bitmap android:gravity="center" android:src="@drawable/splash"/>
</item>
</layer-list>

View file

@ -3,4 +3,7 @@
<item>
<bitmap android:gravity="fill" android:src="@drawable/background"/>
</item>
<item>
<bitmap android:gravity="center" android:src="@drawable/splash"/>
</item>
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 458 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 163 KiB

After

Width:  |  Height:  |  Size: 354 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 458 KiB

View file

@ -3,4 +3,7 @@
<item>
<bitmap android:gravity="fill" android:src="@drawable/background"/>
</item>
<item>
<bitmap android:gravity="center" android:src="@drawable/splash"/>
</item>
</layer-list>

View file

@ -1,5 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
<foreground>
<inset
android:drawable="@drawable/ic_launcher_foreground"
android:inset="16%" />
</foreground>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 84 KiB

View file

@ -7,6 +7,7 @@
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="android:windowSplashScreenBackground">#1a1a2e</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your

View file

@ -7,6 +7,7 @@
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="android:windowSplashScreenBackground">#f9a11b</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#00000000</color>
<color name="ic_launcher_background">#f9a11b</color>
</resources>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 63 KiB

View file

@ -5,15 +5,48 @@
"idiom" : "universal",
"scale" : "1x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "LaunchImageDark.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "LaunchImage@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "LaunchImageDark@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "LaunchImage@3x.png",
"idiom" : "universal",
"scale" : "3x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "LaunchImageDark@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 B

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 B

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 B

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

View file

@ -1,5 +1,6 @@
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.
@ -11,6 +12,8 @@ class AboutScreen extends StatefulWidget {
}
class _AboutScreenState extends State<AboutScreen> {
static const _supportEmail = 'support@car64.app';
PackageInfo? _packageInfo;
@override
@ -24,6 +27,25 @@ class _AboutScreenState extends State<AboutScreen> {
if (mounted) setState(() => _packageInfo = info);
}
Future<void> _contactSupport() async {
final uri = Uri(
scheme: 'mailto',
path: _supportEmail,
queryParameters: {
'subject': 'car64 Support Request',
},
);
final launched = await launchUrl(uri, mode: LaunchMode.externalApplication);
if (!launched && mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Could not open mail app. Please email support@car64.app.'),
),
);
}
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
@ -49,10 +71,17 @@ class _AboutScreenState extends State<AboutScreen> {
gradient: AppColors.brandGradient,
shape: BoxShape.circle,
),
child: const Icon(
Icons.directions_car_filled,
color: Colors.white,
size: 52,
child: Padding(
padding: const EdgeInsets.all(16),
child: Image.asset(
'assets/img/icon_bg_removed.png',
fit: BoxFit.contain,
errorBuilder: (context, error, stackTrace) => const Icon(
Icons.directions_car_filled,
color: Colors.white,
size: 52,
),
),
),
),
),
@ -109,6 +138,16 @@ class _AboutScreenState extends State<AboutScreen> {
value: _packageInfo?.packageName ?? '',
),
const SizedBox(height: 20),
SizedBox(
width: double.infinity,
child: OutlinedButton.icon(
onPressed: _contactSupport,
icon: const Icon(Icons.support_agent),
label: const Text('Contact Support'),
),
),
const SizedBox(height: 32),
// Powered by

View file

@ -202,17 +202,33 @@ class CollectionsScreenState extends State<CollectionsScreen> {
),
),
background: Container(
decoration: const BoxDecoration(
gradient: AppColors.brandGradient,
decoration: BoxDecoration(
image: const DecorationImage(
image: AssetImage('assets/img/login_bg.jpg'),
fit: BoxFit.cover,
),
color: Colors.black.withValues(alpha: 0.15),
),
child: Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.only(right: 24),
child: Icon(
Icons.collections_bookmark,
size: 72,
color: Colors.white.withValues(alpha: 0.15),
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.black.withValues(alpha: 0.3),
Colors.black.withValues(alpha: 0.55),
],
),
),
child: Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.only(right: 24),
child: Icon(
Icons.collections_bookmark,
size: 72,
color: Colors.white.withValues(alpha: 0.18),
),
),
),
),

View file

@ -184,15 +184,33 @@ class GarageScreenState extends State<GarageScreen> {
),
),
background: Container(
decoration: const BoxDecoration(gradient: AppColors.brandGradient),
child: Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.only(right: 24),
child: Icon(
Icons.directions_car_filled,
size: 72,
color: Colors.white.withValues(alpha: 0.15),
decoration: BoxDecoration(
image: const DecorationImage(
image: AssetImage('assets/img/login_bg.jpg'),
fit: BoxFit.cover,
),
color: Colors.black.withValues(alpha: 0.15),
),
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.black.withValues(alpha: 0.3),
Colors.black.withValues(alpha: 0.55),
],
),
),
child: Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.only(right: 24),
child: Icon(
Icons.directions_car_filled,
size: 72,
color: Colors.white.withValues(alpha: 0.18),
),
),
),
),

View file

@ -23,52 +23,68 @@ class ProfileScreen extends StatelessWidget {
expandedHeight: 200,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
background: Container(
decoration: const BoxDecoration(
gradient: AppColors.brandGradient,
),
child: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(height: 16),
// Avatar
Container(
width: 80,
height: 80,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 3),
color: Colors.white.withValues(alpha: 0.2),
),
child: const Icon(
Icons.person,
size: 44,
color: Colors.white,
),
background: Stack(
fit: StackFit.expand,
children: [
Image.asset(
'assets/img/login_bg.jpg',
fit: BoxFit.cover,
),
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.black.withValues(alpha: 0.3),
Colors.black.withValues(alpha: 0.55),
],
),
const SizedBox(height: 12),
Text(
email,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Colors.white.withValues(alpha: 0.95),
),
),
if (createdAt != null) ...[
const SizedBox(height: 4),
Text(
'Member since ${_formatDate(createdAt)}',
style: TextStyle(
fontSize: 12,
color: Colors.white.withValues(alpha: 0.7),
),
),
SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(height: 16),
// Avatar
Container(
width: 80,
height: 80,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 3),
color: Colors.white.withValues(alpha: 0.2),
),
child: const Icon(
Icons.person,
size: 44,
color: Colors.white,
),
),
const SizedBox(height: 12),
Text(
email,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Colors.white.withValues(alpha: 0.95),
),
),
if (createdAt != null) ...[
const SizedBox(height: 4),
Text(
'Member since ${_formatDate(createdAt)}',
style: TextStyle(
fontSize: 12,
color: Colors.white.withValues(alpha: 0.75),
),
),
],
],
],
),
),
),
],
),
),
),

View file

@ -1046,7 +1046,7 @@ packages:
source: hosted
version: "2.3.1"
url_launcher:
dependency: transitive
dependency: "direct main"
description:
name: url_launcher
sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8

View file

@ -46,6 +46,7 @@ dependencies:
image: ^4.2.0
package_info_plus: ^8.1.3
shared_preferences: ^2.5.3
url_launcher: ^6.3.1
dev_dependencies:
flutter_test:
@ -89,9 +90,13 @@ flutter:
flutter_native_splash:
color: "#f9a11b"
color_dark: "#1a1a2e"
image: assets/icon/app_icon.png
image_dark: assets/icon/app_icon.png
android_12:
color: "#f9a11b"
color_dark: "#1a1a2e"
image: assets/icon/app_icon_foreground.png
image_dark: assets/icon/app_icon_foreground.png
# ── Launcher Icon config ──
flutter_launcher_icons:

View file

@ -29,6 +29,9 @@
<title>car64</title>
<link rel="manifest" href="manifest.json">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<style id="splash-screen-style">
html {
height: 100%
@ -100,9 +103,13 @@
document.body.style.background = "transparent";
}
</script>
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
</head>
<body>
<picture id="splash">
<source srcset="splash/img/light-1x.png 1x, splash/img/light-2x.png 2x, splash/img/light-3x.png 3x, splash/img/light-4x.png 4x" media="(prefers-color-scheme: light)">
<source srcset="splash/img/dark-1x.png 1x, splash/img/dark-2x.png 2x, splash/img/dark-3x.png 3x, splash/img/dark-4x.png 4x" media="(prefers-color-scheme: dark)">
<img class="center" aria-hidden="true" src="splash/img/light-1x.png" alt="">
</picture>
<!--
You can customize the "flutter_bootstrap.js" script.
This is useful to provide a custom configuration to the Flutter loader