Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 411166ef5d | |||
| b2f0015556 | |||
| f9a33809a2 | |||
| abd6d779ea | |||
| 92815c4682 | |||
| 8a3487f65e | |||
| 64b80eecc9 | |||
| 43f1e4ecb2 | |||
| 449e345e3e | |||
| c2bcd685cf | |||
| 54cec166b4 | |||
| fff81719b1 |
@@ -0,0 +1,41 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
workflow_dispatch:
|
||||
|
||||
# Target the self-hosted Flutter/Android runner at the android-stuff org.
|
||||
# See tool/setup_ci_runner.sh for how that runner is provisioned.
|
||||
#
|
||||
# The runner is host-mode so the same JDK 17 + Android SDK + Flutter
|
||||
# toolchain is used as the verified release — nothing installs per-run.
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze & Build
|
||||
runs-on: flutter-android
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Toolchain
|
||||
run: |
|
||||
flutter --version
|
||||
echo "ANDROID_HOME=${ANDROID_HOME:?not set on this runner}"
|
||||
|
||||
- name: Get dependencies
|
||||
run: flutter pub get
|
||||
|
||||
- name: Analyze
|
||||
run: flutter analyze
|
||||
|
||||
- name: Build release APK
|
||||
run: flutter build apk --release
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: app-release-apk
|
||||
path: build/app/outputs/flutter-apk/app-release.apk
|
||||
if-no-files-found: error
|
||||
@@ -10,3 +10,11 @@ analyzer:
|
||||
- windows/**
|
||||
- macos/**
|
||||
- linux/**
|
||||
|
||||
linter:
|
||||
rules:
|
||||
prefer_single_quotes: true
|
||||
# SCREAMING_SNAKE_CASE for constants is project convention
|
||||
constant_identifier_names: false
|
||||
# sort_pub_dependencies doesn't play well with flutter SDK dep first
|
||||
sort_pub_dependencies: false
|
||||
@@ -15,9 +15,10 @@ class GpsService {
|
||||
LocationAccuracy accuracy = LocationAccuracy.high,
|
||||
}) {
|
||||
return Geolocator.getPositionStream(
|
||||
locationSettings: LocationSettings(
|
||||
locationSettings: AndroidSettings(
|
||||
accuracy: accuracy,
|
||||
distanceFilter: 0,
|
||||
intervalDuration: Duration(milliseconds: 250),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class OverlayService {
|
||||
() async {
|
||||
await FlutterOverlayWindow.showOverlay(
|
||||
enableDrag: true,
|
||||
overlayTitle: "Speedometer",
|
||||
overlayTitle: 'Speedometer',
|
||||
overlayContent: 'Speedo overlay active',
|
||||
flag: OverlayFlag.defaultFlag,
|
||||
visibility: NotificationVisibility.visibilityPublic,
|
||||
|
||||
+9
-8
@@ -33,19 +33,20 @@ void main() async {
|
||||
},
|
||||
),
|
||||
// UI layer — ViewModels subscribe to repositories
|
||||
ChangeNotifierProvider<SpeedometerViewModel>(
|
||||
create: (context) => SpeedometerViewModel(
|
||||
context.read<LocationRepository>(),
|
||||
),
|
||||
),
|
||||
ChangeNotifierProvider<OverlayViewModel>(
|
||||
create: (_) => OverlayViewModel(),
|
||||
),
|
||||
ChangeNotifierProvider<SettingsProvider>(
|
||||
create: (context) => SettingsProvider(
|
||||
context.read<SharedPreferences>(),
|
||||
),
|
||||
),
|
||||
ChangeNotifierProvider<SpeedometerViewModel>(
|
||||
create: (context) => SpeedometerViewModel(
|
||||
context.read<LocationRepository>(),
|
||||
context.read<SettingsProvider>(),
|
||||
),
|
||||
),
|
||||
ChangeNotifierProvider<OverlayViewModel>(
|
||||
create: (_) => OverlayViewModel(),
|
||||
),
|
||||
],
|
||||
child: const SpeedoApp(),
|
||||
),
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:provider/provider.dart';
|
||||
import '../../../core/constants/overlay_constants.dart';
|
||||
import '../../../core/theme/color_themes.dart';
|
||||
import '../../shared/speed_display.dart';
|
||||
import '../../../providers/settings_provider.dart';
|
||||
import 'speedometer_view_model.dart';
|
||||
|
||||
/// Thin UI view for the speedometer — renders speed and compass.
|
||||
@@ -19,9 +20,8 @@ class SpeedometerView extends StatelessWidget {
|
||||
selector: (_, vm) => vm.displaySpeed,
|
||||
builder: (context, displaySpeed, _) {
|
||||
final vm = context.watch<SpeedometerViewModel>();
|
||||
final theme = context.select<Theme, ColorTheme>(
|
||||
(theme) => ColorThemes.themes[0],
|
||||
);
|
||||
final settings = context.read<SettingsProvider>();
|
||||
final theme = settings.currentTheme;
|
||||
|
||||
// Staleness — UI-layer concern
|
||||
final age = DateTime.now().difference(vm.displayData.timestamp);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../../../core/constants/speed_unit.dart';
|
||||
import '../../../data/models/location_data.dart';
|
||||
import '../../../data/repositories/location_repository.dart';
|
||||
@@ -11,22 +10,16 @@ import '../../../providers/settings_provider.dart';
|
||||
/// the current display state to the view. No data-layer logic here.
|
||||
class SpeedometerViewModel extends ChangeNotifier {
|
||||
final LocationRepository _repository;
|
||||
SettingsProvider? _settings;
|
||||
final SettingsProvider _settings;
|
||||
|
||||
SpeedUnit get displayUnit => _settings?.currentUnit ?? SpeedUnit.kmh;
|
||||
SpeedUnit get displayUnit => _settings.currentUnit;
|
||||
LocationData get displayData => _repository.currentData;
|
||||
String get displaySpeedText => _repository.currentData.displaySpeed;
|
||||
String get displayHeading => _repository.currentData.displayHeading;
|
||||
bool get isSpeedValid => _repository.currentData.isSpeedValid;
|
||||
bool get isHeadingValid => _repository.currentData.isHeadingValid;
|
||||
|
||||
SpeedometerViewModel(this._repository);
|
||||
|
||||
/// Call when the ViewModel is inserted into the provider tree so it
|
||||
/// can listen to SettingsProvider and repository state.
|
||||
void attach(BuildContext context) {
|
||||
_settings = context.read<SettingsProvider>();
|
||||
}
|
||||
SpeedometerViewModel(this._repository, this._settings);
|
||||
|
||||
double get displaySpeed {
|
||||
return displayUnit.convert(_repository.currentData.speed);
|
||||
|
||||
+7
-22
@@ -2,7 +2,7 @@ name: gps_speedo
|
||||
description: "Speedo - A Flutter application."
|
||||
publish_to: 'none'
|
||||
|
||||
version: 2.2.2+1
|
||||
version: 2.3.0+2
|
||||
|
||||
environment:
|
||||
sdk: ^3.9.0
|
||||
@@ -11,37 +11,22 @@ dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
# cupertino_icons: ^1.0.8
|
||||
# Probably no icons needed for now
|
||||
|
||||
# GPS and Location Services - Essential only
|
||||
bg_launcher: ^0.1.0
|
||||
flutter_overlay_window: ^0.5.0
|
||||
geolocator: ^14.0.2
|
||||
permission_handler: ^12.0.1
|
||||
|
||||
# Keep screen awake during navigation
|
||||
wakelock_plus: ^1.2.8
|
||||
|
||||
# Floating window overlay capability
|
||||
flutter_overlay_window: ^0.5.0
|
||||
|
||||
# Bring app to foreground from background/overlay
|
||||
bg_launcher: ^0.1.0
|
||||
|
||||
# State management
|
||||
provider: ^6.1.2
|
||||
|
||||
# Persistent settings storage
|
||||
shared_preferences: ^2.3.4
|
||||
|
||||
# Retry logic for platform operations
|
||||
retry: ^3.1.2
|
||||
shared_preferences: ^2.3.4
|
||||
wakelock_plus: ^1.2.8
|
||||
|
||||
|
||||
dev_dependencies:
|
||||
flutter_launcher_icons: "^0.14.4"
|
||||
flutter_lints: ^6.0.0
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^6.0.0
|
||||
flutter_launcher_icons: "^0.14.4"
|
||||
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
||||
Reference in New Issue
Block a user