import 'dart:collection'; import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; LandmarkType typeSightseeing = LandmarkType(name: 'sightseeing'); LandmarkType typeNature = LandmarkType(name: 'nature'); LandmarkType typeShopping = LandmarkType(name: 'shopping'); // LandmarkType museum = LandmarkType(name: 'Museum'); // LandmarkType restaurant = LandmarkType(name: 'Restaurant'); LandmarkType typeStart = LandmarkType(name: 'start'); LandmarkType typeFinish = LandmarkType(name: 'finish'); final class Landmark extends LinkedListEntry{ // A linked node of a list of Landmarks final String uuid; final String name; final List location; final LandmarkType type; final bool? isSecondary; // description to be shown in the overview final String? nameEN; final String? websiteURL; String? imageURL; // not final because it can be patched final String? description; final Duration? duration; bool visited; // Next node // final Landmark? next; final Duration? tripTime; Landmark({ required this.uuid, required this.name, required this.location, required this.type, this.isSecondary, this.nameEN, this.websiteURL, this.imageURL, this.description, this.duration, this.visited = false, // this.next, this.tripTime, }); factory Landmark.fromJson(Map json) { if (json case { // automatically match all the non-optionals and cast them to the right type 'uuid': String uuid, 'name': String name, 'location': List location, 'type': String type, }) { // refine the parsing on a few fields List locationFixed = List.from(location); // parse the rest separately, they could be missing LandmarkType typeFixed = LandmarkType(name: type); final isSecondary = json['is_secondary'] as bool?; final nameEN = json['name_en'] as String?; final websiteURL = json['website_url'] as String?; final imageURL = json['image_url'] as String?; final description = json['description'] as String?; var duration = Duration(minutes: json['duration'] ?? 0) as Duration?; final visited = json['visited'] ?? false as bool; var tripTime = Duration(minutes: json['time_to_reach_next'] ?? 0) as Duration?; return Landmark( uuid: uuid, name: name, location: locationFixed, type: typeFixed, isSecondary: isSecondary, nameEN: nameEN, websiteURL: websiteURL, imageURL: imageURL, description: description, duration: duration, visited: visited, tripTime: tripTime ); } else { throw FormatException('Invalid JSON: $json'); } } @override bool operator ==(Object other) { return other is Landmark && uuid == other.uuid; } Map toJson() => { 'uuid': uuid, 'name': name, 'location': location, 'type': type.name, 'is_secondary': isSecondary, 'name_en': nameEN, 'website_url': websiteURL, 'image_url': imageURL, 'description': description, 'duration': duration?.inMinutes, 'visited': visited, 'trip_time': tripTime?.inMinutes, }; } class LandmarkType { final String name; // final String description; Icon icon; LandmarkType({required this.name, this.icon = const Icon(Icons.location_on)}) { switch (name) { case 'sightseeing': icon = const Icon(Icons.castle); break; case 'nature': icon = const Icon(Icons.eco); break; case 'shopping': icon = const Icon(Icons.shopping_cart); break; case 'start': icon = const Icon(Icons.play_arrow); break; case 'finish': icon = const Icon(Icons.flag); break; default: icon = const Icon(Icons.location_on); } } @override bool operator ==(Object other) { if (other is LandmarkType) { return name == other.name; } else { return false; } } } // Helpers // Handling the landmarks requires a little bit of special care because the linked list is not directly representable in json (Landmark, String?) getLandmarkFromPrefs(SharedPreferences prefs, String uuid) { String? content = prefs.getString('landmark_$uuid'); Map json = jsonDecode(content!); String? nextUUID = json['next_uuid']; return (Landmark.fromJson(json), nextUUID); } void landmarkToPrefs(SharedPreferences prefs, Landmark current, Landmark? next) { Map json = current.toJson(); json['next_uuid'] = next?.uuid; prefs.setString('landmark_${current.uuid}', jsonEncode(json)); }