Compare commits
25 Commits
99cc98f853
...
master
Author | SHA1 | Date | |
---|---|---|---|
d428be6504 | |||
608ef75f9f | |||
4474eb0aa9 | |||
8be01e92a8 | |||
b64e1525ae | |||
27ffb07256 | |||
9fbc19ff7e | |||
a2326118d2 | |||
169ebeb30a | |||
7eb09390ad | |||
84c838b15e | |||
32c88fa857 | |||
c00d2fac05 | |||
507b321311 | |||
2a38b8ae2b | |||
c8278656cf | |||
e748df092c | |||
4ecf44bafa | |||
bffc603918 | |||
628d52f01f | |||
ef8c5e9039 | |||
ec8d3d466b | |||
4b384b378a | |||
799d1cb335 | |||
911c2738aa |
@ -2,7 +2,7 @@
|
|||||||
<application
|
<application
|
||||||
android:label="Bubatzkarte"
|
android:label="Bubatzkarte"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/launcher_icon">
|
android:icon="@mipmap/ic_launcher">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
@ -41,4 +41,8 @@
|
|||||||
<data android:mimeType="text/plain"/>
|
<data android:mimeType="text/plain"/>
|
||||||
</intent>
|
</intent>
|
||||||
</queries>
|
</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>
|
</manifest>
|
||||||
|
Before Width: | Height: | Size: 544 B After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 442 B After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 721 B After Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 15 KiB |
BIN
assets/background/banner.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
assets/background/tile.png
Normal file
After Width: | Height: | Size: 6.5 KiB |
30
assets/ca/lets-encrypt-r3.pem
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
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
|
||||||
|
nLRbwHOoq7hHwg==
|
||||||
|
-----END CERTIFICATE-----
|
BIN
assets/splash/splash512.png
Normal file
After Width: | Height: | Size: 44 KiB |
@ -1,5 +1,5 @@
|
|||||||
flutter_launcher_icons:
|
flutter_launcher_icons:
|
||||||
android: "launcher_icon"
|
android: true
|
||||||
ios: true
|
ios: true
|
||||||
image_path: "assets/splash/splash.png"
|
image_path: "assets/splash/splash.png"
|
||||||
min_sdk_android: 16
|
min_sdk_android: 16
|
8
lib/extensions/positionLatLngExtension.dart
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +1,23 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:app/state/timeStatusState.dart';
|
import 'package:app/state/timeStatusState.dart';
|
||||||
import 'package:app/state/mapState.dart';
|
import 'package:app/state/mapState.dart';
|
||||||
import 'package:app/view/home.dart';
|
import 'package:app/view/home.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:provider/provider.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(
|
runApp(
|
||||||
MultiProvider(
|
MultiProvider(
|
||||||
providers: [
|
providers: [
|
||||||
ChangeNotifierProvider<TimeStatusState>(create: (context) => TimeStatusState()),
|
ChangeNotifierProvider<TimeWarningState>(create: (context) => TimeWarningState()),
|
||||||
ChangeNotifierProvider<MapState>(create: (context) => MapState()),
|
ChangeNotifierProvider<MapState>(create: (context) => MapState()),
|
||||||
],
|
],
|
||||||
builder: (context, child) => const MyApp(),
|
builder: (context, child) => const MyApp(),
|
||||||
|
@ -1,22 +1,38 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter_map/flutter_map.dart';
|
import 'package:flutter_map/flutter_map.dart';
|
||||||
|
import 'package:geolocator/geolocator.dart';
|
||||||
|
import 'package:latlong2/latlong.dart';
|
||||||
|
|
||||||
class MapState extends ChangeNotifier {
|
class MapState extends ChangeNotifier {
|
||||||
bool _isLocationLock = false;
|
bool _isLocationLive = false;
|
||||||
bool _isCurrentlyFetchin = false;
|
LatLng? _currentLocation;
|
||||||
|
LatLng? _activeMarker;
|
||||||
final MapController _mapController = MapController();
|
final MapController _mapController = MapController();
|
||||||
|
final Geolocator _geolocator = Geolocator();
|
||||||
|
|
||||||
bool get followLocation => _isLocationLock;
|
bool get isLocationLive => _isLocationLive;
|
||||||
bool get isCurrentlyFetching => _isCurrentlyFetchin;
|
LatLng? get getCurrentLocation => _currentLocation;
|
||||||
|
LatLng? get getActiveMarker => _activeMarker;
|
||||||
MapController get getMapController => _mapController;
|
MapController get getMapController => _mapController;
|
||||||
|
Geolocator get getGeolocator => _geolocator;
|
||||||
|
|
||||||
toggleLocationLock() {
|
void setCurrentLocation(LatLng? currentLocation) {
|
||||||
_isLocationLock = !_isLocationLock;
|
_currentLocation = currentLocation;
|
||||||
|
if (currentLocation != null) _mapController.move(currentLocation, 16);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
set setNetworkActivity(bool active) {
|
void setActiveMarker(LatLng? marker) {
|
||||||
_isCurrentlyFetchin = active;
|
_activeMarker = marker;
|
||||||
|
if (marker != null) {
|
||||||
|
_isLocationLive = false;
|
||||||
|
_mapController.move(marker, 16);
|
||||||
|
}
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLocationLive(bool live) {
|
||||||
|
_isLocationLive = live;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,46 +1,11 @@
|
|||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class TimeStatusState extends ChangeNotifier {
|
class TimeWarningState extends ChangeNotifier {
|
||||||
final TimeOfDay legalStart = const TimeOfDay(hour: 20, minute: 00);
|
bool _show = true;
|
||||||
final TimeOfDay legalEnd = const TimeOfDay(hour: 07, minute: 00);
|
|
||||||
|
|
||||||
late Timer _refreshTimer;
|
bool get show => _show;
|
||||||
|
void hide() {
|
||||||
TimeStatusState() {
|
_show = false;
|
||||||
_refreshTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
|
||||||
notifyListeners();
|
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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,15 +1,42 @@
|
|||||||
|
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class AppInfoView extends StatelessWidget {
|
class AppInfoView extends StatelessWidget {
|
||||||
const AppInfoView({super.key});
|
const AppInfoView({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return const AlertDialog(
|
return AlertDialog(
|
||||||
title: Text("Achtung"),
|
title: const Text("Information"),
|
||||||
content: Flexible(
|
content: const Column(
|
||||||
child: Text("bla"),
|
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(),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:app/extensions/obtainProviderExtension.dart';
|
import 'package:app/extensions/obtainProviderExtension.dart';
|
||||||
|
import 'package:app/extensions/positionLatLngExtension.dart';
|
||||||
import 'package:app/state/mapState.dart';
|
import 'package:app/state/mapState.dart';
|
||||||
import 'package:app/state/timeStatusState.dart';
|
import 'package:app/state/timeStatusState.dart';
|
||||||
import 'package:app/util/loadingContainer.dart';
|
|
||||||
import 'package:app/util/watchState.dart';
|
import 'package:app/util/watchState.dart';
|
||||||
import 'package:app/view/appInfo.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:app/view/map.dart';
|
||||||
import 'package:flutter/material.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 {
|
class HomeView extends StatefulWidget {
|
||||||
const HomeView({super.key});
|
const HomeView({super.key});
|
||||||
@ -18,6 +21,101 @@ class HomeView extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _HomePageState extends State<HomeView> {
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@ -27,8 +125,13 @@ class _HomePageState extends State<HomeView> {
|
|||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.search),
|
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(
|
IconButton(
|
||||||
@ -43,30 +146,31 @@ class _HomePageState extends State<HomeView> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
child: WatchState<MapState>((context, state) => Icon(state.followLocation ? Icons.my_location : Icons.location_disabled)),
|
child: WatchState<MapState>((context, state) {
|
||||||
onPressed: () => context.obtainState<MapState>().toggleLocationLock(),
|
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(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
WatchState<TimeStatusState>((context, state) {
|
WatchState<TimeWarningState>((context, state) {
|
||||||
if(state.isCurrentlyLegal) return const SizedBox.shrink();
|
if(!state.show || DateTime.now().hour >= 20 || DateTime.now().hour < 7) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
return const SizedBox(
|
return const SizedBox(
|
||||||
height: 100,
|
height: 100,
|
||||||
child: StatusView(),
|
child: TimeWarningView(),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
Expanded(
|
const Expanded(
|
||||||
child: Consumer<MapState>(
|
child: MapView(),
|
||||||
builder: (context, state, child) {
|
|
||||||
return LoadingContainer(
|
|
||||||
loading: child == null,
|
|
||||||
fetching: state.isCurrentlyFetching,
|
|
||||||
child: const MapView(),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: const MapView(),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
70
lib/view/locationSearch.dart
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:app/extensions/obtainProviderExtension.dart';
|
import 'package:app/extensions/obtainProviderExtension.dart';
|
||||||
import 'package:app/state/mapState.dart';
|
import 'package:app/state/mapState.dart';
|
||||||
|
import 'package:app/util/watchState.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_map/flutter_map.dart';
|
import 'package:flutter_map/flutter_map.dart';
|
||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
@ -18,10 +19,13 @@ class _MapViewState extends State<MapView> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return FlutterMap(
|
return FlutterMap(
|
||||||
mapController: context.obtainState<MapState>().getMapController,
|
mapController: context.obtainState<MapState>().getMapController,
|
||||||
options: const MapOptions(
|
options: MapOptions(
|
||||||
initialCenter: LatLng(50.354, 7.5845),
|
onPositionChanged: (position, hasGesture) => {
|
||||||
|
if (hasGesture) context.obtainState<MapState>().setLocationLive(false)
|
||||||
|
},
|
||||||
|
initialCenter: const LatLng(50.354, 7.5845),
|
||||||
initialZoom: 16,
|
initialZoom: 16,
|
||||||
interactionOptions: InteractionOptions(
|
interactionOptions: const InteractionOptions(
|
||||||
flags: InteractiveFlag.all & ~InteractiveFlag.rotate
|
flags: InteractiveFlag.all & ~InteractiveFlag.rotate
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@ -34,9 +38,51 @@ class _MapViewState extends State<MapView> {
|
|||||||
tileProvider: NetworkTileProvider(
|
tileProvider: NetworkTileProvider(
|
||||||
httpClient: RetryClient(
|
httpClient: RetryClient(
|
||||||
Client(),
|
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,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
))
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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
@ -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)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
154
pubspec.lock
@ -121,6 +121,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.1"
|
version: "1.3.1"
|
||||||
|
fixnum:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fixnum
|
||||||
|
sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -176,6 +184,54 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
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:
|
html:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -328,6 +384,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
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:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -344,6 +408,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.2"
|
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:
|
pointycastle:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -389,6 +461,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.10.0"
|
version: "1.10.0"
|
||||||
|
sprintf:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sprintf
|
||||||
|
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.0.0"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -453,6 +533,78 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.2"
|
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:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -503,4 +655,4 @@ packages:
|
|||||||
version: "3.1.2"
|
version: "3.1.2"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.3.2 <4.0.0"
|
dart: ">=3.3.2 <4.0.0"
|
||||||
flutter: ">=3.10.0"
|
flutter: ">=3.19.0"
|
||||||
|
@ -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
|
# 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
|
# 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.
|
# 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:
|
environment:
|
||||||
sdk: '>=3.3.2 <4.0.0'
|
sdk: '>=3.3.2 <4.0.0'
|
||||||
@ -42,6 +42,9 @@ dependencies:
|
|||||||
latlong2: ^0.9.0
|
latlong2: ^0.9.0
|
||||||
http: ^1.2.1
|
http: ^1.2.1
|
||||||
flutter_timer_countdown: ^1.0.7
|
flutter_timer_countdown: ^1.0.7
|
||||||
|
geolocator: ^11.0.0
|
||||||
|
osm_nominatim: ^3.0.0
|
||||||
|
url_launcher: ^6.2.5
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
@ -67,6 +70,7 @@ flutter:
|
|||||||
|
|
||||||
# To add assets to your application, add an assets section, like this:
|
# To add assets to your application, add an assets section, like this:
|
||||||
assets:
|
assets:
|
||||||
|
- assets/ca/
|
||||||
- assets/splash/splash.png
|
- assets/splash/splash.png
|
||||||
|
|
||||||
# An image asset can refer to one or more resolution-specific "variants", see
|
# An image asset can refer to one or more resolution-specific "variants", see
|
||||||
|
@ -39,6 +39,10 @@
|
|||||||
|
|
||||||
|
|
||||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
|
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<style id="splash-screen-style">
|
<style id="splash-screen-style">
|
||||||
html {
|
html {
|
||||||
height: 100%
|
height: 100%
|
||||||
|