refactor(data): create layered architecture with core/data/ui packages
- Move constants from lib/config/ to lib/core/constants/ - Move color themes to lib/core/theme/ - Create LocationData domain model in data/models/ - Create lightweight GpsService in data/services/ - Create LocationRepository in data/repositories/ consolidating GpsDataManager business logic (GPS stream, staleness, precision) - Create shared SpeedDisplay widget in ui/shared/ - Defer overlay feature — stubs reserved in ui/features/overlay/
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
// GPS behavioral constants — SCREAMING_SNAKE per project convention
|
||||
|
||||
/// GPS configuration constants for data validation and timeouts
|
||||
abstract class GpsConfig {
|
||||
/// Time threshold after which GPS data is considered stale
|
||||
static const Duration STALE_DATA_THRESHOLD = Duration(seconds: 4);
|
||||
|
||||
/// Timeout for initial GPS fix acquisition (outdoor/cold start)
|
||||
static const Duration INITIAL_FIX_TIMEOUT = Duration(seconds: 20);
|
||||
|
||||
/// Timeout for ongoing GPS updates (indoor/signal loss detection)
|
||||
static const Duration UPDATE_TIMEOUT = Duration(seconds: 2);
|
||||
|
||||
/// Minimum valid heading value in degrees
|
||||
static const double VALID_HEADING_MIN = 0.0;
|
||||
|
||||
/// Maximum valid heading value in degrees
|
||||
static const double VALID_HEADING_MAX = 360.0;
|
||||
|
||||
/// Speed threshold below which heading is considered unreliable (m/s)
|
||||
static const double LOW_SPEED_THRESHOLD = 1.0;
|
||||
|
||||
/// Speed threshold for switching to HIGH precision (m/s) - 12 km/h
|
||||
static const double PRECISION_UP_THRESHOLD_MPS = 3.33;
|
||||
|
||||
/// Speed threshold for switching to BALANCED precision (m/s) - 8 km/h
|
||||
static const double PRECISION_DOWN_THRESHOLD_MPS = 2.22;
|
||||
|
||||
/// Minimum time after first fix before precision switching starts
|
||||
static const Duration PRECISION_SWITCH_FIRST_FIX_DELAY = Duration(seconds: 5);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// Overlay sizing and display constants — SCREAMING_SNAKE per project convention
|
||||
|
||||
/// Overlay window configuration for proportional sizing and appearance
|
||||
abstract class OverlayConfig {
|
||||
/// Overlay width as percentage of screen width
|
||||
static const double WIDTH_PERCENTAGE = 0.45;
|
||||
|
||||
/// Overlay height aspect ratio relative to width
|
||||
static const double ASPECT_RATIO = 0.6;
|
||||
|
||||
/// Background color opacity
|
||||
static const double BACKGROUND_OPACITY = 0.85;
|
||||
|
||||
/// Border color opacity
|
||||
static const double BORDER_OPACITY = 0.3;
|
||||
|
||||
/// Speed text font size as ratio of overlay width
|
||||
static const double FONT_SIZE_RATIO = 0.2;
|
||||
|
||||
/// Unit text font size as ratio of speed font size
|
||||
static const double UNIT_FONT_RATIO = 0.6;
|
||||
|
||||
/// Navigation icon size as ratio of overlay width
|
||||
static const double ICON_SIZE_RATIO = 0.72;
|
||||
|
||||
/// Heading text font size as ratio of speed font size
|
||||
static const double HEADING_FONT_RATIO = 0.4;
|
||||
|
||||
/// Default screen width for calculations when actual width unavailable
|
||||
static const double DEFAULT_SCREEN_WIDTH = 400.0;
|
||||
|
||||
/// Default overlay width for calculations
|
||||
static const double DEFAULT_OVERLAY_WIDTH = 280.0;
|
||||
|
||||
/// Default overlay height for calculations
|
||||
static const double DEFAULT_OVERLAY_HEIGHT = 140.0;
|
||||
|
||||
/// Time after which overlay dims speed display due to stale data
|
||||
static const Duration STALENESS_DIM_THRESHOLD = Duration(seconds: 3);
|
||||
|
||||
/// Time after which overlay shows dashes instead of speed
|
||||
static const Duration STALENESS_DASH_THRESHOLD = Duration(seconds: 10);
|
||||
|
||||
/// Opacity value for dimmed stale state
|
||||
static const double STALENESS_DIM_OPACITY = 0.5;
|
||||
|
||||
/// Interval for checking data staleness
|
||||
static const Duration STALENESS_CHECK_INTERVAL = Duration(seconds: 1);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
enum SpeedUnit {
|
||||
kmh('km/h', 1.0),
|
||||
mph('mph', 0.621371),
|
||||
knots('kts', 0.539957);
|
||||
|
||||
const SpeedUnit(this.label, this.multiplier);
|
||||
|
||||
final String label;
|
||||
final double multiplier;
|
||||
|
||||
double convert(double speedMps) {
|
||||
return speedMps * 3.6 * multiplier;
|
||||
}
|
||||
|
||||
SpeedUnit get next {
|
||||
final values = SpeedUnit.values;
|
||||
final currentIndex = values.indexOf(this);
|
||||
return values[(currentIndex + 1) % values.length];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// Timer and interval constants — SCREAMING_SNAKE per project convention
|
||||
|
||||
/// Timing configuration for background tasks and UI interactions
|
||||
abstract class TimingConfig {
|
||||
/// Interval for background heartbeat to keep GPS alive
|
||||
static const Duration HEARTBEAT_INTERVAL = Duration(seconds: 5);
|
||||
|
||||
/// Interval for checking overlay window status
|
||||
static const Duration OVERLAY_STATUS_CHECK_INTERVAL = Duration(milliseconds: 1000);
|
||||
|
||||
/// Delay before closing overlay after tap
|
||||
static const Duration TAP_CLOSE_DELAY = Duration(milliseconds: 500);
|
||||
|
||||
/// Delay for long press gesture signal
|
||||
static const Duration LONG_PRESS_SIGNAL_DELAY = Duration(milliseconds: 50);
|
||||
|
||||
/// GPS grace period - continues for 30s after overlay close when backgrounded
|
||||
/// DEPRECATED: Replaced by BACKGROUND_GRACE_PERIOD (7s) for lifecycle management
|
||||
static const Duration GPS_GRACE_PERIOD = Duration(seconds: 30);
|
||||
|
||||
/// Background grace period - time before GPS stops when app backgrounded without overlay
|
||||
static const Duration BACKGROUND_GRACE_PERIOD = Duration(seconds: 7);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ColorTheme {
|
||||
final Color background;
|
||||
final Color speedText;
|
||||
final Color speedTextSub;
|
||||
final Color headingText;
|
||||
final Color unitText;
|
||||
final String name;
|
||||
|
||||
const ColorTheme({
|
||||
required this.background,
|
||||
required this.speedText,
|
||||
required this.speedTextSub,
|
||||
required this.headingText,
|
||||
required this.unitText,
|
||||
required this.name,
|
||||
});
|
||||
}
|
||||
|
||||
class ColorThemes {
|
||||
static const List<ColorTheme> themes = [
|
||||
ColorTheme(
|
||||
name: 'Yuzuki',
|
||||
background: Color.fromARGB(255, 114, 85, 109),
|
||||
speedText: Color.fromARGB(255, 246, 224, 255),
|
||||
speedTextSub: Color.fromARGB(255, 245, 217, 245),
|
||||
headingText: Color.fromARGB(255, 244, 201, 255),
|
||||
unitText: Color.fromARGB(255, 244, 201, 255),
|
||||
),
|
||||
ColorTheme(
|
||||
name: 'Yuuparo',
|
||||
background: Color.fromARGB(255, 85, 99, 112),
|
||||
speedText: Color.fromARGB(255, 255, 171, 16),
|
||||
speedTextSub: Color.fromARGB(255, 245, 204, 116),
|
||||
headingText: Color.fromARGB(255, 228, 235, 238),
|
||||
unitText: Color.fromARGB(255, 141, 196, 184),
|
||||
),
|
||||
ColorTheme(
|
||||
name: 'Yuzuki Light',
|
||||
background: Color.fromARGB(255, 246, 224, 255),
|
||||
speedText: Color.fromARGB(255, 114, 85, 109),
|
||||
speedTextSub: Color.fromARGB(255, 161, 138, 170),
|
||||
headingText: Color.fromARGB(255, 140, 94, 151),
|
||||
unitText: Color.fromARGB(255, 114, 85, 109),
|
||||
),
|
||||
ColorTheme(
|
||||
name: 'Yuuparo Light',
|
||||
background: Color.fromARGB(255, 228, 235, 238),
|
||||
speedText: Color.fromARGB(255, 85, 99, 112),
|
||||
speedTextSub: Color.fromARGB(255, 100, 100, 131),
|
||||
headingText: Color.fromARGB(255, 221, 118, 0),
|
||||
unitText: Color.fromARGB(255, 98, 124, 118),
|
||||
),
|
||||
];
|
||||
|
||||
static ColorTheme getTheme(int index) {
|
||||
return themes[index % themes.length];
|
||||
}
|
||||
|
||||
static int getNextThemeIndex(int currentIndex) {
|
||||
return (currentIndex + 1) % themes.length;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user