Colour patterns and speed unit selection working
This commit is contained in:
+15
-15
@@ -19,25 +19,25 @@ class ColorTheme {
|
||||
class ColorThemes {
|
||||
static const List<ColorTheme> themes = [
|
||||
ColorTheme(
|
||||
name: 'Classic Dark',
|
||||
background: Color(0xFF121212),
|
||||
speedText: Color(0xFF4CAF50),
|
||||
headingText: Color(0xFFFF9800),
|
||||
unitText: Color(0xFF9E9E9E),
|
||||
name: 'Yuzuki',
|
||||
background: Color.fromARGB(255, 114, 85, 109),
|
||||
speedText: Color.fromARGB(255, 246, 224, 255),
|
||||
headingText: Color.fromARGB(255, 244, 201, 255),
|
||||
unitText: Color.fromARGB(255, 244, 201, 255),
|
||||
),
|
||||
ColorTheme(
|
||||
name: 'Electric Blue',
|
||||
background: Color(0xFF0D1421),
|
||||
speedText: Color(0xFF00E5FF),
|
||||
headingText: Color(0xFFFFEB3B),
|
||||
unitText: Color(0xFF78909C),
|
||||
name: 'Yuubari',
|
||||
background: Color.fromARGB(255, 85, 99, 112),
|
||||
speedText: Color.fromARGB(255, 255, 166, 0),
|
||||
headingText: Color.fromARGB(255, 228, 235, 238),
|
||||
unitText: Color.fromARGB(255, 141, 196, 184),
|
||||
),
|
||||
ColorTheme(
|
||||
name: 'Racing Red',
|
||||
background: Color(0xFF1A0A0A),
|
||||
speedText: Color(0xFFFF1744),
|
||||
headingText: Color(0xFFFFFFFF),
|
||||
unitText: Color(0xFFB0BEC5),
|
||||
name: 'Yuzuki Light',
|
||||
background: Color.fromARGB(255, 246, 224, 255),
|
||||
speedText: Color.fromARGB(255, 114, 85, 109),
|
||||
headingText: Color.fromARGB(255, 140, 94, 151),
|
||||
unitText: Color.fromARGB(255, 114, 85, 109),
|
||||
),
|
||||
];
|
||||
|
||||
|
||||
@@ -31,6 +31,6 @@ class GpsService {
|
||||
|
||||
static String formatHeading(double heading) {
|
||||
if (heading < 0) return 'N/A';
|
||||
return '${getCompassDirection(heading)} ${heading.round()}°';
|
||||
return '${heading.round()} ${getCompassDirection(heading)}';
|
||||
}
|
||||
}
|
||||
+239
-84
@@ -1,29 +1,30 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:async';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import 'package:wakelock_plus/wakelock_plus.dart';
|
||||
import 'speed_units.dart';
|
||||
import 'gps_service.dart';
|
||||
import 'color_themes.dart';
|
||||
|
||||
void main() {
|
||||
runApp(const GpsSpeedoApp());
|
||||
runApp(const SpeedoApp());
|
||||
}
|
||||
|
||||
// Entry point for future overlay mode
|
||||
void mainOverlay() {
|
||||
// TODO: Initialize overlay-specific setup
|
||||
runApp(const GpsSpeedoApp(isOverlayMode: true));
|
||||
runApp(const SpeedoApp(isOverlayMode: true));
|
||||
}
|
||||
|
||||
class GpsSpeedoApp extends StatelessWidget {
|
||||
class SpeedoApp extends StatelessWidget {
|
||||
final bool isOverlayMode;
|
||||
|
||||
const GpsSpeedoApp({super.key, this.isOverlayMode = false});
|
||||
const SpeedoApp({super.key, this.isOverlayMode = false});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'GPS Speedo',
|
||||
title: 'Speedo',
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: ThemeData(
|
||||
useMaterial3: true,
|
||||
@@ -56,11 +57,21 @@ class _SpeedometerScreenState extends State<SpeedometerScreen> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initializeGps();
|
||||
_enableWakelock();
|
||||
}
|
||||
|
||||
Future<void> _enableWakelock() async {
|
||||
try {
|
||||
await WakelockPlus.enable();
|
||||
} catch (e) {
|
||||
// Wake lock not supported on this platform, continue normally
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_positionSubscription?.cancel();
|
||||
WakelockPlus.disable();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -116,87 +127,231 @@ class _SpeedometerScreenState extends State<SpeedometerScreen> {
|
||||
return Scaffold(
|
||||
backgroundColor: currentTheme.background,
|
||||
body: SafeArea(
|
||||
child: GestureDetector(
|
||||
onTap: _cycleTheme,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
color: Colors.transparent,
|
||||
child: Column(
|
||||
children: [
|
||||
// Speed display - takes most space, tappable
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: GestureDetector(
|
||||
onTap: _cycleUnit,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
color: Colors.transparent,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (_errorMessage.isNotEmpty)
|
||||
Text(
|
||||
_errorMessage,
|
||||
style: TextStyle(
|
||||
color: currentTheme.speedText,
|
||||
fontSize: 16,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
)
|
||||
else ...[
|
||||
Text(
|
||||
speedText,
|
||||
style: TextStyle(
|
||||
fontSize: 120,
|
||||
fontWeight: FontWeight.w300,
|
||||
color: currentTheme.speedText,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
_currentUnit.label,
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
color: currentTheme.unitText,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// Compass display - compact
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.navigation,
|
||||
size: 32,
|
||||
color: currentTheme.headingText,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
GpsService.formatHeading(_heading),
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
color: currentTheme.headingText,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final isLandscape = constraints.maxWidth > constraints.maxHeight;
|
||||
|
||||
if (isLandscape) {
|
||||
return _buildLandscapeLayout(speedText, currentTheme);
|
||||
} else {
|
||||
return _buildPortraitLayout(speedText, currentTheme);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPortraitLayout(String speedText, ColorTheme currentTheme) {
|
||||
return Column(
|
||||
children: [
|
||||
// Speed area - takes most space
|
||||
Expanded(
|
||||
flex: 7,
|
||||
child: GestureDetector(
|
||||
onTap: _cycleUnit,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
color: Colors.transparent,
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (_errorMessage.isNotEmpty)
|
||||
Flexible(
|
||||
child: Text(
|
||||
_errorMessage,
|
||||
style: TextStyle(
|
||||
color: currentTheme.speedText,
|
||||
fontSize: 18,
|
||||
fontFamily: 'DIN1451Alt',
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
)
|
||||
else ...[
|
||||
Flexible(
|
||||
flex: 3,
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: Text(
|
||||
speedText,
|
||||
style: TextStyle(
|
||||
fontSize: 200,
|
||||
fontWeight: FontWeight.w300,
|
||||
color: currentTheme.speedText,
|
||||
fontFamily: 'DIN1451Alt',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
flex: 1,
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: Text(
|
||||
_currentUnit.label,
|
||||
style: TextStyle(
|
||||
fontSize: 64,
|
||||
color: currentTheme.unitText,
|
||||
fontFamily: 'DIN1451Alt',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// Compass area - compact but tappable
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: GestureDetector(
|
||||
onTap: _cycleTheme,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
color: Colors.transparent,
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Flexible(
|
||||
flex: 2,
|
||||
child: Icon(
|
||||
Icons.navigation,
|
||||
size: 100,
|
||||
color: currentTheme.headingText,
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
flex: 1,
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: Text(
|
||||
GpsService.formatHeading(_heading),
|
||||
style: TextStyle(
|
||||
fontSize: 96,
|
||||
color: currentTheme.headingText,
|
||||
fontFamily: 'DIN1451Alt',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLandscapeLayout(String speedText, ColorTheme currentTheme) {
|
||||
return Row(
|
||||
children: [
|
||||
// Speed area - takes majority of space
|
||||
Expanded(
|
||||
flex: 65,
|
||||
child: GestureDetector(
|
||||
onTap: _cycleUnit,
|
||||
child: Container(
|
||||
height: double.infinity,
|
||||
color: Colors.transparent,
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (_errorMessage.isNotEmpty)
|
||||
Flexible(
|
||||
child: Text(
|
||||
_errorMessage,
|
||||
style: TextStyle(
|
||||
color: currentTheme.speedText,
|
||||
fontSize: 16,
|
||||
fontFamily: 'DIN1451Alt',
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
)
|
||||
else ...[
|
||||
Flexible(
|
||||
flex: 3,
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: Text(
|
||||
speedText,
|
||||
style: TextStyle(
|
||||
fontSize: 120,
|
||||
fontWeight: FontWeight.w300,
|
||||
color: currentTheme.speedText,
|
||||
fontFamily: 'DIN1451Alt',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
flex: 1,
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: Text(
|
||||
_currentUnit.label,
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
color: currentTheme.unitText,
|
||||
fontFamily: 'DIN1451Alt',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// Compass area - smaller but prominent
|
||||
Expanded(
|
||||
flex: 35,
|
||||
child: GestureDetector(
|
||||
onTap: _cycleTheme,
|
||||
child: Container(
|
||||
height: double.infinity,
|
||||
color: Colors.transparent,
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Flexible(
|
||||
flex: 2,
|
||||
child: Icon(
|
||||
Icons.navigation,
|
||||
size: 32,
|
||||
color: currentTheme.headingText,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Flexible(
|
||||
flex: 1,
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: Text(
|
||||
GpsService.formatHeading(_heading),
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
color: currentTheme.headingText,
|
||||
fontFamily: 'DIN1451Alt',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user