Compare commits

..

25 Commits

Author SHA1 Message Date
d428be6504 bumped app version 2024-04-03 18:00:00 +02:00
608ef75f9f added lets encrypt cert 2024-04-03 17:55:24 +02:00
4474eb0aa9 bumped app version 2024-04-03 17:34:49 +02:00
8be01e92a8 added internet permissions 2024-04-03 13:15:12 +02:00
b64e1525ae bumped app version 2024-04-01 15:04:12 +02:00
27ffb07256 added dialog when location is disabled 2024-04-01 13:57:12 +02:00
9fbc19ff7e bumped build number 2024-03-31 18:17:42 +02:00
a2326118d2 bumped build number 2024-03-27 22:43:45 +01:00
169ebeb30a adjust time warning message 2024-03-27 22:39:04 +01:00
7eb09390ad updated launcher icons 2024-03-27 22:35:19 +01:00
84c838b15e Merge remote-tracking branch 'origin/master'
# Conflicts:
#	lib/view/home.dart
2024-03-27 22:28:42 +01:00
32c88fa857 mimic google behavior with location icon button 2024-03-27 22:11:04 +01:00
c00d2fac05 mimic google behavior with location icon button 2024-03-27 22:08:24 +01:00
507b321311 updated launcher icons 2024-03-27 21:39:22 +01:00
2a38b8ae2b don't show time warning when it's between 8PM and 7AM 2024-03-27 20:07:17 +01:00
c8278656cf added current location marker 2024-03-27 19:35:19 +01:00
e748df092c updated info text and time warning 2024-03-27 19:30:54 +01:00
4ecf44bafa Merge remote-tracking branch 'origin/master' 2024-03-27 18:28:01 +01:00
bffc603918 moved loading indicator to gps button 2024-03-27 18:27:54 +01:00
628d52f01f added text if no search results were found 2024-03-27 18:27:13 +01:00
ef8c5e9039 check if location is activated on startup 2024-03-27 02:49:11 +01:00
ec8d3d466b set location enabled in function to get live location 2024-03-27 02:41:04 +01:00
4b384b378a retry background tiles if status is >= 400 and not 404 2024-03-27 02:34:18 +01:00
799d1cb335 added search functionality 2024-03-27 02:32:05 +01:00
911c2738aa added map live locating 2024-03-27 01:03:01 +01:00
24 changed files with 582 additions and 147 deletions

View File

@ -2,7 +2,7 @@
<application
android:label="Bubatzkarte"
android:name="${applicationName}"
android:icon="@mipmap/launcher_icon">
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
@ -41,4 +41,8 @@
<data android:mimeType="text/plain"/>
</intent>
</queries>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
</manifest>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 442 B

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 B

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
assets/background/tile.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw
WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg
RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP
R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx
sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm
NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg
Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG
/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC
AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB
Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA
FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw
AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw
Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB
gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W
PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl
ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz
CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm
lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4
avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2
yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O
yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids
hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+
HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv
MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX
nLRbwHOoq7hHwg==
-----END CERTIFICATE-----

BIN
assets/splash/splash512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -1,5 +1,5 @@
flutter_launcher_icons:
android: "launcher_icon"
android: true
ios: true
image_path: "assets/splash/splash.png"
min_sdk_android: 16

View File

@ -0,0 +1,8 @@
import 'package:geolocator/geolocator.dart';
import 'package:latlong2/latlong.dart';
extension LatLngPositionExtension on Position {
LatLng latlng() {
return LatLng(latitude, longitude);
}
}

View File

@ -1,14 +1,23 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:app/state/timeStatusState.dart';
import 'package:app/state/mapState.dart';
import 'package:app/view/home.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
void main() {
void main() async {
WidgetsFlutterBinding.ensureInitialized();
ByteData data = await PlatformAssetBundle().load('assets/ca/lets-encrypt-r3.pem');
SecurityContext.defaultContext.setTrustedCertificatesBytes(data.buffer.asUint8List());
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider<TimeStatusState>(create: (context) => TimeStatusState()),
ChangeNotifierProvider<TimeWarningState>(create: (context) => TimeWarningState()),
ChangeNotifierProvider<MapState>(create: (context) => MapState()),
],
builder: (context, child) => const MyApp(),

View File

@ -1,22 +1,38 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:geolocator/geolocator.dart';
import 'package:latlong2/latlong.dart';
class MapState extends ChangeNotifier {
bool _isLocationLock = false;
bool _isCurrentlyFetchin = false;
bool _isLocationLive = false;
LatLng? _currentLocation;
LatLng? _activeMarker;
final MapController _mapController = MapController();
final Geolocator _geolocator = Geolocator();
bool get followLocation => _isLocationLock;
bool get isCurrentlyFetching => _isCurrentlyFetchin;
bool get isLocationLive => _isLocationLive;
LatLng? get getCurrentLocation => _currentLocation;
LatLng? get getActiveMarker => _activeMarker;
MapController get getMapController => _mapController;
Geolocator get getGeolocator => _geolocator;
toggleLocationLock() {
_isLocationLock = !_isLocationLock;
void setCurrentLocation(LatLng? currentLocation) {
_currentLocation = currentLocation;
if (currentLocation != null) _mapController.move(currentLocation, 16);
notifyListeners();
}
set setNetworkActivity(bool active) {
_isCurrentlyFetchin = active;
void setActiveMarker(LatLng? marker) {
_activeMarker = marker;
if (marker != null) {
_isLocationLive = false;
_mapController.move(marker, 16);
}
notifyListeners();
}
void setLocationLive(bool live) {
_isLocationLive = live;
notifyListeners();
}
}

View File

@ -1,46 +1,11 @@
import 'dart:async';
import 'package:flutter/material.dart';
class TimeStatusState extends ChangeNotifier {
final TimeOfDay legalStart = const TimeOfDay(hour: 20, minute: 00);
final TimeOfDay legalEnd = const TimeOfDay(hour: 07, minute: 00);
class TimeWarningState extends ChangeNotifier {
bool _show = true;
late Timer _refreshTimer;
TimeStatusState() {
_refreshTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
notifyListeners();
});
}
bool get isCurrentlyLegal {
final now = TimeOfDay.now();
final currentTime = DateTime.now();
final startDateTime = DateTime(currentTime.year, currentTime.month, currentTime.day, legalStart.hour, legalStart.minute);
final currentDateTime = currentTime.subtract(Duration(hours: now.hour, minutes: now.minute));
return currentDateTime.isBefore(startDateTime);
}
Duration get remainingTimeUntilStart {
final now = DateTime.now();
final today = DateTime(now.year, now.month, now.day);
final targetDateTime = today.add(Duration(hours: legalStart.hour, minutes: legalStart.minute));
if (now.isBefore(targetDateTime)) {
return targetDateTime.difference(now);
} else {
final nextDay = today.add(const Duration(days: 1));
final nextTargetDateTime = nextDay.add(Duration(hours: legalStart.hour, minutes: legalStart.minute));
return nextTargetDateTime.difference(now);
}
}
@override
void dispose() {
_refreshTimer.cancel();
super.dispose();
bool get show => _show;
void hide() {
_show = false;
notifyListeners();
}
}

View File

@ -1,15 +1,42 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
class AppInfoView extends StatelessWidget {
const AppInfoView({super.key});
@override
Widget build(BuildContext context) {
return const AlertDialog(
title: Text("Achtung"),
content: Flexible(
child: Text("bla"),
return AlertDialog(
title: const Text("Information"),
content: const Column(
mainAxisSize: MainAxisSize.min,
children: [
Text("Diese App zeigt die Konsumverbotszonen für Cannabis."),
Text("Keinerlei Gewähr für Vollständigkeit, Richtigkeit und Aktualität!"),
SizedBox(height: 10),
Text("Prüfe selbst die Gesetzlichen Bestimmungen vor dem Besitz oder Konsum von Cannabis!", style: TextStyle(fontWeight: FontWeight.bold)),
SizedBox(height: 10),
Text("Die Daten beruhen auf OpenStreetMap, bearbeitet durch bubatzkarte.de."),
Text("Es besteht keinerlei Kooperation mit OpenStreetMap oder bubatzkarte.de.")
],
),
actions: [
TextButton(
child: const Text("bubatzkarte.de öffnen"),
onPressed: () => launchUrl(Uri.parse("https://bubatzkarte.de/")),
),
TextButton(
child: const Text("openstreetmap.org öffnen"),
onPressed: () => launchUrl(Uri.parse("https://www.openstreetmap.org/")),
),
TextButton(
child: const Text("Schließen"),
onPressed: () => Navigator.of(context).pop(),
),
],
);
}
}

View File

@ -1,14 +1,17 @@
import 'dart:developer';
import 'package:app/extensions/obtainProviderExtension.dart';
import 'package:app/extensions/positionLatLngExtension.dart';
import 'package:app/state/mapState.dart';
import 'package:app/state/timeStatusState.dart';
import 'package:app/util/loadingContainer.dart';
import 'package:app/util/watchState.dart';
import 'package:app/view/appInfo.dart';
import 'package:app/view/status.dart';
import 'package:app/view/locationSearch.dart';
import 'package:app/view/timeWarning.dart';
import 'package:app/view/map.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:geolocator/geolocator.dart';
import 'package:latlong2/latlong.dart';
class HomeView extends StatefulWidget {
const HomeView({super.key});
@ -18,6 +21,101 @@ class HomeView extends StatefulWidget {
}
class _HomePageState extends State<HomeView> {
bool locationEnabled = false;
bool locationLoading = false;
@override
void initState() {
super.initState();
Geolocator.getServiceStatusStream().listen((status) {
setState(() {
locationEnabled = status == ServiceStatus.enabled;
});
});
requestLiveLocation(false, true, true);
}
Future<void> requestLiveLocation(bool ask, bool orLast, bool loadingIndicator) async {
Position? position;
MapState mapState = context.obtainState<MapState>();
LocationPermission permission = await Geolocator.checkPermission();
log(permission.toString());
if(
permission == LocationPermission.denied
|| permission == LocationPermission.deniedForever
|| permission == LocationPermission.unableToDetermine
) {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text("Standort"),
content: const Text("Dein Standort konnte nicht bestimmt werden.\nAktiviere für die Standortbestimmung deinen Standort und gib der App die entsprechenden Berechtigungen."),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text("Okay"),
)
],
);
}
);
}
if (
loadingIndicator
&& permission != LocationPermission.deniedForever
&& permission != LocationPermission.denied
) {
setState(() {
locationLoading = true;
});
}
if (permission == LocationPermission.denied || permission == LocationPermission.deniedForever) {
if (ask && permission != LocationPermission.deniedForever) {
permission = await Geolocator.requestPermission();
}
if (permission == LocationPermission.denied || permission == LocationPermission.deniedForever) {
if (orLast) position = await Geolocator.getLastKnownPosition();
}
}
if (permission == LocationPermission.denied || permission == LocationPermission.deniedForever) {
if (position != null) {
setState(() => locationEnabled = true);
mapState.setCurrentLocation(position.latlng());
}
setState(() => locationLoading = false);
return;
}
try {
position = await Geolocator.getCurrentPosition();
setState(() {
locationEnabled = true;
});
mapState.setCurrentLocation(position.latlng());
mapState.setLocationLive(true);
Geolocator.getServiceStatusStream().first.then((_) => setState(() {
mapState.setLocationLive(false);
}));
} catch (_) {
if (orLast) {
position = await Geolocator.getLastKnownPosition();
if (position != null) {
setState(() => locationEnabled = true);
mapState.setCurrentLocation(position.latlng());
}
}
}
setState(() => locationLoading = false);
}
@override
Widget build(BuildContext context) {
return Scaffold(
@ -27,8 +125,13 @@ class _HomePageState extends State<HomeView> {
actions: [
IconButton(
icon: const Icon(Icons.search),
onPressed: () {
onPressed: () async {
MapState mapState = context.obtainState<MapState>();
LatLng? latLng = await showSearch<LatLng?>(
context: context,
delegate: LocationSearchDelegate(),
);
mapState.setActiveMarker(latLng);
},
),
IconButton(
@ -43,30 +146,31 @@ class _HomePageState extends State<HomeView> {
],
),
floatingActionButton: FloatingActionButton(
child: WatchState<MapState>((context, state) => Icon(state.followLocation ? Icons.my_location : Icons.location_disabled)),
onPressed: () => context.obtainState<MapState>().toggleLocationLock(),
child: WatchState<MapState>((context, state) {
if (locationLoading) {
return const Padding(padding: EdgeInsets.all(15), child: CircularProgressIndicator(strokeWidth: 3));
}
if (locationEnabled) {
return Icon(state.isLocationLive ? Icons.my_location : Icons.location_searching);
}
return const Icon(Icons.location_disabled);
}),
onPressed: () => requestLiveLocation(true, false, true),
),
body: Column(
children: [
WatchState<TimeStatusState>((context, state) {
if(state.isCurrentlyLegal) return const SizedBox.shrink();
WatchState<TimeWarningState>((context, state) {
if(!state.show || DateTime.now().hour >= 20 || DateTime.now().hour < 7) {
return const SizedBox.shrink();
}
return const SizedBox(
height: 100,
child: StatusView(),
child: TimeWarningView(),
);
}),
Expanded(
child: Consumer<MapState>(
builder: (context, state, child) {
return LoadingContainer(
loading: child == null,
fetching: state.isCurrentlyFetching,
child: const MapView(),
);
},
child: const MapView(),
),
const Expanded(
child: MapView(),
)
],
),

View File

@ -0,0 +1,70 @@
import 'package:flutter/material.dart';
import 'package:latlong2/latlong.dart';
import 'package:osm_nominatim/osm_nominatim.dart';
class LocationSearchDelegate extends SearchDelegate<LatLng?> {
@override
List<Widget>? buildActions(BuildContext context) {
return [
IconButton(
icon: const Icon(Icons.clear),
onPressed: () => query = '',
)
];
}
@override
Widget? buildLeading(BuildContext context) {
return IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () => close(context, null),
);
}
@override
Widget buildResults(BuildContext context) {
return FutureBuilder<List<Place>>(
future: query.isEmpty ? Future.value([]) : Nominatim.searchByName(
query: query,
limit: 10,
language: 'de',
// https://gist.github.com/graydon/11198540#file-country-bounding-boxes-py-L45
viewBox: ViewBox(54.983104153, 15.0169958839, 47.3024876979, 5.98865807458)
),
builder: (BuildContext context, AsyncSnapshot<List<Place>> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Center(child: CircularProgressIndicator());
case ConnectionState.active:
case ConnectionState.none:
case ConnectionState.done:
if ((snapshot.data?.length ?? 0) == 0) {
return const Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(height: 50),
Text("Keine Sucherergebnisse gefunden")
],
);
}
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
Place place = snapshot.data![index];
return ListTile(
title: Text(place.displayName),
onTap: () => close(context, LatLng(place.lat, place.lon)),
);
},
);
}
},
);
}
@override
Widget buildSuggestions(BuildContext context) {
return buildResults(context);
}
}

View File

@ -1,5 +1,6 @@
import 'package:app/extensions/obtainProviderExtension.dart';
import 'package:app/state/mapState.dart';
import 'package:app/util/watchState.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:http/http.dart';
@ -18,10 +19,13 @@ class _MapViewState extends State<MapView> {
Widget build(BuildContext context) {
return FlutterMap(
mapController: context.obtainState<MapState>().getMapController,
options: const MapOptions(
initialCenter: LatLng(50.354, 7.5845),
options: MapOptions(
onPositionChanged: (position, hasGesture) => {
if (hasGesture) context.obtainState<MapState>().setLocationLive(false)
},
initialCenter: const LatLng(50.354, 7.5845),
initialZoom: 16,
interactionOptions: InteractionOptions(
interactionOptions: const InteractionOptions(
flags: InteractiveFlag.all & ~InteractiveFlag.rotate
)
),
@ -34,9 +38,51 @@ class _MapViewState extends State<MapView> {
tileProvider: NetworkTileProvider(
httpClient: RetryClient(
Client(),
when: (response) => response.statusCode == 502)
when: (response) => response.statusCode >= 400 && response.statusCode != 404)
),
)
),
WatchState<MapState>((context, state) => state.getActiveMarker != null ? MarkerLayer(
markers: [
Marker(
point: state.getActiveMarker!,
child: const Icon(
Icons.location_on,
color: Colors.red,
size: 48,
)
)
],
) : const SizedBox.shrink()),
WatchState<MapState>((context, state) => MarkerLayer(
markers: [
if (state.getActiveMarker != null) Marker(
point: state.getActiveMarker!,
child: const Icon(
Icons.location_on,
color: Colors.red,
size: 48,
)
),
if (state.getCurrentLocation != null) Marker(
point: state.getCurrentLocation!,
child: const Stack(
alignment: Alignment.center,
children: [
Icon(
Icons.circle,
color: Colors.white,
size: 23,
),
Icon(
Icons.circle,
color: Color.fromARGB(255, 0, 200, 0),
size: 16,
)
],
)
)
]
))
],
);
}

View File

@ -1,61 +0,0 @@
import 'package:app/util/watchState.dart';
import 'package:flutter/material.dart';
import 'package:flutter_timer_countdown/flutter_timer_countdown.dart';
import '../state/timeStatusState.dart';
class StatusView extends StatelessWidget {
const StatusView({super.key});
@override
Widget build(BuildContext context) {
return WatchState<TimeStatusState>((context, state) {
return Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Center(
child: Icon(
Icons.timer_outlined,
size: 40,
),
),
const VerticalDivider(
endIndent: 20,
indent: 20,
color: Colors.white,
thickness: 3,
width: 40,
),
Center(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Text(
"Ab 20 Uhr",
style: TextStyle(fontSize: 20),
),
TimerCountdown(
format: CountDownTimerFormat.hoursMinutesSeconds,
enableDescriptions: false,
spacerWidth: 5,
endTime: DateTime.now().add(state.remainingTimeUntilStart),
onEnd: () {
print("Timer finished");
},
),
],
),
),
],
)
);
});
}
}

57
lib/view/timeWarning.dart Normal file
View File

@ -0,0 +1,57 @@
import 'package:app/extensions/obtainProviderExtension.dart';
import 'package:flutter/material.dart';
import '../state/timeStatusState.dart';
class TimeWarningView extends StatelessWidget {
const TimeWarningView({super.key});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Center(
child: Icon(
Icons.warning_amber,
size: 40,
),
),
const VerticalDivider(
endIndent: 20,
indent: 20,
color: Colors.white,
thickness: 3,
width: 40,
),
const Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"In einer Fußgängerzone?",
style: TextStyle(fontSize: 20),
),
Flexible(
child: Text(
"hier ist der Konsum nur\nzwischen 20 und 7 Uhr gestattet",
textAlign: TextAlign.center,
),
),
],
),
Center(
child: IconButton(
onPressed: context.obtainState<TimeWarningState>().hide,
icon: const Icon(Icons.close)
),
)
],
)
);
}
}

View File

@ -121,6 +121,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.1"
fixnum:
dependency: transitive
description:
name: fixnum
sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
flutter:
dependency: "direct main"
description: flutter
@ -176,6 +184,54 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
geolocator:
dependency: "direct main"
description:
name: geolocator
sha256: "694ec58afe97787b5b72b8a0ab78c1a9244811c3c10e72c4362ef3c0ceb005cd"
url: "https://pub.dev"
source: hosted
version: "11.0.0"
geolocator_android:
dependency: transitive
description:
name: geolocator_android
sha256: f15d1536cd01b1399578f1da1eb5d566e7a718db6a3648f2c24d2e2f859f0692
url: "https://pub.dev"
source: hosted
version: "4.5.4"
geolocator_apple:
dependency: transitive
description:
name: geolocator_apple
sha256: bc2aca02423ad429cb0556121f56e60360a2b7d694c8570301d06ea0c00732fd
url: "https://pub.dev"
source: hosted
version: "2.3.7"
geolocator_platform_interface:
dependency: transitive
description:
name: geolocator_platform_interface
sha256: "009a21c4bc2761e58dccf07c24f219adaebe0ff707abdfd40b0a763d4003fab9"
url: "https://pub.dev"
source: hosted
version: "4.2.2"
geolocator_web:
dependency: transitive
description:
name: geolocator_web
sha256: "49d8f846ebeb5e2b6641fe477a7e97e5dd73f03cbfef3fd5c42177b7300fb0ed"
url: "https://pub.dev"
source: hosted
version: "3.0.0"
geolocator_windows:
dependency: transitive
description:
name: geolocator_windows
sha256: "53da08937d07c24b0d9952eb57a3b474e29aae2abf9dd717f7e1230995f13f0e"
url: "https://pub.dev"
source: hosted
version: "0.2.3"
html:
dependency: transitive
description:
@ -328,6 +384,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.0"
osm_nominatim:
dependency: "direct main"
description:
name: osm_nominatim
sha256: "037f1af3abee92cf34e33b562cec1acbb0210234b1bdf3d8975bdef3849e6287"
url: "https://pub.dev"
source: hosted
version: "3.0.0"
path:
dependency: transitive
description:
@ -344,6 +408,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.0.2"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.dev"
source: hosted
version: "2.1.8"
pointycastle:
dependency: transitive
description:
@ -389,6 +461,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.10.0"
sprintf:
dependency: transitive
description:
name: sprintf
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
url: "https://pub.dev"
source: hosted
version: "7.0.0"
stack_trace:
dependency: transitive
description:
@ -453,6 +533,78 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.2.2"
url_launcher:
dependency: "direct main"
description:
name: url_launcher
sha256: "0ecc004c62fd3ed36a2ffcbe0dd9700aee63bd7532d0b642a488b1ec310f492e"
url: "https://pub.dev"
source: hosted
version: "6.2.5"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
sha256: d4ed0711849dd8e33eb2dd69c25db0d0d3fdc37e0a62e629fe32f57a22db2745
url: "https://pub.dev"
source: hosted
version: "6.3.0"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
sha256: "9149d493b075ed740901f3ee844a38a00b33116c7c5c10d7fb27df8987fb51d5"
url: "https://pub.dev"
source: hosted
version: "6.2.5"
url_launcher_linux:
dependency: transitive
description:
name: url_launcher_linux
sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811
url: "https://pub.dev"
source: hosted
version: "3.1.1"
url_launcher_macos:
dependency: transitive
description:
name: url_launcher_macos
sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234
url: "https://pub.dev"
source: hosted
version: "3.1.0"
url_launcher_platform_interface:
dependency: transitive
description:
name: url_launcher_platform_interface
sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
url_launcher_web:
dependency: transitive
description:
name: url_launcher_web
sha256: "3692a459204a33e04bc94f5fb91158faf4f2c8903281ddd82915adecdb1a901d"
url: "https://pub.dev"
source: hosted
version: "2.3.0"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7
url: "https://pub.dev"
source: hosted
version: "3.1.1"
uuid:
dependency: transitive
description:
name: uuid
sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8
url: "https://pub.dev"
source: hosted
version: "4.3.3"
vector_math:
dependency: transitive
description:
@ -503,4 +655,4 @@ packages:
version: "3.1.2"
sdks:
dart: ">=3.3.2 <4.0.0"
flutter: ">=3.10.0"
flutter: ">=3.19.0"

View File

@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.0+1
version: 1.1.1+6
environment:
sdk: '>=3.3.2 <4.0.0'
@ -42,6 +42,9 @@ dependencies:
latlong2: ^0.9.0
http: ^1.2.1
flutter_timer_countdown: ^1.0.7
geolocator: ^11.0.0
osm_nominatim: ^3.0.0
url_launcher: ^6.2.5
dev_dependencies:
flutter_test:
@ -67,7 +70,8 @@ flutter:
# To add assets to your application, add an assets section, like this:
assets:
- assets/splash/splash.png
- assets/ca/
- assets/splash/splash.png
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware

View File

@ -39,6 +39,10 @@
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<style id="splash-screen-style">
html {
height: 100%