fix: assets
Container images / publish (., deploy/pocketbase/Dockerfile, evoliohealth-pocketbase) (push) Canceled after 0s
Container images / publish (., server/Dockerfile, evoliohealth-server) (push) Canceled after 0s
Checks / go (push) Canceled after 0s
Checks / flutter (push) Canceled after 0s
Checks / compose (push) Canceled after 0s
Android development APK / apk (push) Canceled after 0s

This commit is contained in:
2026-08-06 17:44:05 +02:00
parent 7ea467e84c
commit eb61478203
17 changed files with 2621 additions and 0 deletions
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 528 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 251 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 KiB

+788
View File
@@ -0,0 +1,788 @@
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'evolio_theme.dart';
import 'strings.dart';
class BiometricsScreen extends StatefulWidget {
const BiometricsScreen({super.key});
@override
State<BiometricsScreen> createState() => _BiometricsScreenState();
}
class _BiometricsScreenState extends State<BiometricsScreen> {
int _selectedDestination = 2;
int _selectedPeriod = 1;
@override
Widget build(BuildContext context) {
final strings = AppStrings.of(context);
return Scaffold(
appBar: AppBar(
backgroundColor: EvolioColors.background.withValues(alpha: 0.96),
surfaceTintColor: Colors.transparent,
titleSpacing: 20,
title: const _Brand(),
actions: [
IconButton(
tooltip: strings.settings,
onPressed: () => _showNotice(strings.scaleUnavailable),
icon: const Icon(Icons.settings_outlined),
),
const SizedBox(width: 8),
],
),
body: SafeArea(
top: false,
child: CustomScrollView(
key: const Key('biometrics-scroll-view'),
slivers: [
SliverPadding(
padding: const EdgeInsets.fromLTRB(20, 20, 20, 112),
sliver: SliverList.list(
children: [
_WeightCard(strings: strings),
const SizedBox(height: 16),
_GradientAction(
icon: Icons.bluetooth_rounded,
label: strings.connectScale,
onPressed: () => _showNotice(strings.scaleUnavailable),
),
const SizedBox(height: 28),
_SectionHeader(title: strings.silhouetteTitle),
const SizedBox(height: 12),
_SilhouetteCard(strings: strings),
const SizedBox(height: 28),
_EvolutionHeader(
strings: strings,
selectedPeriod: _selectedPeriod,
onSelected: (value) =>
setState(() => _selectedPeriod = value),
),
const SizedBox(height: 12),
_ChartCard(strings: strings, selectedPeriod: _selectedPeriod),
const SizedBox(height: 28),
_SectionHeader(title: strings.bodyMeasurements),
const SizedBox(height: 12),
_MetricsGrid(strings: strings),
const SizedBox(height: 16),
_GuidanceCard(strings: strings),
const SizedBox(height: 20),
_GradientAction(
icon: Icons.save_outlined,
label: strings.saveSession,
onPressed: () => _showNotice(strings.saveUnavailable),
),
],
),
),
],
),
),
bottomNavigationBar: NavigationBar(
selectedIndex: _selectedDestination,
onDestinationSelected: (index) {
setState(() => _selectedDestination = index);
},
destinations: [
NavigationDestination(
icon: const Icon(Icons.dashboard_outlined),
selectedIcon: const Icon(Icons.dashboard),
label: strings.overview,
),
NavigationDestination(
icon: const Icon(Icons.directions_run_outlined),
selectedIcon: const Icon(Icons.directions_run),
label: strings.activity,
),
NavigationDestination(
icon: const Icon(Icons.straighten_outlined),
selectedIcon: const Icon(Icons.straighten),
label: strings.measurements,
),
NavigationDestination(
icon: const Icon(Icons.photo_library_outlined),
selectedIcon: const Icon(Icons.photo_library),
label: strings.photos,
),
],
),
);
}
void _showNotice(String message) {
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(SnackBar(content: Text(message)));
}
}
class _Brand extends StatelessWidget {
const _Brand();
@override
Widget build(BuildContext context) {
return Semantics(
label: 'EvolioHealth',
header: true,
excludeSemantics: true,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.asset(
'assets/images/evoliohealth-logo.png',
width: 42,
height: 42,
fit: BoxFit.cover,
semanticLabel: 'EvolioHealth logo',
),
),
const SizedBox(width: 11),
RichText(
text: const TextSpan(
style: TextStyle(
color: EvolioColors.text,
fontSize: 18,
fontWeight: FontWeight.w800,
letterSpacing: -0.6,
),
children: [
TextSpan(text: 'EVOLIO'),
TextSpan(
text: 'HEALTH',
style: TextStyle(color: EvolioColors.cyan),
),
],
),
),
],
),
);
}
}
class _Panel extends StatelessWidget {
const _Panel({
required this.child,
this.padding = const EdgeInsets.all(18),
this.accent,
});
final Widget child;
final EdgeInsets padding;
final Color? accent;
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: EvolioColors.surface,
borderRadius: BorderRadius.circular(18),
border: Border.all(color: Colors.white.withValues(alpha: 0.06)),
),
clipBehavior: Clip.antiAlias,
child: Stack(
children: [
Padding(padding: padding, child: child),
if (accent != null)
Positioned(
left: 0,
top: 0,
bottom: 0,
child: ColoredBox(
color: accent!,
child: const SizedBox(width: 3),
),
),
],
),
);
}
}
class _WeightCard extends StatelessWidget {
const _WeightCard({required this.strings});
final AppStrings strings;
@override
Widget build(BuildContext context) {
return _Panel(
accent: EvolioColors.cyan,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_CapsLabel(strings.currentWeight),
const SizedBox(height: 10),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
'82.4',
style: Theme.of(context).textTheme.displayLarge,
),
const SizedBox(width: 8),
Padding(
padding: const EdgeInsets.only(bottom: 5),
child: Text(
strings.kilograms,
style: const TextStyle(color: EvolioColors.muted),
),
),
],
),
],
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Container(
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 7,
),
decoration: BoxDecoration(
color: EvolioColors.magenta.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(9),
border: Border.all(
color: EvolioColors.magenta.withValues(alpha: 0.35),
),
),
child: const Row(
children: [
Icon(
Icons.south_east_rounded,
size: 16,
color: EvolioColors.magenta,
),
SizedBox(width: 4),
Text(
'-1.2 kg',
style: TextStyle(
color: EvolioColors.magenta,
fontWeight: FontWeight.w700,
),
),
],
),
),
const SizedBox(height: 8),
Text(
strings.dateLabel,
style: Theme.of(context).textTheme.labelSmall,
),
],
),
],
),
);
}
}
class _GradientAction extends StatelessWidget {
const _GradientAction({
required this.icon,
required this.label,
required this.onPressed,
});
final IconData icon;
final String label;
final VoidCallback onPressed;
@override
Widget build(BuildContext context) {
return Semantics(
button: true,
child: DecoratedBox(
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [EvolioColors.cyan, EvolioColors.lime],
),
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: EvolioColors.cyan.withValues(alpha: 0.16),
blurRadius: 18,
offset: const Offset(0, 8),
),
],
),
child: FilledButton.icon(
onPressed: onPressed,
style: FilledButton.styleFrom(
foregroundColor: const Color(0xFF09201E),
backgroundColor: Colors.transparent,
shadowColor: Colors.transparent,
minimumSize: const Size.fromHeight(54),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
icon: Icon(icon, size: 20),
label: Text(
label.toUpperCase(),
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w800,
letterSpacing: 1.5,
),
),
),
),
);
}
}
class _SectionHeader extends StatelessWidget {
const _SectionHeader({required this.title});
final String title;
@override
Widget build(BuildContext context) {
return Text(
title.toUpperCase(),
style: Theme.of(context).textTheme.titleMedium,
);
}
}
class _SilhouetteCard extends StatelessWidget {
const _SilhouetteCard({required this.strings});
final AppStrings strings;
@override
Widget build(BuildContext context) {
return _Panel(
padding: EdgeInsets.zero,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(18, 18, 18, 6),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_CapsLabel(strings.silhouetteHint),
const SizedBox(height: 5),
Text(
strings.silhouetteDisclaimer,
style: const TextStyle(
color: EvolioColors.muted,
fontSize: 12,
height: 1.35,
),
),
],
),
),
SizedBox(
height: 260,
width: double.infinity,
child: Semantics(
image: true,
label: strings.silhouetteTitle,
child: const CustomPaint(painter: _SilhouettePainter()),
),
),
],
),
);
}
}
class _SilhouettePainter extends CustomPainter {
const _SilhouettePainter();
@override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
final glow = Paint()
..shader =
RadialGradient(
colors: [
EvolioColors.cyan.withValues(alpha: 0.18),
EvolioColors.surface.withValues(alpha: 0),
],
).createShader(
Rect.fromCircle(center: center, radius: size.shortestSide * 0.48),
);
canvas.drawCircle(center, size.shortestSide * 0.48, glow);
final grid = Paint()
..color = Colors.white.withValues(alpha: 0.035)
..strokeWidth = 1;
for (double x = 18; x < size.width; x += 24) {
canvas.drawLine(Offset(x, 0), Offset(x, size.height), grid);
}
for (double y = 18; y < size.height; y += 24) {
canvas.drawLine(Offset(0, y), Offset(size.width, y), grid);
}
final outline = Paint()
..shader = const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [EvolioColors.cyan, EvolioColors.lime],
).createShader(Rect.fromLTWH(0, 20, size.width, size.height - 30))
..style = PaintingStyle.stroke
..strokeWidth = 2.2;
final fill = Paint()
..color = EvolioColors.cyan.withValues(alpha: 0.07)
..style = PaintingStyle.fill;
final x = size.width / 2;
final body = Path()
..addOval(Rect.fromCircle(center: Offset(x, 46), radius: 22))
..moveTo(x - 18, 70)
..cubicTo(x - 48, 88, x - 47, 122, x - 38, 151)
..cubicTo(x - 34, 171, x - 42, 205, x - 34, 242)
..lineTo(x - 13, 242)
..cubicTo(x - 8, 211, x - 7, 184, x, 169)
..cubicTo(x + 7, 184, x + 8, 211, x + 13, 242)
..lineTo(x + 34, 242)
..cubicTo(x + 42, 205, x + 34, 171, x + 38, 151)
..cubicTo(x + 47, 122, x + 48, 88, x + 18, 70)
..close();
canvas.drawPath(body, fill);
canvas.drawPath(body, outline);
final marker = Paint()..color = EvolioColors.magenta;
for (final y in [105.0, 139.0, 168.0]) {
canvas.drawCircle(Offset(x + 39, y), 3, marker);
canvas.drawLine(
Offset(x + 39, y),
Offset(x + 70, y),
Paint()
..color = EvolioColors.magenta.withValues(alpha: 0.5)
..strokeWidth = 1,
);
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
class _EvolutionHeader extends StatelessWidget {
const _EvolutionHeader({
required this.strings,
required this.selectedPeriod,
required this.onSelected,
});
final AppStrings strings;
final int selectedPeriod;
final ValueChanged<int> onSelected;
@override
Widget build(BuildContext context) {
final periods = [strings.oneWeek, strings.oneMonth, strings.sixMonths];
return Row(
children: [
Expanded(child: _SectionHeader(title: strings.evolution)),
SegmentedButton<int>(
segments: [
for (var i = 0; i < periods.length; i++)
ButtonSegment(value: i, label: Text(periods[i])),
],
selected: {selectedPeriod},
onSelectionChanged: (selection) => onSelected(selection.first),
showSelectedIcon: false,
style: ButtonStyle(
visualDensity: VisualDensity.compact,
textStyle: const WidgetStatePropertyAll(
TextStyle(fontSize: 10, fontWeight: FontWeight.w700),
),
padding: const WidgetStatePropertyAll(
EdgeInsets.symmetric(horizontal: 9),
),
),
),
],
);
}
}
class _ChartCard extends StatelessWidget {
const _ChartCard({required this.strings, required this.selectedPeriod});
final AppStrings strings;
final int selectedPeriod;
@override
Widget build(BuildContext context) {
return _Panel(
padding: const EdgeInsets.fromLTRB(12, 18, 12, 12),
child: SizedBox(
height: 170,
child: Stack(
children: [
Positioned.fill(
child: CustomPaint(
painter: _TrendPainter(period: selectedPeriod),
),
),
Positioned(
left: 6,
right: 6,
bottom: 0,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'01.07',
style: TextStyle(color: EvolioColors.muted, fontSize: 10),
),
const Text(
'10.07',
style: TextStyle(color: EvolioColors.muted, fontSize: 10),
),
Text(
strings.latest.toUpperCase(),
style: const TextStyle(
color: EvolioColors.magenta,
fontSize: 10,
fontWeight: FontWeight.w700,
),
),
],
),
),
],
),
),
);
}
}
class _TrendPainter extends CustomPainter {
const _TrendPainter({required this.period});
final int period;
@override
void paint(Canvas canvas, Size size) {
final chartHeight = size.height - 26;
final gridPaint = Paint()
..color = Colors.white.withValues(alpha: 0.05)
..strokeWidth = 1;
for (var i = 0; i < 4; i++) {
final y = chartHeight * i / 3;
canvas.drawLine(Offset(0, y), Offset(size.width, y), gridPaint);
}
final phase = period * 0.35;
final line = Path();
for (var i = 0; i <= 60; i++) {
final progress = i / 60;
final wave = math.sin(progress * math.pi * 3 + phase) * 8;
final y = chartHeight * (0.72 - progress * 0.32) + wave;
final point = Offset(size.width * progress, y);
if (i == 0) {
line.moveTo(point.dx, point.dy);
} else {
line.lineTo(point.dx, point.dy);
}
}
final area = Path.from(line)
..lineTo(size.width, chartHeight)
..lineTo(0, chartHeight)
..close();
canvas.drawPath(
area,
Paint()
..shader = LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
EvolioColors.magenta.withValues(alpha: 0.3),
EvolioColors.magenta.withValues(alpha: 0),
],
).createShader(Rect.fromLTWH(0, 0, size.width, chartHeight)),
);
canvas.drawPath(
line,
Paint()
..color = EvolioColors.magenta
..style = PaintingStyle.stroke
..strokeWidth = 2.2
..strokeCap = StrokeCap.round,
);
}
@override
bool shouldRepaint(covariant _TrendPainter oldDelegate) =>
oldDelegate.period != period;
}
class _MetricsGrid extends StatelessWidget {
const _MetricsGrid({required this.strings});
final AppStrings strings;
@override
Widget build(BuildContext context) {
final metrics = [
(strings.chest, '104.5', Icons.straighten, EvolioColors.cyan),
(strings.waist, '82.0', Icons.accessibility_new, EvolioColors.lime),
(strings.hips, '98.2', Icons.data_usage, EvolioColors.magenta),
(strings.arms, '38.5', Icons.fitness_center, const Color(0xFFFFB4EE)),
];
return LayoutBuilder(
builder: (context, constraints) {
final columnCount = constraints.maxWidth >= 620 ? 4 : 2;
final width =
(constraints.maxWidth - 12 * (columnCount - 1)) / columnCount;
return Wrap(
spacing: 12,
runSpacing: 12,
children: [
for (final metric in metrics)
SizedBox(
width: width,
child: _MetricCard(
label: metric.$1,
value: metric.$2,
icon: metric.$3,
accent: metric.$4,
unit: strings.centimeters,
),
),
],
);
},
);
}
}
class _MetricCard extends StatelessWidget {
const _MetricCard({
required this.label,
required this.value,
required this.icon,
required this.accent,
required this.unit,
});
final String label;
final String value;
final IconData icon;
final Color accent;
final String unit;
@override
Widget build(BuildContext context) {
return _Panel(
accent: accent.withValues(alpha: 0.55),
padding: const EdgeInsets.all(15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(child: _CapsLabel(label)),
Icon(icon, color: accent, size: 19),
],
),
const SizedBox(height: 17),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(
child: TextFormField(
initialValue: value,
keyboardType: const TextInputType.numberWithOptions(
decimal: true,
),
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.w700,
),
decoration: InputDecoration(
semanticCounterText: '$label, $unit',
),
),
),
Padding(
padding: const EdgeInsets.only(bottom: 4),
child: Text(
unit,
style: const TextStyle(
color: EvolioColors.muted,
fontSize: 11,
),
),
),
],
),
],
),
);
}
}
class _GuidanceCard extends StatelessWidget {
const _GuidanceCard({required this.strings});
final AppStrings strings;
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: EvolioColors.cyan.withValues(alpha: 0.07),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: EvolioColors.cyan.withValues(alpha: 0.18)),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Icon(
Icons.lightbulb_outline,
color: EvolioColors.cyan,
size: 19,
),
const SizedBox(width: 10),
Expanded(
child: Text(
strings.measurementGuidance,
style: const TextStyle(
color: EvolioColors.muted,
fontSize: 12,
height: 1.4,
),
),
),
],
),
);
}
}
class _CapsLabel extends StatelessWidget {
const _CapsLabel(this.text);
final String text;
@override
Widget build(BuildContext context) {
return Text(
text.toUpperCase(),
style: Theme.of(context).textTheme.labelSmall,
);
}
}
+81
View File
@@ -0,0 +1,81 @@
import 'package:flutter/material.dart';
abstract final class EvolioColors {
static const background = Color(0xFF101315);
static const surface = Color(0xFF191D20);
static const surfaceRaised = Color(0xFF22272B);
static const cyan = Color(0xFF51E5DC);
static const lime = Color(0xFFB8EE70);
static const magenta = Color(0xFFE77ADD);
static const text = Color(0xFFF0F4F3);
static const muted = Color(0xFF9DA9A7);
}
abstract final class EvolioTheme {
static ThemeData get dark {
const scheme = ColorScheme.dark(
primary: EvolioColors.cyan,
onPrimary: Color(0xFF00201E),
secondary: EvolioColors.magenta,
onSecondary: Color(0xFF32102F),
tertiary: EvolioColors.lime,
onTertiary: Color(0xFF172500),
surface: EvolioColors.surface,
onSurface: EvolioColors.text,
error: Color(0xFFFFB4AB),
);
return ThemeData(
brightness: Brightness.dark,
colorScheme: scheme,
scaffoldBackgroundColor: EvolioColors.background,
useMaterial3: true,
fontFamily: 'sans-serif',
textTheme: const TextTheme(
displayLarge: TextStyle(
color: EvolioColors.text,
fontSize: 48,
height: 1,
fontWeight: FontWeight.w800,
letterSpacing: -1.5,
),
headlineMedium: TextStyle(
color: EvolioColors.text,
fontSize: 24,
height: 1.2,
fontWeight: FontWeight.w700,
),
titleMedium: TextStyle(
color: EvolioColors.text,
fontSize: 13,
fontWeight: FontWeight.w700,
letterSpacing: 1.5,
),
bodyMedium: TextStyle(
color: EvolioColors.text,
fontSize: 15,
height: 1.45,
),
labelSmall: TextStyle(
color: EvolioColors.muted,
fontSize: 11,
fontWeight: FontWeight.w700,
letterSpacing: 1.25,
),
),
navigationBarTheme: const NavigationBarThemeData(
backgroundColor: EvolioColors.background,
indicatorColor: Color(0x2451E5DC),
height: 72,
labelTextStyle: WidgetStatePropertyAll(
TextStyle(fontSize: 10, fontWeight: FontWeight.w700),
),
),
inputDecorationTheme: const InputDecorationTheme(
border: InputBorder.none,
contentPadding: EdgeInsets.zero,
isDense: true,
),
);
}
}
+57
View File
@@ -0,0 +1,57 @@
import 'package:flutter/material.dart';
class AppStrings {
const AppStrings._(this.locale);
final Locale locale;
static const supportedLocales = [Locale('en'), Locale('fr')];
static AppStrings of(BuildContext context) {
return AppStrings._(Localizations.localeOf(context));
}
bool get _fr => locale.languageCode == 'fr';
String get appName => 'EvolioHealth';
String get overview => _fr ? 'Aperçu' : 'Overview';
String get activity => _fr ? 'Activité' : 'Activity';
String get measurements => _fr ? 'Mesures' : 'Measurements';
String get photos => _fr ? 'Photos' : 'Photos';
String get settings => _fr ? 'Paramètres' : 'Settings';
String get currentWeight => _fr ? 'Poids actuel' : 'Current weight';
String get weightChange =>
_fr ? 'depuis la dernière mesure' : 'since last measurement';
String get connectScale => _fr ? 'Connecter la balance' : 'Connect scale';
String get scaleUnavailable => _fr
? 'La connexion BLE sera disponible dans une prochaine étape.'
: 'BLE connection will be available in a future step.';
String get silhouetteTitle =>
_fr ? 'Silhouette indicative' : 'Indicative silhouette';
String get silhouetteHint => _fr
? 'Aperçu paramétrique basé sur vos mesures'
: 'Parametric preview based on your measurements';
String get silhouetteDisclaimer => _fr
? 'Visualisation indicative uniquement — ce nest ni une reconstruction fidèle ni une analyse médicale.'
: 'Indicative visualization only — not a faithful reconstruction or medical analysis.';
String get evolution => _fr ? 'Évolution' : 'Evolution';
String get oneWeek => _fr ? '1 sem.' : '1 wk';
String get oneMonth => _fr ? '1 mois' : '1 mo';
String get sixMonths => _fr ? '6 mois' : '6 mo';
String get latest => _fr ? 'Récent' : 'Latest';
String get bodyMeasurements => _fr ? 'Mensurations' : 'Body measurements';
String get chest => _fr ? 'Poitrine' : 'Chest';
String get waist => _fr ? 'Taille' : 'Waist';
String get hips => _fr ? 'Hanches' : 'Hips';
String get arms => _fr ? 'Bras' : 'Arms';
String get saveSession => _fr ? 'Enregistrer la session' : 'Save session';
String get saveUnavailable => _fr
? 'Cette maquette ne persiste pas encore les mesures.'
: 'This prototype does not persist measurements yet.';
String get measurementGuidance => _fr
? 'Pour suivre une tendance fiable, mesurez-vous dans des conditions similaires.'
: 'For a reliable trend, measure under similar conditions.';
String get dateLabel => _fr ? '14 juillet' : 'July 14';
String get kilograms => 'kg';
String get centimeters => 'cm';
}
+218
View File
@@ -0,0 +1,218 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
async:
dependency: transitive
description:
name: async
sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37
url: "https://pub.dev"
source: hosted
version: "2.13.1"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
characters:
dependency: transitive
description:
name: characters
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
url: "https://pub.dev"
source: hosted
version: "1.4.1"
clock:
dependency: transitive
description:
name: clock
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
url: "https://pub.dev"
source: hosted
version: "1.1.2"
collection:
dependency: transitive
description:
name: collection
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
url: "https://pub.dev"
source: hosted
version: "1.19.1"
fake_async:
dependency: transitive
description:
name: fake_async
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.dev"
source: hosted
version: "1.3.3"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1"
url: "https://pub.dev"
source: hosted
version: "6.0.0"
flutter_localizations:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
intl:
dependency: transitive
description:
name: intl
sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
url: "https://pub.dev"
source: hosted
version: "0.20.2"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
url: "https://pub.dev"
source: hosted
version: "11.0.2"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
url: "https://pub.dev"
source: hosted
version: "3.0.10"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
lints:
dependency: transitive
description:
name: lints
sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df"
url: "https://pub.dev"
source: hosted
version: "6.1.0"
matcher:
dependency: transitive
description:
name: matcher
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
url: "https://pub.dev"
source: hosted
version: "0.12.19"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
url: "https://pub.dev"
source: hosted
version: "0.13.0"
meta:
dependency: transitive
description:
name: meta
sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
url: "https://pub.dev"
source: hosted
version: "1.18.0"
path:
dependency: transitive
description:
name: path
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
url: "https://pub.dev"
source: hosted
version: "1.9.1"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
source_span:
dependency: transitive
description:
name: source_span
sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab"
url: "https://pub.dev"
source: hosted
version: "1.10.2"
stack_trace:
dependency: transitive
description:
name: stack_trace
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
url: "https://pub.dev"
source: hosted
version: "1.12.1"
stream_channel:
dependency: transitive
description:
name: stream_channel
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
string_scanner:
dependency: transitive
description:
name: string_scanner
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
url: "https://pub.dev"
source: hosted
version: "1.4.1"
term_glyph:
dependency: transitive
description:
name: term_glyph
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
url: "https://pub.dev"
source: hosted
version: "1.2.2"
test_api:
dependency: transitive
description:
name: test_api
sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
url: "https://pub.dev"
source: hosted
version: "0.7.11"
vector_math:
dependency: transitive
description:
name: vector_math
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
url: "https://pub.dev"
source: hosted
version: "2.2.0"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360"
url: "https://pub.dev"
source: hosted
version: "15.2.0"
sdks:
dart: ">=3.12.0 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54"
Binary file not shown.

After

Width:  |  Height:  |  Size: 528 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 KiB

+77
View File
@@ -0,0 +1,77 @@
import 'package:flutter/material.dart';
abstract final class EvolioColors {
static const background = Color(0xFF101315);
static const surface = Color(0xFF191D20);
static const surfaceRaised = Color(0xFF22272B);
static const cyan = Color(0xFF51E5DC);
static const lime = Color(0xFFB8EE70);
static const magenta = Color(0xFFE77ADD);
static const text = Color(0xFFF0F4F3);
static const muted = Color(0xFF9DA9A7);
}
abstract final class EvolioTheme {
static ThemeData get dark {
const scheme = ColorScheme.dark(
primary: EvolioColors.cyan,
onPrimary: Color(0xFF00201E),
secondary: EvolioColors.magenta,
onSecondary: Color(0xFF32102F),
tertiary: EvolioColors.lime,
onTertiary: Color(0xFF172500),
surface: EvolioColors.surface,
onSurface: EvolioColors.text,
error: Color(0xFFFFB4AB),
);
return ThemeData(
brightness: Brightness.dark,
colorScheme: scheme,
scaffoldBackgroundColor: EvolioColors.background,
useMaterial3: true,
fontFamily: 'sans-serif',
textTheme: const TextTheme(
displayLarge: TextStyle(
color: EvolioColors.text,
fontSize: 52,
height: 1,
fontWeight: FontWeight.w800,
letterSpacing: -1.5,
),
headlineMedium: TextStyle(
color: EvolioColors.text,
fontSize: 26,
height: 1.2,
fontWeight: FontWeight.w700,
),
titleMedium: TextStyle(
color: EvolioColors.text,
fontSize: 13,
fontWeight: FontWeight.w700,
letterSpacing: 1.5,
),
bodyMedium: TextStyle(
color: EvolioColors.text,
fontSize: 15,
height: 1.45,
),
labelSmall: TextStyle(
color: EvolioColors.muted,
fontSize: 11,
fontWeight: FontWeight.w700,
letterSpacing: 1.25,
),
),
inputDecorationTheme: const InputDecorationTheme(
border: InputBorder.none,
contentPadding: EdgeInsets.zero,
isDense: true,
),
snackBarTheme: const SnackBarThemeData(
backgroundColor: EvolioColors.surfaceRaised,
contentTextStyle: TextStyle(color: EvolioColors.text),
),
);
}
}
+62
View File
@@ -0,0 +1,62 @@
import 'package:flutter/material.dart';
class AppStrings {
const AppStrings._(this.locale);
final Locale locale;
static const supportedLocales = [Locale('en'), Locale('fr')];
static AppStrings of(BuildContext context) =>
AppStrings._(Localizations.localeOf(context));
bool get _fr => locale.languageCode == 'fr';
String get appName => 'EvolioHealth';
String get overview => _fr ? 'Aperçu' : 'Overview';
String get activity => _fr ? 'Activité' : 'Activity';
String get measurements => _fr ? 'Mesures' : 'Measurements';
String get photos => _fr ? 'Photos' : 'Photos';
String get exports => _fr ? 'Exports' : 'Exports';
String get settings => _fr ? 'Paramètres' : 'Settings';
String get greeting => _fr ? 'Bonjour, Alex' : 'Hello, Alex';
String get subtitle => _fr
? 'Voici l’évolution de vos mesures personnelles.'
: 'Here is the evolution of your personal measurements.';
String get currentWeight => _fr ? 'Poids actuel' : 'Current weight';
String get lastMeasurement => _fr ? 'Dernière mesure' : 'Last measurement';
String get bodyFat => _fr ? 'Masse grasse estimée' : 'Estimated body fat';
String get pendingSync => _fr ? 'Synchronisation' : 'Synchronization';
String get synchronized => _fr ? 'À jour' : 'Up to date';
String get newSession => _fr ? 'Nouvelle session' : 'New session';
String get prototypeNotice => _fr
? 'Cette maquette nest pas encore connectée à lAPI EvolioHealth.'
: 'This prototype is not connected to the EvolioHealth API yet.';
String get silhouetteTitle =>
_fr ? 'Silhouette indicative' : 'Indicative silhouette';
String get silhouetteHint => _fr
? 'Aperçu paramétrique basé sur vos mesures'
: 'Parametric preview based on your measurements';
String get silhouetteDisclaimer => _fr
? 'Visualisation indicative uniquement — ce nest ni une reconstruction fidèle ni une analyse médicale.'
: 'Indicative visualization only — not a faithful reconstruction or medical analysis.';
String get evolution => _fr ? 'Évolution du poids' : 'Weight evolution';
String get oneWeek => _fr ? '1 sem.' : '1 wk';
String get oneMonth => _fr ? '1 mois' : '1 mo';
String get sixMonths => _fr ? '6 mois' : '6 mo';
String get latest => _fr ? 'Récent' : 'Latest';
String get bodyMeasurements => _fr ? 'Mensurations' : 'Body measurements';
String get chest => _fr ? 'Poitrine' : 'Chest';
String get waist => _fr ? 'Taille' : 'Waist';
String get hips => _fr ? 'Hanches' : 'Hips';
String get arms => _fr ? 'Bras' : 'Arms';
String get shoulders => _fr ? 'Épaules' : 'Shoulders';
String get thighs => _fr ? 'Cuisses' : 'Thighs';
String get history => _fr ? 'Voir lhistorique' : 'View history';
String get guidance => _fr
? 'Pour suivre une tendance fiable, effectuez vos mesures dans des conditions similaires.'
: 'For a reliable trend, take measurements under similar conditions.';
String get dateLabel => _fr ? '14 juillet 2026' : 'July 14, 2026';
String get kilograms => 'kg';
String get centimeters => 'cm';
}
File diff suppressed because it is too large Load Diff
+218
View File
@@ -0,0 +1,218 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
async:
dependency: transitive
description:
name: async
sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37
url: "https://pub.dev"
source: hosted
version: "2.13.1"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
characters:
dependency: transitive
description:
name: characters
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
url: "https://pub.dev"
source: hosted
version: "1.4.1"
clock:
dependency: transitive
description:
name: clock
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
url: "https://pub.dev"
source: hosted
version: "1.1.2"
collection:
dependency: transitive
description:
name: collection
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
url: "https://pub.dev"
source: hosted
version: "1.19.1"
fake_async:
dependency: transitive
description:
name: fake_async
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.dev"
source: hosted
version: "1.3.3"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1"
url: "https://pub.dev"
source: hosted
version: "6.0.0"
flutter_localizations:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
intl:
dependency: transitive
description:
name: intl
sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
url: "https://pub.dev"
source: hosted
version: "0.20.2"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
url: "https://pub.dev"
source: hosted
version: "11.0.2"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
url: "https://pub.dev"
source: hosted
version: "3.0.10"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
lints:
dependency: transitive
description:
name: lints
sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df"
url: "https://pub.dev"
source: hosted
version: "6.1.0"
matcher:
dependency: transitive
description:
name: matcher
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
url: "https://pub.dev"
source: hosted
version: "0.12.19"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
url: "https://pub.dev"
source: hosted
version: "0.13.0"
meta:
dependency: transitive
description:
name: meta
sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
url: "https://pub.dev"
source: hosted
version: "1.18.0"
path:
dependency: transitive
description:
name: path
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
url: "https://pub.dev"
source: hosted
version: "1.9.1"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
source_span:
dependency: transitive
description:
name: source_span
sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab"
url: "https://pub.dev"
source: hosted
version: "1.10.2"
stack_trace:
dependency: transitive
description:
name: stack_trace
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
url: "https://pub.dev"
source: hosted
version: "1.12.1"
stream_channel:
dependency: transitive
description:
name: stream_channel
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
string_scanner:
dependency: transitive
description:
name: string_scanner
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
url: "https://pub.dev"
source: hosted
version: "1.4.1"
term_glyph:
dependency: transitive
description:
name: term_glyph
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
url: "https://pub.dev"
source: hosted
version: "1.2.2"
test_api:
dependency: transitive
description:
name: test_api
sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
url: "https://pub.dev"
source: hosted
version: "0.7.11"
vector_math:
dependency: transitive
description:
name: vector_math
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
url: "https://pub.dev"
source: hosted
version: "2.2.0"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360"
url: "https://pub.dev"
source: hosted
version: "15.2.0"
sdks:
dart: ">=3.12.0 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54"
Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 KiB