mirror of
https://github.com/Myzel394/quid_faciam_hodie.git
synced 2025-06-18 23:35:25 +02:00
added memory map screen; improvements on memory sheet
This commit is contained in:
parent
8549d7c4a9
commit
4aff60a00c
@ -62,6 +62,20 @@
|
||||
"memorySheetUpdateMemoryMakePublic": "Veröffentlichen",
|
||||
"memorySheetUpdateMemoryMakePrivate": "Privat machen",
|
||||
"memorySheetDeleteMemory": "Erinnerung löschen",
|
||||
"memorySheetViewMoreDetails": "Mehr Details",
|
||||
|
||||
|
||||
"memoryMapScreenTitle": "Standort",
|
||||
"memoryMapScreenExpandForMoreDescription": "Erweitere für mehr Details",
|
||||
"memoryMapScreenOpenNavigation": "Navigation öffnen",
|
||||
"memoryMapScreenValuesAddressLabel": "Geschätzte Adresse",
|
||||
"memoryMapScreenValuesAddressIsLoading": "Adresse wird geladen...",
|
||||
"memoryMapScreenValuesAddressIsUnavailable": "Address nicht verfügbar",
|
||||
"memoryMapScreenValuesLatitudeLabel": "Latitude",
|
||||
"memoryMapScreenValuesLongitudeLabel": "Longitude",
|
||||
"memoryMapScreenValuesAltitudeLabel": "Höhe",
|
||||
"memoryMapScreenValuesAccuracyLabel": "Genauigkeit",
|
||||
"memoryMapScreenValuesSpeedLabel": "Geschwindigkeit",
|
||||
|
||||
|
||||
"emptyScreenTitle": "Houston, wir haben ein Problem",
|
||||
|
@ -91,7 +91,44 @@
|
||||
}
|
||||
},
|
||||
"memorySheetMapEstimatedAddressLabel": "Estimated Address",
|
||||
"memorySheetMapOpenNavigation": "Open Navigation",
|
||||
"memorySheetViewMoreDetails": "View More Details",
|
||||
|
||||
|
||||
"memoryMapScreenTitle": "Location",
|
||||
"memoryMapScreenExpandForMoreDescription": "Expand for more details",
|
||||
"memoryMapScreenOpenNavigation": "Open Navigation",
|
||||
"memoryMapScreenValuesAddressLabel": "Estimated Address",
|
||||
"memoryMapScreenValuesAddressIsLoading": "Loading address...",
|
||||
"memoryMapScreenValuesAddressIsUnavailable": "Address not available",
|
||||
"memoryMapScreenValuesLatitudeLabel": "Latitude",
|
||||
"memoryMapScreenValuesLongitudeLabel": "Longitude",
|
||||
"memoryMapScreenValuesAltitudeLabel": "Altitude",
|
||||
"memoryMapScreenValuesAltitudeValue": "{valueInMeter}m",
|
||||
"@memoryMapScreenValuesAltitudeValue": {
|
||||
"placeholders": {
|
||||
"valueInMeter": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"memoryMapScreenValuesAccuracyLabel": "Accuracy",
|
||||
"memoryMapScreenValuesAccuracyValue": "{valueInMeter}m",
|
||||
"@memoryMapScreenValuesAccuracyValue": {
|
||||
"placeholders": {
|
||||
"valueInMeter": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"memoryMapScreenValuesSpeedLabel": "Speed",
|
||||
"memoryMapScreenValuesSpeedValue": "{valueInKmh} km/h",
|
||||
"@memoryMapScreenValuesSpeedValue": {
|
||||
"placeholders": {
|
||||
"valueInKmh": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
"emptyScreenTitle": "Houston, we have a problem",
|
||||
|
204
lib/screens/memory_map_screen.dart
Normal file
204
lib/screens/memory_map_screen.dart
Normal file
@ -0,0 +1,204 @@
|
||||
import 'package:expandable_bottom_sheet/expandable_bottom_sheet.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:flutter_osm_plugin/flutter_osm_plugin.dart';
|
||||
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
|
||||
import 'package:quid_faciam_hodie/constants/spacing.dart';
|
||||
import 'package:quid_faciam_hodie/foreign_types/memory_location.dart';
|
||||
import 'package:quid_faciam_hodie/utils/loadable.dart';
|
||||
import 'package:quid_faciam_hodie/utils/lookup_address.dart';
|
||||
import 'package:quid_faciam_hodie/utils/theme.dart';
|
||||
import 'package:quid_faciam_hodie/widgets/icon_button_child.dart';
|
||||
import 'package:quid_faciam_hodie/widgets/key_value_info.dart';
|
||||
import 'package:quid_faciam_hodie/widgets/sheet_indicator.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class MemoryMapScreen extends StatefulWidget {
|
||||
final MemoryLocation location;
|
||||
|
||||
const MemoryMapScreen({
|
||||
Key? key,
|
||||
required this.location,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<MemoryMapScreen> createState() => _MemoryMapScreenState();
|
||||
}
|
||||
|
||||
class _MemoryMapScreenState extends State<MemoryMapScreen> with Loadable {
|
||||
late final MapController controller;
|
||||
String? address;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
callWithLoading(fetchAddress);
|
||||
|
||||
controller = MapController(
|
||||
initPosition: GeoPoint(
|
||||
latitude: widget.location.latitude,
|
||||
longitude: widget.location.longitude,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> fetchAddress() async {
|
||||
try {
|
||||
final foundAddress = await lookupAddress(
|
||||
latitude: widget.location.latitude,
|
||||
longitude: widget.location.longitude,
|
||||
);
|
||||
|
||||
setState(() {
|
||||
address = foundAddress;
|
||||
});
|
||||
} catch (error) {
|
||||
setState(() {
|
||||
address = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void drawCircle() => controller.drawCircle(
|
||||
CircleOSM(
|
||||
key: 'accuracy',
|
||||
color: Colors.blue,
|
||||
centerPoint: GeoPoint(
|
||||
latitude: widget.location.latitude,
|
||||
longitude: widget.location.longitude,
|
||||
),
|
||||
radius: widget.location.accuracy,
|
||||
strokeWidth: 4,
|
||||
),
|
||||
);
|
||||
|
||||
List<StaticPositionGeoPoint> get staticPoints => [
|
||||
StaticPositionGeoPoint(
|
||||
'position',
|
||||
const MarkerIcon(
|
||||
icon: Icon(Icons.location_on, size: 150, color: Colors.blue),
|
||||
),
|
||||
[
|
||||
GeoPoint(
|
||||
latitude: widget.location.latitude,
|
||||
longitude: widget.location.longitude,
|
||||
)
|
||||
],
|
||||
)
|
||||
];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final localizations = AppLocalizations.of(context)!;
|
||||
final backgroundColor = getSheetColor(context);
|
||||
|
||||
return PlatformScaffold(
|
||||
appBar: PlatformAppBar(
|
||||
title: Text(localizations.memoryMapScreenTitle),
|
||||
),
|
||||
body: ExpandableBottomSheet(
|
||||
enableToggle: true,
|
||||
background: OSMFlutter(
|
||||
controller: controller,
|
||||
initZoom: 13,
|
||||
stepZoom: 1.0,
|
||||
trackMyPosition: true,
|
||||
staticPoints: staticPoints,
|
||||
onMapIsReady: (_) {
|
||||
drawCircle();
|
||||
},
|
||||
),
|
||||
persistentHeader: Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(SMALL_SPACE),
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor,
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(LARGE_SPACE),
|
||||
topRight: Radius.circular(LARGE_SPACE),
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(MEDIUM_SPACE),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
const SheetIndicator(),
|
||||
const SizedBox(height: MEDIUM_SPACE),
|
||||
Text(
|
||||
localizations.memoryMapScreenExpandForMoreDescription,
|
||||
style: getBodyTextTextStyle(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
expandableContent: Container(
|
||||
color: backgroundColor,
|
||||
padding: const EdgeInsets.all(MEDIUM_SPACE),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
KeyValueInfo(
|
||||
title: localizations.memoryMapScreenValuesAddressLabel,
|
||||
value: () {
|
||||
if (isLoading) {
|
||||
return localizations.memoryMapScreenValuesAddressIsLoading;
|
||||
}
|
||||
|
||||
if (address == null) {
|
||||
return localizations
|
||||
.memoryMapScreenValuesAddressIsUnavailable;
|
||||
}
|
||||
|
||||
return address!;
|
||||
}(),
|
||||
),
|
||||
const SizedBox(height: SMALL_SPACE),
|
||||
PlatformTextButton(
|
||||
onPressed: () {
|
||||
final url =
|
||||
'geo:0,0?q=${widget.location.latitude},${widget.location.longitude} (${address ?? ''})';
|
||||
launchUrl(Uri.parse(url));
|
||||
},
|
||||
child: IconButtonChild(
|
||||
icon: Icon(context.platformIcons.location),
|
||||
label: Text(localizations.memoryMapScreenOpenNavigation),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: SMALL_SPACE),
|
||||
KeyValueInfo(
|
||||
title: localizations.memoryMapScreenValuesLatitudeLabel,
|
||||
value: widget.location.latitude.toString(),
|
||||
),
|
||||
KeyValueInfo(
|
||||
title: localizations.memoryMapScreenValuesLongitudeLabel,
|
||||
value: widget.location.longitude.toString(),
|
||||
),
|
||||
KeyValueInfo(
|
||||
title: localizations.memoryMapScreenValuesAccuracyLabel,
|
||||
value: localizations.memoryMapScreenValuesAccuracyValue(
|
||||
widget.location.accuracy.toString(),
|
||||
),
|
||||
icon: Icons.circle,
|
||||
),
|
||||
KeyValueInfo(
|
||||
title: localizations.memoryMapScreenValuesSpeedLabel,
|
||||
value: localizations.memoryMapScreenValuesSpeedValue(
|
||||
widget.location.speed.toString(),
|
||||
),
|
||||
icon: Icons.speed_rounded,
|
||||
),
|
||||
KeyValueInfo(
|
||||
title: localizations.memoryMapScreenValuesAltitudeLabel,
|
||||
value: localizations.memoryMapScreenValuesAltitudeValue(
|
||||
widget.location.altitude.toString(),
|
||||
),
|
||||
icon: Icons.landscape_rounded,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -128,12 +128,7 @@ class _SettingsScreenState extends AuthRequiredState<SettingsScreen>
|
||||
Text(
|
||||
localizations
|
||||
.settingsScreenAccountSectionCreationDateLabel,
|
||||
style: platformThemeData(
|
||||
context,
|
||||
material: (data) => data.textTheme.bodySmall,
|
||||
cupertino: (data) =>
|
||||
data.textTheme.tabLabelTextStyle,
|
||||
),
|
||||
style: getCaptionTextStyle(context),
|
||||
)
|
||||
],
|
||||
),
|
||||
|
@ -1,17 +1,12 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:flutter_osm_plugin/flutter_osm_plugin.dart';
|
||||
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:quid_faciam_hodie/constants/spacing.dart';
|
||||
import 'package:quid_faciam_hodie/constants/values.dart';
|
||||
import 'package:quid_faciam_hodie/foreign_types/memory_location.dart';
|
||||
import 'package:quid_faciam_hodie/widgets/fade_and_move_in_animation.dart';
|
||||
import 'package:quid_faciam_hodie/screens/memory_map_screen.dart';
|
||||
import 'package:quid_faciam_hodie/widgets/icon_button_child.dart';
|
||||
import 'package:quid_faciam_hodie/widgets/key_value_info.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class MemoryLocationView extends StatefulWidget {
|
||||
final MemoryLocation location;
|
||||
@ -27,14 +22,11 @@ class MemoryLocationView extends StatefulWidget {
|
||||
|
||||
class _MemoryLocationViewState extends State<MemoryLocationView> {
|
||||
late final MapController controller;
|
||||
String address = '';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
lookupAddress();
|
||||
|
||||
controller = MapController(
|
||||
initPosition: GeoPoint(
|
||||
latitude: widget.location.latitude,
|
||||
@ -43,22 +35,11 @@ class _MemoryLocationViewState extends State<MemoryLocationView> {
|
||||
);
|
||||
}
|
||||
|
||||
void lookupAddress() async {
|
||||
final url =
|
||||
'https://geocode.maps.co/reverse?lat=${widget.location.latitude}&lon=${widget.location.longitude}';
|
||||
final uri = Uri.parse(url);
|
||||
@override
|
||||
void dispose() {
|
||||
controller.dispose();
|
||||
|
||||
final response = await http.get(uri);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
setState(() {
|
||||
address = '';
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
address = jsonDecode(response.body)['display_name'];
|
||||
});
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void drawCircle() => controller.drawCircle(
|
||||
@ -104,45 +85,45 @@ class _MemoryLocationViewState extends State<MemoryLocationView> {
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
height: 400,
|
||||
child: OSMFlutter(
|
||||
controller: controller,
|
||||
initZoom: 14,
|
||||
stepZoom: 1.0,
|
||||
staticPoints: staticPoints,
|
||||
onMapIsReady: (_) {
|
||||
drawCircle();
|
||||
},
|
||||
child: GestureDetector(
|
||||
// Avoid panning, map is view-only
|
||||
onDoubleTap: () {},
|
||||
child: OSMFlutter(
|
||||
controller: controller,
|
||||
initZoom: 14,
|
||||
minZoomLevel: 14,
|
||||
maxZoomLevel: 14,
|
||||
staticPoints: staticPoints,
|
||||
onMapIsReady: (_) {
|
||||
drawCircle();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: MEDIUM_SPACE),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
const SizedBox(height: MEDIUM_SPACE),
|
||||
if (address.isNotEmpty) ...[
|
||||
FadeAndMoveInAnimation(
|
||||
child: KeyValueInfo(
|
||||
title: localizations.memorySheetMapEstimatedAddressLabel,
|
||||
value: address,
|
||||
icon: context.platformIcons.location,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: MEDIUM_SPACE),
|
||||
],
|
||||
PlatformTextButton(
|
||||
onPressed: () {
|
||||
final url =
|
||||
'geo:0,0?q=${widget.location.latitude},${widget.location.longitude} ($address)';
|
||||
launchUrl(Uri.parse(url));
|
||||
},
|
||||
child: IconButtonChild(
|
||||
icon: Icon(context.platformIcons.location),
|
||||
label: Text(localizations.memorySheetMapOpenNavigation),
|
||||
const SizedBox(height: MEDIUM_SPACE),
|
||||
PlatformTextButton(
|
||||
child: IconButtonChild(
|
||||
icon: Icon(context.platformIcons.fullscreen),
|
||||
label: Text(localizations.memorySheetViewMoreDetails),
|
||||
),
|
||||
onPressed: () async {
|
||||
await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => MemoryMapScreen(
|
||||
location: widget.location,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: MEDIUM_SPACE),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
@ -129,12 +129,7 @@ class _MemorySheetState extends State<MemorySheet> with Loadable {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final localizations = AppLocalizations.of(context)!;
|
||||
final backgroundColor = platformThemeData(
|
||||
context,
|
||||
material: (data) =>
|
||||
data.bottomSheetTheme.modalBackgroundColor ?? data.bottomAppBarColor,
|
||||
cupertino: (data) => data.barBackgroundColor,
|
||||
);
|
||||
final backgroundColor = getSheetColor(context);
|
||||
|
||||
return ExpandableBottomSheet(
|
||||
background: GestureDetector(
|
||||
@ -151,13 +146,13 @@ class _MemorySheetState extends State<MemorySheet> with Loadable {
|
||||
),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: MEDIUM_SPACE,
|
||||
horizontal: MEDIUM_SPACE,
|
||||
if (widget.memory.location != null) ...[
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(SMALL_SPACE),
|
||||
child: SheetIndicator(),
|
||||
),
|
||||
child: SheetIndicator(),
|
||||
),
|
||||
const SizedBox(height: MEDIUM_SPACE),
|
||||
],
|
||||
Text(
|
||||
localizations.memorySheetTitle,
|
||||
style: getTitleTextStyle(context),
|
||||
@ -224,43 +219,41 @@ class _MemorySheetState extends State<MemorySheet> with Loadable {
|
||||
? buildLoadingIndicator()
|
||||
: null,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
expandableContent: Container(
|
||||
width: double.infinity,
|
||||
color: backgroundColor,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
widget.memory.location == null
|
||||
? const SizedBox.shrink()
|
||||
: MemoryLocationView(
|
||||
location: widget.memory.location!,
|
||||
const SizedBox(height: MEDIUM_SPACE),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
context.platformIcons.time,
|
||||
size: platformThemeData(
|
||||
context,
|
||||
material: (data) => data.textTheme.bodyLarge!.fontSize,
|
||||
cupertino: (data) => data.textTheme.textStyle.fontSize,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(MEDIUM_SPACE),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Text(
|
||||
localizations.memorySheetCreatedAtDataKey(
|
||||
DateFormat.jms().format(
|
||||
widget.memory.creationDate,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: TINY_SPACE),
|
||||
Text(
|
||||
localizations.memorySheetCreatedAtDataKey(
|
||||
DateFormat.jms().format(
|
||||
widget.memory.creationDate,
|
||||
),
|
||||
style: getBodyTextTextStyle(context),
|
||||
),
|
||||
const SizedBox(height: SMALL_SPACE),
|
||||
Text(
|
||||
widget.memory.id,
|
||||
textAlign: TextAlign.center,
|
||||
style: getBodyTextTextStyle(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
style: getBodyTextTextStyle(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
expandableContent: widget.memory.location == null
|
||||
? const SizedBox.shrink()
|
||||
: Container(
|
||||
width: double.infinity,
|
||||
color: backgroundColor,
|
||||
child: MemoryLocationView(
|
||||
location: widget.memory.location!,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
15
lib/utils/lookup_address.dart
Normal file
15
lib/utils/lookup_address.dart
Normal file
@ -0,0 +1,15 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
Future<String> lookupAddress({
|
||||
required final double latitude,
|
||||
required final double longitude,
|
||||
}) async {
|
||||
final url = 'https://geocode.maps.co/reverse?lat=$latitude&lon=$longitude';
|
||||
final uri = Uri.parse(url);
|
||||
|
||||
final response = await http.get(uri);
|
||||
|
||||
return jsonDecode(response.body)['display_name'];
|
||||
}
|
@ -24,3 +24,16 @@ TextStyle getSubTitleTextStyle(final BuildContext context) => platformThemeData(
|
||||
material: (data) => data.textTheme.subtitle1!,
|
||||
cupertino: (data) => data.textTheme.navTitleTextStyle,
|
||||
);
|
||||
|
||||
TextStyle getCaptionTextStyle(final BuildContext context) => platformThemeData(
|
||||
context,
|
||||
material: (data) => data.textTheme.caption!,
|
||||
cupertino: (data) => data.textTheme.tabLabelTextStyle,
|
||||
);
|
||||
|
||||
Color getSheetColor(final BuildContext context) => platformThemeData(
|
||||
context,
|
||||
material: (data) =>
|
||||
data.bottomSheetTheme.modalBackgroundColor ?? data.bottomAppBarColor,
|
||||
cupertino: (data) => data.barBackgroundColor,
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user