summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorMica White <botahamec@outlook.com>2025-12-28 18:50:31 -0500
committerMica White <botahamec@outlook.com>2025-12-28 18:50:31 -0500
commitde2ed3cdc1a47c783c0afb378c058c16c6ed8ac8 (patch)
treed4a2498f5439ec3b6ae3e55689daca911b77a38e /lib
parent40794d320d287c13e011078d09b3669af77e120f (diff)
Remove title from home page
Diffstat (limited to 'lib')
-rw-r--r--lib/about.dart74
-rw-r--r--lib/home.dart119
2 files changed, 193 insertions, 0 deletions
diff --git a/lib/about.dart b/lib/about.dart
new file mode 100644
index 0000000..6e829dc
--- /dev/null
+++ b/lib/about.dart
@@ -0,0 +1,74 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:package_info_plus/package_info_plus.dart';
+import 'package:url_launcher/url_launcher_string.dart';
+
+class AboutPage extends StatefulWidget {
+ const AboutPage({super.key});
+
+ @override
+ State<AboutPage> createState() => _AboutPageState();
+}
+
+class _AboutPageState extends State<AboutPage> {
+ Future<(PackageInfo, String)>? _loadedData;
+
+ @override
+ void initState() {
+ super.initState();
+ _loadedData = Future(() async {
+ var packageInfo = await PackageInfo.fromPlatform();
+ var license = await rootBundle.loadString('LICENSE');
+ return (packageInfo, license);
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(title: Text('About')),
+ body: Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Icon(
+ Icons.speed,
+ size: Theme.of(context).textTheme.displayLarge?.fontSize ?? 72,
+ ),
+ Text(
+ 'Simple Speedometer',
+ style: Theme.of(context).textTheme.titleLarge,
+ ),
+ Text('© 2025 Mica White'),
+ Text(''),
+ TextButton.icon(
+ icon: Icon(Icons.code),
+ label: Text('View source code'),
+ onPressed: () =>
+ launchUrlString("https://www.botahamec.dev/cgit/speedometer"),
+ ),
+ FutureBuilder(
+ future: _loadedData,
+ builder: (context, snapshot) => TextButton.icon(
+ icon: Icon(Icons.copyright),
+ label: Text('View licenses'),
+ onPressed: () => Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) => LicensePage(
+ applicationIcon: Icon(Icons.speed),
+ applicationName: snapshot.data?.$1.appName,
+ applicationVersion: snapshot.data?.$1.version,
+ applicationLegalese: snapshot.data?.$2,
+ ),
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/home.dart b/lib/home.dart
new file mode 100644
index 0000000..0235016
--- /dev/null
+++ b/lib/home.dart
@@ -0,0 +1,119 @@
+import 'dart:async';
+
+import 'package:flutter/material.dart';
+import 'package:geolocator/geolocator.dart';
+
+import 'main.dart';
+import 'about.dart';
+
+extension on SpeedUnit {
+ double fromMetersPerSecond(double metersPerSecond) => switch (this) {
+ SpeedUnit.kilometersPerHour => metersPerSecond * 3.6,
+ SpeedUnit.milesPerHour => metersPerSecond * 2.236936,
+ };
+
+ String get acronym => switch (this) {
+ SpeedUnit.kilometersPerHour => 'kmph',
+ SpeedUnit.milesPerHour => 'mph',
+ };
+}
+
+class HomePage extends StatefulWidget {
+ const HomePage({super.key});
+
+ @override
+ State<HomePage> createState() => _HomePageState();
+}
+
+class _HomePageState extends State<HomePage> {
+ StreamSubscription<Position>? _positionStream;
+ double _speed = 0.0;
+ double _speedAccuracy = 0.0;
+ SpeedUnit _speedUnit = SpeedUnit.milesPerHour;
+ LocationSettings _locationSettings = LocationSettings();
+
+ void _initPositionStream() {
+ _positionStream =
+ Geolocator.getPositionStream(
+ locationSettings: _locationSettings,
+ ).listen((Position? position) {
+ if (position != null) {
+ setState(() {
+ _speed = position.speed;
+ _speedAccuracy = position.speedAccuracy;
+ });
+ }
+ });
+ }
+
+ @override
+ void initState() {
+ super.initState();
+
+ Geolocator.checkPermission()
+ .then(
+ (permission) => permission == LocationPermission.denied
+ ? Geolocator.requestPermission()
+ : permission,
+ )
+ .then((permission) {
+ if (![
+ LocationPermission.deniedForever,
+ LocationPermission.denied,
+ ].contains(permission)) {
+ _initPositionStream();
+ }
+ });
+ }
+
+ @override
+ void dispose() {
+ super.dispose();
+ _positionStream?.cancel();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ actions: [
+ MenuAnchor(
+ builder: (context, controller, child) => IconButton(
+ icon: Icon(Icons.more_vert),
+ onPressed: () =>
+ controller.isOpen ? controller.close() : controller.open(),
+ tooltip: "Navigation menu",
+ ),
+ menuChildren: [
+ MenuItemButton(child: Text('Settings')),
+ MenuItemButton(
+ child: Text('About'),
+ onPressed: () => Navigator.push(
+ context,
+ MaterialPageRoute(builder: (context) => AboutPage()),
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
+ body: Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Flex(direction: Axis.horizontal),
+ Text(
+ '${_speedUnit.fromMetersPerSecond(_speed).round()} ${_speedUnit.acronym}',
+ style: Theme.of(context).textTheme.displayLarge,
+ ),
+ Text(
+ '± ${_speedUnit.fromMetersPerSecond(_speedAccuracy).round()} ${_speedUnit.acronym}',
+ style: Theme.of(context).textTheme.displaySmall,
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}