branding added

This commit is contained in:
davmar
2026-09-18 00:16:49 +02:00
parent 78fd23f69f
commit c1bb355986
32 changed files with 342 additions and 287 deletions
+6 -6
View File
@@ -1,15 +1,15 @@
# Values Sort
# Values Compass
Flutter Android app for a guided personal-values card-sort practice.
Built with Flutter **3.47** (Dart **3.13**), Material 3, Provider, and local persistence.
Built with Flutter (Dart **3.13**), Material 3, Provider, and local persistence.
Package name: `com.example.myapp`
Package name: `com.valuesort.app`
## Practice flow
1. Welcome (+ how-it-works guide)
2. Sort each value into five reflective piles
3. Review / refine piles
3. Review piles and reorder cards as needed
4. Rank your most important values
5. Reflect with journal prompts
@@ -23,13 +23,13 @@ lib/
├── data/values_deck.dart
├── models/
├── providers/
├── screens/ # welcome → sort → overview → rank → reflect
├── screens/ # welcome → sort → hub → overview / rank / reflect
├── services/storage_service.dart
├── utils/
└── widgets/
assets/
├── data/values_deck.json
└── images/emblem.png
└── images/emblem.png / app_icon.png
stitch/ # exported Stitch HTML/PNG design references
```
+2 -3
View File
@@ -5,7 +5,7 @@ plugins {
}
android {
namespace = "com.example.myapp"
namespace = "com.valuesort.app"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
@@ -15,8 +15,7 @@ android {
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.example.myapp"
applicationId = "com.valuesort.app"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion
+1 -1
View File
@@ -1,7 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:label="MyApp"
android:label="Values Compass"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
@@ -1,4 +1,4 @@
package com.example.myapp
package com.valuesort.app
import io.flutter.embedding.android.FlutterActivity
Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 442 B

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 B

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 84 KiB

+1 -1
View File
@@ -1,4 +1,4 @@
/// Application entry point: wires Provider and the Values Sort practice.
/// Application entry point: wires Provider and the Values Compass practice.
library;
import 'package:flutter/material.dart';
-7
View File
@@ -23,11 +23,4 @@ class ValueCard {
domain: json['domain'] as String? ?? 'Values',
);
}
Map<String, dynamic> toJson() => {
'id': id,
'title': title,
'description': description,
'domain': domain,
};
}
+11 -22
View File
@@ -1,4 +1,4 @@
/// Session state for the Values Sort practice: sorting, ranking, reflection.
/// Session state for the Values Compass practice: sorting, ranking, reflection.
library;
import 'dart:async';
@@ -25,14 +25,13 @@ enum SortStage {
sorting,
hub,
overview,
pileReview,
ranking,
reflection,
}
/// ChangeNotifier that owns the deck, pile assignments, ranking, and journals.
class ValuesSortController extends ChangeNotifier {
ValuesSortController({required StorageService this._storage});
ValuesSortController({required this._storage});
final StorageService _storage;
@@ -65,9 +64,6 @@ class ValuesSortController extends ChangeNotifier {
/// Reflections keyed by `valueId|promptIndex`.
final Map<String, String> _reflections = {};
ValuePile? _reviewPile;
ValuePile? get reviewPile => _reviewPile;
String? _activeReflectionValueId;
String? get activeReflectionValueId => _activeReflectionValueId;
@@ -112,7 +108,6 @@ class ValuesSortController extends ChangeNotifier {
..addAll(_deck);
_rankedIds.clear();
_reflections.clear();
_reviewPile = null;
_activeReflectionValueId = null;
_activePromptIndex = 0;
}
@@ -218,12 +213,6 @@ class ValuesSortController extends ChangeNotifier {
_commit();
}
void openPileReview(ValuePile pile) {
_reviewPile = pile;
_stage = SortStage.pileReview;
_commit();
}
void goToRanking() {
_syncRankingWithMostImportant();
if (_rankedIds.isEmpty) {
@@ -333,7 +322,6 @@ class ValuesSortController extends ChangeNotifier {
'version': 1,
'deckFingerprint': _deckFingerprint,
'stage': _stage.name,
'reviewPile': _reviewPile?.name,
'assignments': {
for (final e in _assignments.entries) e.key: e.value.name,
},
@@ -414,7 +402,6 @@ class ValuesSortController extends ChangeNotifier {
.where(knownIds.contains),
);
_reviewPile = ValuePileX.fromName(data['reviewPile'] as String?);
_activeReflectionValueId = data['activeReflectionValueId'] as String?;
if (_activeReflectionValueId != null &&
!knownIds.contains(_activeReflectionValueId)) {
@@ -423,19 +410,21 @@ class ValuesSortController extends ChangeNotifier {
_activePromptIndex = (data['activePromptIndex'] as num?)?.toInt() ?? 0;
final stageName = data['stage'] as String? ?? SortStage.welcome.name;
_stage = SortStage.values.firstWhere(
(s) => s.name == stageName,
orElse: () => SortStage.welcome,
);
// Remap removed refine stage onto the hub for older sessions.
if (stageName == 'pileReview') {
_stage = SortStage.hub;
} else {
_stage = SortStage.values.firstWhere(
(s) => s.name == stageName,
orElse: () => SortStage.welcome,
);
}
// If restored mid-sort but nothing left unsorted, land on the hub.
if (_stage == SortStage.sorting && isSortComplete) {
_seedRankingFromMostImportant();
_stage = SortStage.hub;
}
if (_stage == SortStage.pileReview && _reviewPile == null) {
_stage = SortStage.hub;
}
} catch (_) {
await _storage.clearSortSession();
_resetSortState();
-129
View File
@@ -1,129 +0,0 @@
/// Refine a single pile — promote / demote cards (Important Cards pattern).
library;
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../models/value_pile.dart';
import '../providers/values_sort_provider.dart';
import '../widgets/practice_chrome.dart';
import '../widgets/value_card_tile.dart';
import '../utils/app_theme.dart';
class PileReviewScreen extends StatelessWidget {
const PileReviewScreen({super.key});
@override
Widget build(BuildContext context) {
final sort = context.watch<ValuesSortController>();
final pile = sort.reviewPile ?? ValuePile.important;
final cards = sort.cardsInPile(pile);
final text = Theme.of(context).textTheme;
final colors = Theme.of(context).colorScheme;
return PracticeScaffold(
header: Padding(
padding: const EdgeInsets.fromLTRB(8, 4, 12, 0),
child: Row(
children: [
IconButton(
onPressed: sort.goToOverview,
icon: const Icon(Icons.arrow_back),
),
Expanded(
child: Text(
'Reviewing · ${pile.label}',
style: text.labelMedium,
),
),
],
),
),
footer: PrimaryPillButton(
label: 'Back to overview',
onPressed: sort.goToOverview,
),
child: ListView(
children: [
Text(pile.label, style: text.displaySmall),
const SizedBox(height: 6),
Text(
'${cards.length} values in this pile. Promote toward your core or demote when something feels less essential.',
style: text.bodyMedium,
),
const SizedBox(height: 12),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: ValuePile.values.map((p) {
return Padding(
padding: const EdgeInsets.only(right: 8),
child: ActionChip(
avatar: CircleAvatar(
backgroundColor: p.accent.withValues(alpha: 0.2),
radius: 8,
),
label: Text('${p.shortLabel} ${sort.countInPile(p)}'),
onPressed: () => sort.openPileReview(p),
backgroundColor: p == pile
? AppTheme.surfaceContainerHigh
: AppTheme.surfaceContainerLow,
),
);
}).toList(),
),
),
const SizedBox(height: 16),
if (cards.isEmpty)
Padding(
padding: const EdgeInsets.symmetric(vertical: 40),
child: Text(
'No cards in this pile yet.',
style: text.bodyLarge?.copyWith(color: colors.onSurfaceVariant),
textAlign: TextAlign.center,
),
)
else
...cards.map((card) {
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: ValueCardTile(
card: card,
compact: true,
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
tooltip: 'Promote',
onPressed: () => _shift(sort, card.id, pile, up: true),
icon: const Icon(Icons.arrow_upward),
),
IconButton(
tooltip: 'Demote',
onPressed: () =>
_shift(sort, card.id, pile, up: false),
icon: const Icon(Icons.arrow_downward),
),
],
),
),
);
}),
],
),
);
}
void _shift(
ValuesSortController sort,
String id,
ValuePile current, {
required bool up,
}) {
final values = ValuePile.values;
final index = values.indexOf(current);
final next = up ? index - 1 : index + 1;
if (next < 0 || next >= values.length) return;
sort.moveCard(id, values[next]);
}
}
-2
View File
@@ -6,7 +6,6 @@ import 'package:provider/provider.dart';
import '../providers/values_sort_provider.dart';
import 'card_sorting_screen.dart';
import 'pile_review_screen.dart';
import 'practice_hub_screen.dart';
import 'rank_order_screen.dart';
import 'reflection_screen.dart';
@@ -56,7 +55,6 @@ class PracticeFlowScreen extends StatelessWidget {
SortStage.sorting => const CardSortingScreen(),
SortStage.hub => const PracticeHubScreen(),
SortStage.overview => const SortedOverviewScreen(),
SortStage.pileReview => const PileReviewScreen(),
SortStage.ranking => const RankOrderScreen(),
SortStage.reflection => const ReflectionScreen(),
},
-8
View File
@@ -135,14 +135,6 @@ class RankOrderScreen extends StatelessWidget {
color: colors.onSurfaceVariant,
),
),
const SizedBox(height: 4),
Text(
'Pillar: ${card.domain}',
style: text.labelSmall?.copyWith(
color: colors.onSurfaceVariant,
),
),
],
),
),
+2 -10
View File
@@ -1,4 +1,4 @@
/// Bird's-eye overview of all five piles with demote / refine actions.
/// Bird's-eye overview of all five piles with reorder actions.
library;
import 'package:flutter/material.dart';
@@ -25,7 +25,6 @@ class _SortedOverviewScreenState extends State<SortedOverviewScreen> {
Widget build(BuildContext context) {
final sort = context.watch<ValuesSortController>();
final text = Theme.of(context).textTheme;
final colors = Theme.of(context).colorScheme;
return PracticeScaffold(
header: Padding(
@@ -47,8 +46,6 @@ class _SortedOverviewScreenState extends State<SortedOverviewScreen> {
),
child: ListView(
children: [
Text('All Sorted Values', style: text.displaySmall),
const SizedBox(height: 16),
TextField(
onChanged: (v) => setState(() => _query = v.trim().toLowerCase()),
decoration: const InputDecoration(
@@ -93,12 +90,7 @@ class _SortedOverviewScreenState extends State<SortedOverviewScreen> {
onDemote: (card) => _showDemoteSheet(context, sort, card),
);
}),
const SizedBox(height: 12),
Text(
'Tip: refine any pile before ranking your core values.',
style: text.bodySmall?.copyWith(color: colors.onSurfaceVariant),
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
],
),
);
+10 -6
View File
@@ -89,20 +89,24 @@ class _SplashScreenState extends State<SplashScreen>
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ClipOval(
ClipRRect(
borderRadius: BorderRadius.circular(22),
child: Image.asset(
'assets/images/emblem.png',
'assets/images/app_icon.png',
width: 88,
height: 88,
fit: BoxFit.cover,
errorBuilder: (_, _, _) => Container(
width: 88,
height: 88,
color: colors.primaryContainer,
child: const Icon(
Icons.spa,
decoration: BoxDecoration(
color: colors.primaryContainer,
borderRadius: BorderRadius.circular(22),
),
child: Icon(
Icons.explore,
size: 44,
color: Colors.white,
color: colors.onPrimary,
),
),
),
+6 -7
View File
@@ -5,7 +5,6 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/values_sort_provider.dart';
import '../utils/app_theme.dart';
import '../widgets/practice_chrome.dart';
class WelcomeScreen extends StatelessWidget {
@@ -136,9 +135,9 @@ class WelcomeScreen extends StatelessWidget {
Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: AppTheme.surfaceContainerLow,
color: colors.surfaceContainerLow,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: AppTheme.outlineVariant),
border: Border.all(color: colors.outlineVariant),
),
child: Row(
children: [
@@ -214,7 +213,7 @@ class _GuideStep extends StatelessWidget {
children: [
CircleAvatar(
radius: 12,
backgroundColor: AppTheme.surfaceContainerHigh,
backgroundColor: colors.surfaceContainerHigh,
child: Text(
'$index',
style: text.labelMedium?.copyWith(
@@ -266,12 +265,12 @@ class _CardFan extends StatelessWidget {
height: hero ? 155 : 135,
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: hero ? Colors.white : AppTheme.surfaceContainer,
color: hero ? colors.surface : colors.surfaceContainer,
borderRadius: BorderRadius.circular(20),
border: Border.all(color: AppTheme.outlineVariant),
border: Border.all(color: colors.outlineVariant),
boxShadow: [
BoxShadow(
color: const Color(0x142C2825),
color: colors.shadow.withValues(alpha: 0.08),
blurRadius: hero ? 16 : 8,
offset: const Offset(0, 4),
),
+1 -1
View File
@@ -30,7 +30,7 @@ class StorageService {
return _prefs.setString(StorageKeys.themeMode, value);
}
/// Serialized Values Sort session JSON, or null when none saved.
/// Serialized Values Compass session JSON, or null when none saved.
String? get sortSessionJson => _prefs.getString(StorageKeys.sortSession);
Future<void> setSortSessionJson(String json) {
+2 -11
View File
@@ -1,11 +1,8 @@
/// Shared string keys, timings, and layout breakpoints used across the app.
/// Shared string keys, timings, and layout used across the app.
library;
/// Human-readable name shown in the splash screen and brand chrome.
const String kAppName = 'Values Sort';
/// Package / Android application id.
const String kPackageName = 'com.example.myapp';
const String kAppName = 'Values Compass';
/// How long the splash screen stays visible before navigating onward.
const Duration kSplashDuration = Duration(milliseconds: 1400);
@@ -16,11 +13,5 @@ abstract final class StorageKeys {
static const sortSession = 'values_sort_session_v1';
}
/// Width thresholds used to adapt padding and card density.
abstract final class Breakpoints {
static const compact = 600.0;
static const medium = 840.0;
}
/// Maximum content width on tablets so reflective flows stay readable.
const double kMaxContentWidth = 600.0;
+11 -7
View File
@@ -1,4 +1,4 @@
/// Shared layout chrome for Values Sort screens.
/// Shared layout chrome for Values Compass screens.
library;
import 'package:flutter/material.dart';
@@ -44,7 +44,7 @@ class PracticeScaffold extends StatelessWidget {
child: ThemeToggleButton(),
),
),
if (header != null) header!,
?header,
Expanded(
child: Padding(
padding: pad,
@@ -76,23 +76,27 @@ class BrandMark extends StatelessWidget {
return Row(
children: [
ClipOval(
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.asset(
'assets/images/emblem.png',
'assets/images/app_icon.png',
width: 32,
height: 32,
fit: BoxFit.cover,
errorBuilder: (_, _, _) => Container(
width: 32,
height: 32,
color: colors.primaryContainer,
child: Icon(Icons.spa, size: 18, color: colors.onPrimary),
decoration: BoxDecoration(
color: colors.primaryContainer,
borderRadius: BorderRadius.circular(8),
),
child: Icon(Icons.explore, size: 18, color: colors.onPrimary),
),
),
),
const SizedBox(width: 8),
Text(
'VALUES SORT',
'VALUES COMPASS',
style: labels.labelSmall?.copyWith(
letterSpacing: 2.2,
color: colors.onSurfaceVariant,
-8
View File
@@ -65,14 +65,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.7"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd"
url: "https://pub.dev"
source: hosted
version: "1.0.9"
fake_async:
dependency: transitive
description:
+4 -50
View File
@@ -1,5 +1,5 @@
name: myapp
description: Values Sort — a guided card-sort practice for clarifying personal values.
name: valuecards
description: Values Compass — a guided card-sort practice for clarifying personal values.
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
@@ -21,19 +21,10 @@ version: 1.0.0+1
environment:
sdk: ^3.13.2
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8
provider: ^6.1.5+1
shared_preferences: ^2.5.5
google_fonts: ^8.2.1
@@ -42,50 +33,13 @@ dev_dependencies:
flutter_test:
sdk: flutter
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^6.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
assets:
- assets/data/values_deck.json
- assets/images/emblem.png
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/to/asset-from-package
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/to/font-from-package
- assets/images/app_icon.png
- assets/images/app_icon_1024.png
+12
View File
@@ -106,5 +106,17 @@
"deviceType": "MOBILE",
"htmlFile": "practice-hub.html",
"screenshotFile": "practice-hub.png"
},
{
"id": "2ff051bf1fe5482699a5f1bb9a5e980d",
"slug": "app-icon",
"title": "Values Compass App Icon (Values Top / Compass Bottom)",
"width": "512",
"height": "512",
"deviceType": "ICON",
"htmlFile": "app-icon.svg",
"screenshotFile": "app-icon.png",
"htmlBytes": 6549,
"screenshotBytes": 85489
}
]
+125
View File
@@ -0,0 +1,125 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="100%" height="100%">
<defs>
<!-- Background subtle gradient: warm linen into soft stone -->
<linearGradient id="bgGrad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#FAF6F0"/>
<stop offset="100%" stop-color="#EFE9DE"/>
</linearGradient>
<!-- Card 1 (back) -->
<linearGradient id="cardBackGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#E8E0D2"/>
<stop offset="100%" stop-color="#DDD3C3"/>
</linearGradient>
<!-- Card 2 (middle) -->
<linearGradient id="cardMidGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#F4EFE6"/>
<stop offset="100%" stop-color="#E5DDD0"/>
</linearGradient>
<!-- Card 3 (front/core anchor) -->
<linearGradient id="cardFrontGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#FFFFFF"/>
<stop offset="100%" stop-color="#FAF7F1"/>
</linearGradient>
<!-- Gold / Warm Taupe Compass Accent -->
<linearGradient id="goldAccent" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#C4975E"/>
<stop offset="50%" stop-color="#7D6B58"/>
<stop offset="100%" stop-color="#5E4E3E"/>
</linearGradient>
<!-- Drop Shadows -->
<filter id="shadowBack" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="5" stdDeviation="7" flood-color="#4A3F35" flood-opacity="0.08"/>
</filter>
<filter id="shadowMid" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="8" stdDeviation="10" flood-color="#4A3F35" flood-opacity="0.09"/>
</filter>
<filter id="shadowFront" x="-25%" y="-20%" width="150%" height="150%">
<feDropShadow dx="0" dy="12" stdDeviation="14" flood-color="#4A3F35" flood-opacity="0.12"/>
</filter>
<filter id="glowCenter" x="-30%" y="-30%" width="160%" height="160%">
<feDropShadow dx="0" dy="2" stdDeviation="4" flood-color="#C4975E" flood-opacity="0.25"/>
</filter>
</defs>
<!-- Squircle Base Container for App Icon -->
<rect width="512" height="512" rx="116" fill="url(#bgGrad)"/>
<!-- Subtle tactile border -->
<rect width="508" height="508" x="2" y="2" rx="114" fill="none" stroke="#FFFFFF" stroke-width="2.5" stroke-opacity="0.7"/>
<rect width="510" height="510" x="1" y="1" rx="115" fill="none" stroke="#D8CEBF" stroke-width="1" stroke-opacity="0.35"/>
<!-- TOP TEXT: "Values" in even bigger font -->
<text x="256" y="96" font-family="'Newsreader', 'Georgia', serif" font-size="46" font-weight="700" fill="#2E241C" text-anchor="middle" letter-spacing="1.2">Values</text>
<!-- CENTER DECK: Layered Cards (Vertically centered between y=120 and y=390, centered at y=255) -->
<!-- BACK CARD -->
<g transform="translate(256, 255) rotate(-9) translate(-256, -255)" filter="url(#shadowBack)">
<rect x="168" y="148" width="176" height="214" rx="24" fill="url(#cardBackGrad)" stroke="#D0C5B4" stroke-width="1.5"/>
<line x1="198" y1="180" x2="252" y2="180" stroke="#B8AC9B" stroke-width="3" stroke-linecap="round" opacity="0.6"/>
</g>
<!-- MIDDLE CARD -->
<g transform="translate(256, 255) rotate(7) translate(-256, -255)" filter="url(#shadowMid)">
<rect x="168" y="148" width="176" height="214" rx="24" fill="url(#cardMidGrad)" stroke="#E0D7C9" stroke-width="1.5"/>
<line x1="198" y1="180" x2="270" y2="180" stroke="#A89B8A" stroke-width="3" stroke-linecap="round" opacity="0.6"/>
</g>
<!-- FRONT CARD -->
<g filter="url(#shadowFront)">
<!-- Primary Card Body -->
<rect x="168" y="142" width="176" height="226" rx="24" fill="url(#cardFrontGrad)" stroke="#ECE4D8" stroke-width="1.5"/>
<!-- Inset border line for tactile editorial paper texture -->
<rect x="178" y="152" width="156" height="206" rx="18" fill="none" stroke="#7D6B58" stroke-width="1" stroke-opacity="0.12" stroke-dasharray="3 3"/>
<!-- Pill indicator -->
<rect x="238" y="163" width="36" height="3" rx="1.5" fill="#7D6B58" fill-opacity="0.25"/>
<!-- CENTER EMBLEM: Compass / North Star Icon -->
<g transform="translate(256, 252)">
<!-- Outer delicate concentric aura circles -->
<circle cx="0" cy="0" r="48" fill="none" stroke="#7D6B58" stroke-opacity="0.1" stroke-width="1"/>
<circle cx="0" cy="0" r="37" fill="#FAF6F0" stroke="#7D6B58" stroke-opacity="0.18" stroke-width="1.2"/>
<!-- 4 cardinal tick marks -->
<line x1="0" y1="-43" x2="0" y2="-39" stroke="#7D6B58" stroke-width="1.5" stroke-linecap="round" opacity="0.5"/>
<line x1="0" y1="39" x2="0" y2="43" stroke="#7D6B58" stroke-width="1.5" stroke-linecap="round" opacity="0.5"/>
<line x1="-43" y1="0" x2="-39" y2="0" stroke="#7D6B58" stroke-width="1.5" stroke-linecap="round" opacity="0.5"/>
<line x1="39" y1="0" x2="43" y2="0" stroke="#7D6B58" stroke-width="1.5" stroke-linecap="round" opacity="0.5"/>
<!-- 8-Pointed Mindfulness Star / Compass Star of Core Values -->
<!-- North Point -->
<path d="M 0,0 L -6,-11 L 0,-33 L 6,-11 Z" fill="url(#goldAccent)" filter="url(#glowCenter)"/>
<!-- South Point -->
<path d="M 0,0 L -6,11 L 0,33 L 6,11 Z" fill="#7D6B58"/>
<!-- East Point -->
<path d="M 0,0 L 11,-6 L 33,0 L 11,6 Z" fill="#8C7965"/>
<!-- West Point -->
<path d="M 0,0 L -11,-6 L -33,0 L -11,6 Z" fill="#6F5E4E"/>
<!-- Diagonal Secondary Petals -->
<path d="M 0,0 L 4,-8 L 16,-16 L 8,-4 Z" fill="#A48C74" opacity="0.85"/>
<path d="M 0,0 L -4,-8 L -16,-16 L -8,-4 Z" fill="#917A63" opacity="0.85"/>
<path d="M 0,0 L 4,8 L 16,16 L 8,4 Z" fill="#786552" opacity="0.85"/>
<path d="M 0,0 L -4,8 L -16,16 L -8,4 Z" fill="#6A5846" opacity="0.85"/>
<!-- Center Stillness Core -->
<circle cx="0" cy="0" r="6" fill="#FAF6F0" stroke="#7D6B58" stroke-width="1.8"/>
<circle cx="0" cy="0" r="2.8" fill="#C4975E"/>
</g>
<!-- Three grounded dots at bottom of card -->
<g transform="translate(256, 332)">
<circle cx="-13" cy="0" r="2.4" fill="#7D6B58" opacity="0.25"/>
<circle cx="0" cy="0" r="2.8" fill="#C4975E" opacity="0.8"/>
<circle cx="13" cy="0" r="2.4" fill="#7D6B58" opacity="0.25"/>
</g>
</g>
<!-- BOTTOM TEXT: "Compass" in even bigger font -->
<text x="256" y="445" font-family="'Newsreader', 'Georgia', serif" font-size="46" font-weight="700" fill="#2E241C" text-anchor="middle" letter-spacing="1.2">Compass</text>
</svg>

After

Width:  |  Height:  |  Size: 6.4 KiB

+15
View File
@@ -0,0 +1,15 @@
{
"name": "projects/9343387512370733994/screens/2ff051bf1fe5482699a5f1bb9a5e980d",
"title": "Values Compass App Icon (Values Top / Compass Bottom)",
"screenshot": {
"name": "projects/9343387512370733994/screens/37733e82516730535cc8c45d50f9c83977990ea75683fd3b9c5aae43deb599ac/fileEntries/screenshot",
"downloadUrl": "https://lh3.googleusercontent.com/aida/AEtjO1V7dhzWlGzADkZJ8s9ld_l4GvEdZrbr95uqltFAiNz4A3amMuVlieFKXgEeSbXi9JXw5EKVu9kizHY2o62tAWpZ81-RddVCw_8KXWcvWjEwOxPzALUFySqbtXkuW2ghbCe9-Ne-L9KTjubvBLv9MSOaPyEpIKNrbfqlh53CoYtCc9g1UunfdGYMPYXefoJ6BHDS2eERw2Y1JZdS5lW-s65D_qWCwZ4zBjMaDa66GOcTexVMKFo7DA-kt4s"
},
"htmlCode": {
"name": "projects/9343387512370733994/screens/2ff051bf1fe5482699a5f1bb9a5e980d/fileEntries/html",
"downloadUrl": "https://contribution.usercontent.google.com/download?c=CgthaWRhX2NvZGVmeBJ7Eh1hcHBfY29tcGFuaW9uX2dlbmVyYXRlZF9maWxlcxpaCiVodG1sXzAwMDY1YmI1MTMzNzJjM2YwNDRmNDUwZGQ3M2RhNGY3EgsSBxD_iY311x0YAZIBIwoKcHJvamVjdF9pZBIVQhM5MzQzMzg3NTEyMzcwNzMzOTk0&filename=&opi=89354086",
"mimeType": "image/svg+xml"
},
"width": "512",
"height": "512"
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

+125
View File
@@ -0,0 +1,125 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="100%" height="100%">
<defs>
<!-- Background subtle gradient: warm linen into soft stone -->
<linearGradient id="bgGrad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#FAF6F0"/>
<stop offset="100%" stop-color="#EFE9DE"/>
</linearGradient>
<!-- Card 1 (back) -->
<linearGradient id="cardBackGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#E8E0D2"/>
<stop offset="100%" stop-color="#DDD3C3"/>
</linearGradient>
<!-- Card 2 (middle) -->
<linearGradient id="cardMidGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#F4EFE6"/>
<stop offset="100%" stop-color="#E5DDD0"/>
</linearGradient>
<!-- Card 3 (front/core anchor) -->
<linearGradient id="cardFrontGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#FFFFFF"/>
<stop offset="100%" stop-color="#FAF7F1"/>
</linearGradient>
<!-- Gold / Warm Taupe Compass Accent -->
<linearGradient id="goldAccent" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#C4975E"/>
<stop offset="50%" stop-color="#7D6B58"/>
<stop offset="100%" stop-color="#5E4E3E"/>
</linearGradient>
<!-- Drop Shadows -->
<filter id="shadowBack" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="5" stdDeviation="7" flood-color="#4A3F35" flood-opacity="0.08"/>
</filter>
<filter id="shadowMid" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="8" stdDeviation="10" flood-color="#4A3F35" flood-opacity="0.09"/>
</filter>
<filter id="shadowFront" x="-25%" y="-20%" width="150%" height="150%">
<feDropShadow dx="0" dy="12" stdDeviation="14" flood-color="#4A3F35" flood-opacity="0.12"/>
</filter>
<filter id="glowCenter" x="-30%" y="-30%" width="160%" height="160%">
<feDropShadow dx="0" dy="2" stdDeviation="4" flood-color="#C4975E" flood-opacity="0.25"/>
</filter>
</defs>
<!-- Squircle Base Container for App Icon -->
<rect width="512" height="512" rx="116" fill="url(#bgGrad)"/>
<!-- Subtle tactile border -->
<rect width="508" height="508" x="2" y="2" rx="114" fill="none" stroke="#FFFFFF" stroke-width="2.5" stroke-opacity="0.7"/>
<rect width="510" height="510" x="1" y="1" rx="115" fill="none" stroke="#D8CEBF" stroke-width="1" stroke-opacity="0.35"/>
<!-- TOP TEXT: "Values" in even bigger font -->
<text x="256" y="96" font-family="'Newsreader', 'Georgia', serif" font-size="46" font-weight="700" fill="#2E241C" text-anchor="middle" letter-spacing="1.2">Values</text>
<!-- CENTER DECK: Layered Cards (Vertically centered between y=120 and y=390, centered at y=255) -->
<!-- BACK CARD -->
<g transform="translate(256, 255) rotate(-9) translate(-256, -255)" filter="url(#shadowBack)">
<rect x="168" y="148" width="176" height="214" rx="24" fill="url(#cardBackGrad)" stroke="#D0C5B4" stroke-width="1.5"/>
<line x1="198" y1="180" x2="252" y2="180" stroke="#B8AC9B" stroke-width="3" stroke-linecap="round" opacity="0.6"/>
</g>
<!-- MIDDLE CARD -->
<g transform="translate(256, 255) rotate(7) translate(-256, -255)" filter="url(#shadowMid)">
<rect x="168" y="148" width="176" height="214" rx="24" fill="url(#cardMidGrad)" stroke="#E0D7C9" stroke-width="1.5"/>
<line x1="198" y1="180" x2="270" y2="180" stroke="#A89B8A" stroke-width="3" stroke-linecap="round" opacity="0.6"/>
</g>
<!-- FRONT CARD -->
<g filter="url(#shadowFront)">
<!-- Primary Card Body -->
<rect x="168" y="142" width="176" height="226" rx="24" fill="url(#cardFrontGrad)" stroke="#ECE4D8" stroke-width="1.5"/>
<!-- Inset border line for tactile editorial paper texture -->
<rect x="178" y="152" width="156" height="206" rx="18" fill="none" stroke="#7D6B58" stroke-width="1" stroke-opacity="0.12" stroke-dasharray="3 3"/>
<!-- Pill indicator -->
<rect x="238" y="163" width="36" height="3" rx="1.5" fill="#7D6B58" fill-opacity="0.25"/>
<!-- CENTER EMBLEM: Compass / North Star Icon -->
<g transform="translate(256, 252)">
<!-- Outer delicate concentric aura circles -->
<circle cx="0" cy="0" r="48" fill="none" stroke="#7D6B58" stroke-opacity="0.1" stroke-width="1"/>
<circle cx="0" cy="0" r="37" fill="#FAF6F0" stroke="#7D6B58" stroke-opacity="0.18" stroke-width="1.2"/>
<!-- 4 cardinal tick marks -->
<line x1="0" y1="-43" x2="0" y2="-39" stroke="#7D6B58" stroke-width="1.5" stroke-linecap="round" opacity="0.5"/>
<line x1="0" y1="39" x2="0" y2="43" stroke="#7D6B58" stroke-width="1.5" stroke-linecap="round" opacity="0.5"/>
<line x1="-43" y1="0" x2="-39" y2="0" stroke="#7D6B58" stroke-width="1.5" stroke-linecap="round" opacity="0.5"/>
<line x1="39" y1="0" x2="43" y2="0" stroke="#7D6B58" stroke-width="1.5" stroke-linecap="round" opacity="0.5"/>
<!-- 8-Pointed Mindfulness Star / Compass Star of Core Values -->
<!-- North Point -->
<path d="M 0,0 L -6,-11 L 0,-33 L 6,-11 Z" fill="url(#goldAccent)" filter="url(#glowCenter)"/>
<!-- South Point -->
<path d="M 0,0 L -6,11 L 0,33 L 6,11 Z" fill="#7D6B58"/>
<!-- East Point -->
<path d="M 0,0 L 11,-6 L 33,0 L 11,6 Z" fill="#8C7965"/>
<!-- West Point -->
<path d="M 0,0 L -11,-6 L -33,0 L -11,6 Z" fill="#6F5E4E"/>
<!-- Diagonal Secondary Petals -->
<path d="M 0,0 L 4,-8 L 16,-16 L 8,-4 Z" fill="#A48C74" opacity="0.85"/>
<path d="M 0,0 L -4,-8 L -16,-16 L -8,-4 Z" fill="#917A63" opacity="0.85"/>
<path d="M 0,0 L 4,8 L 16,16 L 8,4 Z" fill="#786552" opacity="0.85"/>
<path d="M 0,0 L -4,8 L -16,16 L -8,4 Z" fill="#6A5846" opacity="0.85"/>
<!-- Center Stillness Core -->
<circle cx="0" cy="0" r="6" fill="#FAF6F0" stroke="#7D6B58" stroke-width="1.8"/>
<circle cx="0" cy="0" r="2.8" fill="#C4975E"/>
</g>
<!-- Three grounded dots at bottom of card -->
<g transform="translate(256, 332)">
<circle cx="-13" cy="0" r="2.4" fill="#7D6B58" opacity="0.25"/>
<circle cx="0" cy="0" r="2.8" fill="#C4975E" opacity="0.8"/>
<circle cx="13" cy="0" r="2.4" fill="#7D6B58" opacity="0.25"/>
</g>
</g>
<!-- BOTTOM TEXT: "Compass" in even bigger font -->
<text x="256" y="445" font-family="'Newsreader', 'Georgia', serif" font-size="46" font-weight="700" fill="#2E241C" text-anchor="middle" letter-spacing="1.2">Compass</text>
</svg>

After

Width:  |  Height:  |  Size: 6.4 KiB

+7 -7
View File
@@ -1,18 +1,18 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:myapp/main.dart';
import 'package:myapp/models/value_card.dart';
import 'package:myapp/models/value_pile.dart';
import 'package:myapp/providers/values_sort_provider.dart';
import 'package:myapp/services/storage_service.dart';
import 'package:myapp/utils/constants.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:valuecards/main.dart';
import 'package:valuecards/models/value_card.dart';
import 'package:valuecards/models/value_pile.dart';
import 'package:valuecards/providers/values_sort_provider.dart';
import 'package:valuecards/services/storage_service.dart';
import 'package:valuecards/utils/constants.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
GoogleFonts.config.allowRuntimeFetching = false;
testWidgets('splash shows Values Sort then opens welcome', (tester) async {
testWidgets('splash shows Values Compass then opens welcome', (tester) async {
SharedPreferences.setMockInitialValues({});
final storage = StorageService();
await storage.init();