feat: add route history, map controls, and geofence/FP management

- Position history with polyline trail and date range picker
  - Map style selector (standard, voyager, light, dark, satellite) persisted via SharedPreferences
  - Geofence and frequent place CRUD with info cards
  - Device banner with swipeable carousel
  - Refresh position button
  - Widget extraction: map controls, info cards, device banner, modal overlay
This commit is contained in:
2026-03-18 19:45:29 +01:00
parent cf0c55eafe
commit 8e3a27e0d3
85 changed files with 11026 additions and 1020 deletions

View File

@@ -1,8 +1,10 @@
library legacy_shared;
export 'src/providers/map_style_provider.dart';
export 'src/providers/selected_device_provider.dart';
export 'src/widgets/layouts/page_layout.dart';
export 'src/components/section_button.dart';
export 'src/widgets/pulsing_location_marker.dart';
export 'src/widgets/week_day_chips.dart';
export 'src/components/menu_button.dart';
export 'src/data/models/device_response_model.dart';

View File

@@ -0,0 +1,44 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
const _mapStyleKey = 'location_map_style';
enum MapStyle {
standard('https://tile.openstreetmap.org/{z}/{x}/{y}.png'),
voyager('https://basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}.png'),
light('https://basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png'),
dark('https://basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png'),
satellite('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}');
const MapStyle(this.urlTemplate);
final String urlTemplate;
}
final mapStyleProvider =
NotifierProvider<MapStyleNotifier, MapStyle>(MapStyleNotifier.new);
class MapStyleNotifier extends Notifier<MapStyle> {
@override
MapStyle build() {
_load();
return MapStyle.standard;
}
Future<void> _load() async {
final prefs = await SharedPreferences.getInstance();
final savedName = prefs.getString(_mapStyleKey);
if (savedName == null) return;
final style =
MapStyle.values.where((s) => s.name == savedName).firstOrNull;
if (style != null && style != state) {
state = style;
}
}
Future<void> setStyle(MapStyle style) async {
state = style;
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_mapStyleKey, style.name);
}
}

View File

@@ -0,0 +1,88 @@
import 'package:flutter/material.dart';
class PulsingLocationMarker extends StatefulWidget {
final Color? color;
const PulsingLocationMarker({super.key, this.color});
@override
State<PulsingLocationMarker> createState() => _PulsingLocationMarkerState();
}
class _PulsingLocationMarkerState extends State<PulsingLocationMarker>
with TickerProviderStateMixin {
late final AnimationController _controller1;
late final AnimationController _controller2;
@override
void initState() {
super.initState();
_controller1 = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 2000),
)..repeat();
_controller2 = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 2000),
);
Future.delayed(const Duration(milliseconds: 1000), () {
if (mounted) _controller2.repeat();
});
}
@override
void dispose() {
_controller1.dispose();
_controller2.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final c = widget.color ?? const Color(0xFF329E95);
return Stack(
alignment: Alignment.center,
children: [
_buildRipple(_controller1, c),
_buildRipple(_controller2, c),
Container(
width: 18,
height: 18,
decoration: BoxDecoration(
color: c,
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 3),
boxShadow: [
BoxShadow(
color: c.withValues(alpha: 0.4),
blurRadius: 6,
spreadRadius: 1,
),
],
),
),
],
);
}
Widget _buildRipple(AnimationController controller, Color color) {
return AnimatedBuilder(
animation: controller,
builder: (context, child) {
final scale = 1.0 + controller.value * 2.5;
final opacity = (1.0 - controller.value).clamp(0.0, 0.5);
return Transform.scale(
scale: scale,
child: Container(
width: 18,
height: 18,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: color.withValues(alpha: opacity),
),
),
);
},
);
}
}

View File

@@ -28,6 +28,7 @@ dependencies:
dio: ^5.9.0
get_it: ^9.0.5
flutter_riverpod: ^3.0.3
shared_preferences: ^2.5.0
freezed_annotation: ^3.1.0
freezed: ^3.2.3
json_annotation: ^4.9.0