chore(wip): upgrade dependencies, begin refactor
This commit is contained in:
@@ -10,7 +10,8 @@ pkgs.mkShell {
|
||||
# androidenv.androidPkgs.ndk-bundle
|
||||
];
|
||||
|
||||
# Set up Android SDK paths if needed
|
||||
# Setting up android build environments on nix is a bit of a pain - immutable paths, etc.
|
||||
# I used a hacky workaround by manually downloading the SDK and NDK from the website and simply telling the shell where to find them
|
||||
ANDROID_HOME = "/scratch/remy/android";
|
||||
|
||||
shellHook = ''
|
||||
|
||||
@@ -26,7 +26,7 @@ To truly deploy a new version of the application, i.e. to the official app store
|
||||
git tag -a v<name> -m "Release <name>"
|
||||
git push origin v<name>
|
||||
```
|
||||
We adhere to the [Semantic Versioning](https://semver.org/) standard, so the tag should be of the form `v0.1.8` for example.
|
||||
We adhere to the [Semantic Versioning](https://semver.org/) standard, so the tag should be of the form `v0.1.8` for example.
|
||||
|
||||
### Icons and logos
|
||||
The application uses a custom launcher icon and splash screen. These are managed platform-independently using the `flutter_launcher_icons` package.
|
||||
@@ -66,3 +66,10 @@ These are used by the CI/CD pipeline to deploy the application.
|
||||
- `IOS_ASC_KEY_ID` as well
|
||||
- `IOS_MATCH_PASSWORD` is used by fastlane match to download the certificates
|
||||
- `IOS_MATCH_REPO_SSH_KEY_BASE64` is used to authenticate with the git repository where the certificates are stored
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Android SDK&NDK setup
|
||||
|
||||
@@ -49,7 +49,8 @@ if (secretPropertiesFile.exists()) {
|
||||
android {
|
||||
namespace "com.anydev.anyway"
|
||||
compileSdk flutter.compileSdkVersion
|
||||
ndkVersion flutter.ndkVersion
|
||||
ndkVersion = "27.0.12077973"
|
||||
// TODO - set back to ndkVersion flutter.ndkVersion once https://github.com/flutter/flutter/issues/139427 is resolved
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
@@ -65,7 +66,7 @@ android {
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
|
||||
|
||||
applicationId "com.anydev.anyway"
|
||||
// You can update the following values to match your application needs.
|
||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
||||
|
||||
83
frontend/lib/core/constants.dart
Normal file
83
frontend/lib/core/constants.dart
Normal file
@@ -0,0 +1,83 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
const String APP_NAME = 'AnyWay';
|
||||
|
||||
String API_URL_BASE = 'https://anyway.anydev.info';
|
||||
String API_URL_DEBUG = 'https://anyway-stg.anydev.info';
|
||||
String PRIVACY_URL = 'https://anydev.info/privacy';
|
||||
|
||||
const String MAP_ID = '41c21ac9b81dbfd8';
|
||||
|
||||
|
||||
const Color GRADIENT_START = Color(0xFFF9B208);
|
||||
const Color GRADIENT_END = Color(0xFFE72E77);
|
||||
|
||||
const Color PRIMARY_COLOR = Color(0xFFF38F1A);
|
||||
|
||||
|
||||
|
||||
const double TRIP_PANEL_MAX_HEIGHT = 0.8;
|
||||
const double TRIP_PANEL_MIN_HEIGHT = 0.12;
|
||||
|
||||
ThemeData APP_THEME = ThemeData(
|
||||
primaryColor: PRIMARY_COLOR,
|
||||
|
||||
scaffoldBackgroundColor: Colors.white,
|
||||
cardColor: Colors.white,
|
||||
useMaterial3: true,
|
||||
|
||||
colorScheme: const ColorScheme.light(
|
||||
primary: PRIMARY_COLOR,
|
||||
secondary: GRADIENT_END,
|
||||
surface: Colors.white,
|
||||
error: Colors.red,
|
||||
onPrimary: Colors.white,
|
||||
onSecondary: Color.fromARGB(255, 30, 22, 22),
|
||||
onSurface: Colors.black,
|
||||
onError: Colors.white,
|
||||
brightness: Brightness.light,
|
||||
),
|
||||
|
||||
|
||||
textButtonTheme: const TextButtonThemeData(
|
||||
style: ButtonStyle(
|
||||
foregroundColor: WidgetStatePropertyAll(PRIMARY_COLOR),
|
||||
side: WidgetStatePropertyAll(
|
||||
BorderSide(
|
||||
color: PRIMARY_COLOR,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
|
||||
elevatedButtonTheme: const ElevatedButtonThemeData(
|
||||
style: ButtonStyle(
|
||||
foregroundColor: WidgetStatePropertyAll(PRIMARY_COLOR),
|
||||
)
|
||||
),
|
||||
|
||||
outlinedButtonTheme: const OutlinedButtonThemeData(
|
||||
style: ButtonStyle(
|
||||
foregroundColor: WidgetStatePropertyAll(PRIMARY_COLOR),
|
||||
)
|
||||
),
|
||||
|
||||
|
||||
sliderTheme: const SliderThemeData(
|
||||
trackHeight: 15,
|
||||
inactiveTrackColor: Colors.grey,
|
||||
thumbColor: PRIMARY_COLOR,
|
||||
activeTrackColor: GRADIENT_END
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
const Gradient APP_GRADIENT = LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [GRADIENT_START, GRADIENT_END],
|
||||
);
|
||||
|
||||
|
||||
final GlobalKey<ScaffoldMessengerState> rootScaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
|
||||
16
frontend/lib/core/dio_client.dart
Normal file
16
frontend/lib/core/dio_client.dart
Normal file
@@ -0,0 +1,16 @@
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
class DioClient {
|
||||
final Dio dio;
|
||||
|
||||
DioClient({required String baseUrl}): dio = Dio(BaseOptions(
|
||||
baseUrl: baseUrl,
|
||||
connectTimeout: const Duration(seconds: 5),
|
||||
receiveTimeout: const Duration(seconds: 120),
|
||||
// also accept 500 errors, since we cannot rule out that the server is at fault. We still want to gracefully handle these errors
|
||||
validateStatus: (status) => status! <= 500,
|
||||
receiveDataWhenStatusError: true,
|
||||
contentType: Headers.jsonContentType,
|
||||
responseType: ResponseType.json,
|
||||
));
|
||||
}
|
||||
0
frontend/lib/data/README.md
Normal file
0
frontend/lib/data/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
import 'package:anyway/domain/entities/landmark.dart';
|
||||
|
||||
abstract class TripRemoteDataSource {
|
||||
Future<List<Landmark>> fetchLandmarks();
|
||||
}
|
||||
30
frontend/lib/data/models/landmark_model.dart
Normal file
30
frontend/lib/data/models/landmark_model.dart
Normal file
@@ -0,0 +1,30 @@
|
||||
import 'package:anyway/domain/entities/landmark.dart';
|
||||
import 'package:anyway/domain/entities/landmark_description.dart';
|
||||
import 'package:anyway/domain/entities/landmark_type.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'landmark_model.freezed.dart';
|
||||
part 'landmark_model.g.dart';
|
||||
|
||||
@freezed
|
||||
abstract class LandmarkModel with _$LandmarkModel {
|
||||
const factory LandmarkModel({
|
||||
required String uuid,
|
||||
required String name,
|
||||
required List<double> location,
|
||||
required String type,
|
||||
required bool isSecondary,
|
||||
required String description,
|
||||
}) = _LandmarkModel;
|
||||
|
||||
factory LandmarkModel.fromJson(Map<String, dynamic> json) => _$LandmarkModelFromJson(json);
|
||||
|
||||
Landmark toEntity() => Landmark(
|
||||
uuid: uuid,
|
||||
name: name,
|
||||
location: location,
|
||||
type: LandmarkType(type: LandmarkTypeEnum.values.firstWhere((e) => e.value == type)),
|
||||
isSecondary: isSecondary,
|
||||
// TODO - try to set tags
|
||||
description: LandmarkDescription(description: description, tags: [])
|
||||
);
|
||||
}
|
||||
443
frontend/lib/data/models/landmark_model.freezed.dart
Normal file
443
frontend/lib/data/models/landmark_model.freezed.dart
Normal file
@@ -0,0 +1,443 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'landmark_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
/// @nodoc
|
||||
mixin _$LandmarkModel {
|
||||
String get uuid;
|
||||
String get name;
|
||||
List<double> get location;
|
||||
String get type;
|
||||
bool get isSecondary;
|
||||
String get description;
|
||||
|
||||
/// Create a copy of LandmarkModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$LandmarkModelCopyWith<LandmarkModel> get copyWith =>
|
||||
_$LandmarkModelCopyWithImpl<LandmarkModel>(
|
||||
this as LandmarkModel, _$identity);
|
||||
|
||||
/// Serializes this LandmarkModel to a JSON map.
|
||||
Map<String, dynamic> toJson();
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is LandmarkModel &&
|
||||
(identical(other.uuid, uuid) || other.uuid == uuid) &&
|
||||
(identical(other.name, name) || other.name == name) &&
|
||||
const DeepCollectionEquality().equals(other.location, location) &&
|
||||
(identical(other.type, type) || other.type == type) &&
|
||||
(identical(other.isSecondary, isSecondary) ||
|
||||
other.isSecondary == isSecondary) &&
|
||||
(identical(other.description, description) ||
|
||||
other.description == description));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
uuid,
|
||||
name,
|
||||
const DeepCollectionEquality().hash(location),
|
||||
type,
|
||||
isSecondary,
|
||||
description);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'LandmarkModel(uuid: $uuid, name: $name, location: $location, type: $type, isSecondary: $isSecondary, description: $description)';
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $LandmarkModelCopyWith<$Res> {
|
||||
factory $LandmarkModelCopyWith(
|
||||
LandmarkModel value, $Res Function(LandmarkModel) _then) =
|
||||
_$LandmarkModelCopyWithImpl;
|
||||
@useResult
|
||||
$Res call(
|
||||
{String uuid,
|
||||
String name,
|
||||
List<double> location,
|
||||
String type,
|
||||
bool isSecondary,
|
||||
String description});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$LandmarkModelCopyWithImpl<$Res>
|
||||
implements $LandmarkModelCopyWith<$Res> {
|
||||
_$LandmarkModelCopyWithImpl(this._self, this._then);
|
||||
|
||||
final LandmarkModel _self;
|
||||
final $Res Function(LandmarkModel) _then;
|
||||
|
||||
/// Create a copy of LandmarkModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? uuid = null,
|
||||
Object? name = null,
|
||||
Object? location = null,
|
||||
Object? type = null,
|
||||
Object? isSecondary = null,
|
||||
Object? description = null,
|
||||
}) {
|
||||
return _then(_self.copyWith(
|
||||
uuid: null == uuid
|
||||
? _self.uuid
|
||||
: uuid // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
name: null == name
|
||||
? _self.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
location: null == location
|
||||
? _self.location
|
||||
: location // ignore: cast_nullable_to_non_nullable
|
||||
as List<double>,
|
||||
type: null == type
|
||||
? _self.type
|
||||
: type // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
isSecondary: null == isSecondary
|
||||
? _self.isSecondary
|
||||
: isSecondary // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
description: null == description
|
||||
? _self.description
|
||||
: description // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds pattern-matching-related methods to [LandmarkModel].
|
||||
extension LandmarkModelPatterns on LandmarkModel {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>(
|
||||
TResult Function(_LandmarkModel value)? $default, {
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _LandmarkModel() when $default != null:
|
||||
return $default(_that);
|
||||
case _:
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>(
|
||||
TResult Function(_LandmarkModel value) $default,
|
||||
) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _LandmarkModel():
|
||||
return $default(_that);
|
||||
case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
}
|
||||
}
|
||||
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>(
|
||||
TResult? Function(_LandmarkModel value)? $default,
|
||||
) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _LandmarkModel() when $default != null:
|
||||
return $default(_that);
|
||||
case _:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>(
|
||||
TResult Function(String uuid, String name, List<double> location,
|
||||
String type, bool isSecondary, String description)?
|
||||
$default, {
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _LandmarkModel() when $default != null:
|
||||
return $default(_that.uuid, _that.name, _that.location, _that.type,
|
||||
_that.isSecondary, _that.description);
|
||||
case _:
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>(
|
||||
TResult Function(String uuid, String name, List<double> location,
|
||||
String type, bool isSecondary, String description)
|
||||
$default,
|
||||
) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _LandmarkModel():
|
||||
return $default(_that.uuid, _that.name, _that.location, _that.type,
|
||||
_that.isSecondary, _that.description);
|
||||
case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
}
|
||||
}
|
||||
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>(
|
||||
TResult? Function(String uuid, String name, List<double> location,
|
||||
String type, bool isSecondary, String description)?
|
||||
$default,
|
||||
) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _LandmarkModel() when $default != null:
|
||||
return $default(_that.uuid, _that.name, _that.location, _that.type,
|
||||
_that.isSecondary, _that.description);
|
||||
case _:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _LandmarkModel implements LandmarkModel {
|
||||
const _LandmarkModel(
|
||||
{required this.uuid,
|
||||
required this.name,
|
||||
required final List<double> location,
|
||||
required this.type,
|
||||
required this.isSecondary,
|
||||
required this.description})
|
||||
: _location = location;
|
||||
factory _LandmarkModel.fromJson(Map<String, dynamic> json) =>
|
||||
_$LandmarkModelFromJson(json);
|
||||
|
||||
@override
|
||||
final String uuid;
|
||||
@override
|
||||
final String name;
|
||||
final List<double> _location;
|
||||
@override
|
||||
List<double> get location {
|
||||
if (_location is EqualUnmodifiableListView) return _location;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_location);
|
||||
}
|
||||
|
||||
@override
|
||||
final String type;
|
||||
@override
|
||||
final bool isSecondary;
|
||||
@override
|
||||
final String description;
|
||||
|
||||
/// Create a copy of LandmarkModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$LandmarkModelCopyWith<_LandmarkModel> get copyWith =>
|
||||
__$LandmarkModelCopyWithImpl<_LandmarkModel>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$LandmarkModelToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _LandmarkModel &&
|
||||
(identical(other.uuid, uuid) || other.uuid == uuid) &&
|
||||
(identical(other.name, name) || other.name == name) &&
|
||||
const DeepCollectionEquality().equals(other._location, _location) &&
|
||||
(identical(other.type, type) || other.type == type) &&
|
||||
(identical(other.isSecondary, isSecondary) ||
|
||||
other.isSecondary == isSecondary) &&
|
||||
(identical(other.description, description) ||
|
||||
other.description == description));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
uuid,
|
||||
name,
|
||||
const DeepCollectionEquality().hash(_location),
|
||||
type,
|
||||
isSecondary,
|
||||
description);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'LandmarkModel(uuid: $uuid, name: $name, location: $location, type: $type, isSecondary: $isSecondary, description: $description)';
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$LandmarkModelCopyWith<$Res>
|
||||
implements $LandmarkModelCopyWith<$Res> {
|
||||
factory _$LandmarkModelCopyWith(
|
||||
_LandmarkModel value, $Res Function(_LandmarkModel) _then) =
|
||||
__$LandmarkModelCopyWithImpl;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{String uuid,
|
||||
String name,
|
||||
List<double> location,
|
||||
String type,
|
||||
bool isSecondary,
|
||||
String description});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$LandmarkModelCopyWithImpl<$Res>
|
||||
implements _$LandmarkModelCopyWith<$Res> {
|
||||
__$LandmarkModelCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _LandmarkModel _self;
|
||||
final $Res Function(_LandmarkModel) _then;
|
||||
|
||||
/// Create a copy of LandmarkModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$Res call({
|
||||
Object? uuid = null,
|
||||
Object? name = null,
|
||||
Object? location = null,
|
||||
Object? type = null,
|
||||
Object? isSecondary = null,
|
||||
Object? description = null,
|
||||
}) {
|
||||
return _then(_LandmarkModel(
|
||||
uuid: null == uuid
|
||||
? _self.uuid
|
||||
: uuid // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
name: null == name
|
||||
? _self.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
location: null == location
|
||||
? _self._location
|
||||
: location // ignore: cast_nullable_to_non_nullable
|
||||
as List<double>,
|
||||
type: null == type
|
||||
? _self.type
|
||||
: type // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
isSecondary: null == isSecondary
|
||||
? _self.isSecondary
|
||||
: isSecondary // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
description: null == description
|
||||
? _self.description
|
||||
: description // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// dart format on
|
||||
29
frontend/lib/data/models/landmark_model.g.dart
Normal file
29
frontend/lib/data/models/landmark_model.g.dart
Normal file
@@ -0,0 +1,29 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'landmark_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_LandmarkModel _$LandmarkModelFromJson(Map<String, dynamic> json) =>
|
||||
_LandmarkModel(
|
||||
uuid: json['uuid'] as String,
|
||||
name: json['name'] as String,
|
||||
location: (json['location'] as List<dynamic>)
|
||||
.map((e) => (e as num).toDouble())
|
||||
.toList(),
|
||||
type: json['type'] as String,
|
||||
isSecondary: json['isSecondary'] as bool,
|
||||
description: json['description'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$LandmarkModelToJson(_LandmarkModel instance) =>
|
||||
<String, dynamic>{
|
||||
'uuid': instance.uuid,
|
||||
'name': instance.name,
|
||||
'location': instance.location,
|
||||
'type': instance.type,
|
||||
'isSecondary': instance.isSecondary,
|
||||
'description': instance.description,
|
||||
};
|
||||
119
frontend/lib/data/repositories/backend_trip_repository.dart
Normal file
119
frontend/lib/data/repositories/backend_trip_repository.dart
Normal file
@@ -0,0 +1,119 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:anyway/domain/entities/preferences.dart';
|
||||
import 'package:anyway/domain/entities/trip.dart';
|
||||
import 'package:anyway/domain/repositories/trip_repository.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
// We can request a new trip from our backend API by passing it user preferences (which contain all the necessary data)
|
||||
class BackendTripRepository implements TripRepository {
|
||||
final Dio dio;
|
||||
|
||||
BackendTripRepository({required this.dio});
|
||||
|
||||
|
||||
@override
|
||||
Future<Trip> getTrip({Preferences? preferences, String? tripUUID}) async {
|
||||
Map<String, dynamic> data = {
|
||||
"preferences": preferences!.toJson(),
|
||||
// "start": preferences!.startPoint.location,
|
||||
};
|
||||
|
||||
|
||||
late Response response;
|
||||
try {
|
||||
response = await dio.post(
|
||||
"/trip/new",
|
||||
data: data
|
||||
);
|
||||
} catch (e) {
|
||||
trip.updateUUID("error");
|
||||
|
||||
// Format the error message to be more user friendly
|
||||
String errorDescription;
|
||||
if (e is DioException) {
|
||||
errorDescription = e.message ?? "Unknown error";
|
||||
} else if (e is SocketException) {
|
||||
errorDescription = "No internet connection";
|
||||
} else if (e is TimeoutException) {
|
||||
errorDescription = "Request timed out";
|
||||
} else {
|
||||
errorDescription = "Unknown error";
|
||||
}
|
||||
|
||||
String errorMessage = """
|
||||
We're sorry, the following error was generated:
|
||||
|
||||
${errorDescription.trim()}
|
||||
""".trim();
|
||||
|
||||
trip.updateError(errorMessage);
|
||||
log(e.toString());
|
||||
log(errorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
// handle more specific errors
|
||||
if (response.statusCode != 200) {
|
||||
trip.updateUUID("error");
|
||||
String errorDescription;
|
||||
if (response.data.runtimeType == String) {
|
||||
errorDescription = response.data;
|
||||
} else if (response.data.runtimeType == Map<String, dynamic>) {
|
||||
errorDescription = response.data["detail"] ?? "Unknown error";
|
||||
} else {
|
||||
errorDescription = "Unknown error";
|
||||
}
|
||||
|
||||
String errorMessage = """
|
||||
We're sorry, our servers generated the following error:
|
||||
|
||||
${errorDescription.trim()}
|
||||
Please try again.
|
||||
""".trim();
|
||||
trip.updateError(errorMessage);
|
||||
log(errorMessage);
|
||||
// Actualy no need to throw an exception, we can just log the error and let the user retry
|
||||
// throw Exception(errorDetail);
|
||||
} else {
|
||||
|
||||
// if the response data is not json, throw an error
|
||||
if (response.data is! Map<String, dynamic>) {
|
||||
log("${response.data.runtimeType}");
|
||||
trip.updateUUID("error");
|
||||
String errorMessage = """
|
||||
We're sorry, our servers generated the following error:
|
||||
|
||||
${response.data.trim()}
|
||||
Please try again.
|
||||
""".trim();
|
||||
trip.updateError(errorMessage);
|
||||
log(errorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, dynamic> json = response.data;
|
||||
|
||||
// only fill in the trip "meta" data for now
|
||||
trip.loadFromJson(json);
|
||||
|
||||
// now fill the trip with landmarks
|
||||
// we are going to recreate ALL the landmarks from the information given by the api
|
||||
trip.landmarks.remove(trip.landmarks.first);
|
||||
String? nextUUID = json["first_landmark_uuid"];
|
||||
while (nextUUID != null) {
|
||||
var (landmark, newUUID) = await fetchLandmark(nextUUID);
|
||||
trip.addLandmark(landmark);
|
||||
nextUUID = newUUID;
|
||||
}
|
||||
|
||||
log(response.data.toString());
|
||||
// // Also save the trip for the user's convenience
|
||||
// savedTrips.addTrip(trip);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
24
frontend/lib/domain/README.md
Normal file
24
frontend/lib/domain/README.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Domain layer
|
||||
|
||||
## `entities` - Model definition
|
||||
|
||||
Since we follow the repository structure convention, in this folder we purely define data models, without providing any logic. To reduce boilerplate, we use the `freezed` package. This requires some code generation, which means that all model definitions have the following structure:
|
||||
```dart
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
// required: associates our `main.dart` with the code generated by Freezed
|
||||
part 'main.freezed.dart';
|
||||
// optional: Since our Person class is serializable, we must add this line.
|
||||
// But if Person was not serializable, we could skip it.
|
||||
part 'main.g.dart';
|
||||
```
|
||||
|
||||
This is required boilerplate for all models. Then, we define the model itself using the `@freezed` annotation:
|
||||
```dart
|
||||
@freezed
|
||||
...
|
||||
```
|
||||
|
||||
The `*.frozen.dart` and `*.g.dart` are pure boilerplate and should not be touched.
|
||||
|
||||
Note that the description of the data will losely follow the capabilities of the backend but does not need to reflect it exactly. That is where the `data` part is for: translating api calls into flutter objects.
|
||||
21
frontend/lib/domain/entities/landmark.dart
Normal file
21
frontend/lib/domain/entities/landmark.dart
Normal file
@@ -0,0 +1,21 @@
|
||||
import 'package:anyway/domain/entities/landmark_type.dart';
|
||||
import 'package:anyway/domain/entities/landmark_description.dart';
|
||||
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'landmark.freezed.dart';
|
||||
part 'landmark.g.dart';
|
||||
|
||||
@unfreezed
|
||||
abstract class Landmark with _$Landmark {
|
||||
factory Landmark({
|
||||
required String uuid,
|
||||
required String name,
|
||||
required List<double> location,
|
||||
required LandmarkType type,
|
||||
required bool isSecondary,
|
||||
|
||||
required LandmarkDescription description,
|
||||
}) = _Landmark;
|
||||
|
||||
factory Landmark.fromJson(Map<String, Object?> json) => _$LandmarkFromJson(json);
|
||||
}
|
||||
448
frontend/lib/domain/entities/landmark.freezed.dart
Normal file
448
frontend/lib/domain/entities/landmark.freezed.dart
Normal file
@@ -0,0 +1,448 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'landmark.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
/// @nodoc
|
||||
mixin _$Landmark {
|
||||
String get uuid;
|
||||
set uuid(String value);
|
||||
String get name;
|
||||
set name(String value);
|
||||
List<double> get location;
|
||||
set location(List<double> value);
|
||||
LandmarkType get type;
|
||||
set type(LandmarkType value);
|
||||
bool get isSecondary;
|
||||
set isSecondary(bool value);
|
||||
LandmarkDescription get description;
|
||||
set description(LandmarkDescription value);
|
||||
|
||||
/// Create a copy of Landmark
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$LandmarkCopyWith<Landmark> get copyWith =>
|
||||
_$LandmarkCopyWithImpl<Landmark>(this as Landmark, _$identity);
|
||||
|
||||
/// Serializes this Landmark to a JSON map.
|
||||
Map<String, dynamic> toJson();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Landmark(uuid: $uuid, name: $name, location: $location, type: $type, isSecondary: $isSecondary, description: $description)';
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $LandmarkCopyWith<$Res> {
|
||||
factory $LandmarkCopyWith(Landmark value, $Res Function(Landmark) _then) =
|
||||
_$LandmarkCopyWithImpl;
|
||||
@useResult
|
||||
$Res call(
|
||||
{String uuid,
|
||||
String name,
|
||||
List<double> location,
|
||||
LandmarkType type,
|
||||
bool isSecondary,
|
||||
LandmarkDescription description});
|
||||
|
||||
$LandmarkTypeCopyWith<$Res> get type;
|
||||
$LandmarkDescriptionCopyWith<$Res> get description;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$LandmarkCopyWithImpl<$Res> implements $LandmarkCopyWith<$Res> {
|
||||
_$LandmarkCopyWithImpl(this._self, this._then);
|
||||
|
||||
final Landmark _self;
|
||||
final $Res Function(Landmark) _then;
|
||||
|
||||
/// Create a copy of Landmark
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? uuid = null,
|
||||
Object? name = null,
|
||||
Object? location = null,
|
||||
Object? type = null,
|
||||
Object? isSecondary = null,
|
||||
Object? description = null,
|
||||
}) {
|
||||
return _then(_self.copyWith(
|
||||
uuid: null == uuid
|
||||
? _self.uuid
|
||||
: uuid // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
name: null == name
|
||||
? _self.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
location: null == location
|
||||
? _self.location
|
||||
: location // ignore: cast_nullable_to_non_nullable
|
||||
as List<double>,
|
||||
type: null == type
|
||||
? _self.type
|
||||
: type // ignore: cast_nullable_to_non_nullable
|
||||
as LandmarkType,
|
||||
isSecondary: null == isSecondary
|
||||
? _self.isSecondary
|
||||
: isSecondary // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
description: null == description
|
||||
? _self.description
|
||||
: description // ignore: cast_nullable_to_non_nullable
|
||||
as LandmarkDescription,
|
||||
));
|
||||
}
|
||||
|
||||
/// Create a copy of Landmark
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$LandmarkTypeCopyWith<$Res> get type {
|
||||
return $LandmarkTypeCopyWith<$Res>(_self.type, (value) {
|
||||
return _then(_self.copyWith(type: value));
|
||||
});
|
||||
}
|
||||
|
||||
/// Create a copy of Landmark
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$LandmarkDescriptionCopyWith<$Res> get description {
|
||||
return $LandmarkDescriptionCopyWith<$Res>(_self.description, (value) {
|
||||
return _then(_self.copyWith(description: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds pattern-matching-related methods to [Landmark].
|
||||
extension LandmarkPatterns on Landmark {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>(
|
||||
TResult Function(_Landmark value)? $default, {
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _Landmark() when $default != null:
|
||||
return $default(_that);
|
||||
case _:
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>(
|
||||
TResult Function(_Landmark value) $default,
|
||||
) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _Landmark():
|
||||
return $default(_that);
|
||||
case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
}
|
||||
}
|
||||
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>(
|
||||
TResult? Function(_Landmark value)? $default,
|
||||
) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _Landmark() when $default != null:
|
||||
return $default(_that);
|
||||
case _:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>(
|
||||
TResult Function(
|
||||
String uuid,
|
||||
String name,
|
||||
List<double> location,
|
||||
LandmarkType type,
|
||||
bool isSecondary,
|
||||
LandmarkDescription description)?
|
||||
$default, {
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _Landmark() when $default != null:
|
||||
return $default(_that.uuid, _that.name, _that.location, _that.type,
|
||||
_that.isSecondary, _that.description);
|
||||
case _:
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>(
|
||||
TResult Function(
|
||||
String uuid,
|
||||
String name,
|
||||
List<double> location,
|
||||
LandmarkType type,
|
||||
bool isSecondary,
|
||||
LandmarkDescription description)
|
||||
$default,
|
||||
) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _Landmark():
|
||||
return $default(_that.uuid, _that.name, _that.location, _that.type,
|
||||
_that.isSecondary, _that.description);
|
||||
case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
}
|
||||
}
|
||||
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>(
|
||||
TResult? Function(
|
||||
String uuid,
|
||||
String name,
|
||||
List<double> location,
|
||||
LandmarkType type,
|
||||
bool isSecondary,
|
||||
LandmarkDescription description)?
|
||||
$default,
|
||||
) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _Landmark() when $default != null:
|
||||
return $default(_that.uuid, _that.name, _that.location, _that.type,
|
||||
_that.isSecondary, _that.description);
|
||||
case _:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _Landmark implements Landmark {
|
||||
_Landmark(
|
||||
{required this.uuid,
|
||||
required this.name,
|
||||
required this.location,
|
||||
required this.type,
|
||||
required this.isSecondary,
|
||||
required this.description});
|
||||
factory _Landmark.fromJson(Map<String, dynamic> json) =>
|
||||
_$LandmarkFromJson(json);
|
||||
|
||||
@override
|
||||
String uuid;
|
||||
@override
|
||||
String name;
|
||||
@override
|
||||
List<double> location;
|
||||
@override
|
||||
LandmarkType type;
|
||||
@override
|
||||
bool isSecondary;
|
||||
@override
|
||||
LandmarkDescription description;
|
||||
|
||||
/// Create a copy of Landmark
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$LandmarkCopyWith<_Landmark> get copyWith =>
|
||||
__$LandmarkCopyWithImpl<_Landmark>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$LandmarkToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Landmark(uuid: $uuid, name: $name, location: $location, type: $type, isSecondary: $isSecondary, description: $description)';
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$LandmarkCopyWith<$Res>
|
||||
implements $LandmarkCopyWith<$Res> {
|
||||
factory _$LandmarkCopyWith(_Landmark value, $Res Function(_Landmark) _then) =
|
||||
__$LandmarkCopyWithImpl;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{String uuid,
|
||||
String name,
|
||||
List<double> location,
|
||||
LandmarkType type,
|
||||
bool isSecondary,
|
||||
LandmarkDescription description});
|
||||
|
||||
@override
|
||||
$LandmarkTypeCopyWith<$Res> get type;
|
||||
@override
|
||||
$LandmarkDescriptionCopyWith<$Res> get description;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$LandmarkCopyWithImpl<$Res> implements _$LandmarkCopyWith<$Res> {
|
||||
__$LandmarkCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _Landmark _self;
|
||||
final $Res Function(_Landmark) _then;
|
||||
|
||||
/// Create a copy of Landmark
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$Res call({
|
||||
Object? uuid = null,
|
||||
Object? name = null,
|
||||
Object? location = null,
|
||||
Object? type = null,
|
||||
Object? isSecondary = null,
|
||||
Object? description = null,
|
||||
}) {
|
||||
return _then(_Landmark(
|
||||
uuid: null == uuid
|
||||
? _self.uuid
|
||||
: uuid // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
name: null == name
|
||||
? _self.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
location: null == location
|
||||
? _self.location
|
||||
: location // ignore: cast_nullable_to_non_nullable
|
||||
as List<double>,
|
||||
type: null == type
|
||||
? _self.type
|
||||
: type // ignore: cast_nullable_to_non_nullable
|
||||
as LandmarkType,
|
||||
isSecondary: null == isSecondary
|
||||
? _self.isSecondary
|
||||
: isSecondary // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
description: null == description
|
||||
? _self.description
|
||||
: description // ignore: cast_nullable_to_non_nullable
|
||||
as LandmarkDescription,
|
||||
));
|
||||
}
|
||||
|
||||
/// Create a copy of Landmark
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$LandmarkTypeCopyWith<$Res> get type {
|
||||
return $LandmarkTypeCopyWith<$Res>(_self.type, (value) {
|
||||
return _then(_self.copyWith(type: value));
|
||||
});
|
||||
}
|
||||
|
||||
/// Create a copy of Landmark
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$LandmarkDescriptionCopyWith<$Res> get description {
|
||||
return $LandmarkDescriptionCopyWith<$Res>(_self.description, (value) {
|
||||
return _then(_self.copyWith(description: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// dart format on
|
||||
28
frontend/lib/domain/entities/landmark.g.dart
Normal file
28
frontend/lib/domain/entities/landmark.g.dart
Normal file
@@ -0,0 +1,28 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'landmark.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_Landmark _$LandmarkFromJson(Map<String, dynamic> json) => _Landmark(
|
||||
uuid: json['uuid'] as String,
|
||||
name: json['name'] as String,
|
||||
location: (json['location'] as List<dynamic>)
|
||||
.map((e) => (e as num).toDouble())
|
||||
.toList(),
|
||||
type: LandmarkType.fromJson(json['type'] as Map<String, dynamic>),
|
||||
isSecondary: json['isSecondary'] as bool,
|
||||
description: LandmarkDescription.fromJson(
|
||||
json['description'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$LandmarkToJson(_Landmark instance) => <String, dynamic>{
|
||||
'uuid': instance.uuid,
|
||||
'name': instance.name,
|
||||
'location': instance.location,
|
||||
'type': instance.type,
|
||||
'isSecondary': instance.isSecondary,
|
||||
'description': instance.description,
|
||||
};
|
||||
15
frontend/lib/domain/entities/landmark_description.dart
Normal file
15
frontend/lib/domain/entities/landmark_description.dart
Normal file
@@ -0,0 +1,15 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'landmark_description.freezed.dart';
|
||||
part 'landmark_description.g.dart';
|
||||
|
||||
|
||||
@Freezed(makeCollectionsUnmodifiable: false)
|
||||
abstract class LandmarkDescription with _$LandmarkDescription {
|
||||
const factory LandmarkDescription({
|
||||
required String description,
|
||||
required List<String> tags,
|
||||
}) = _LandmarkDescription;
|
||||
|
||||
factory LandmarkDescription.fromJson(Map<String, Object?> json) => _$LandmarkDescriptionFromJson(json);
|
||||
}
|
||||
|
||||
336
frontend/lib/domain/entities/landmark_description.freezed.dart
Normal file
336
frontend/lib/domain/entities/landmark_description.freezed.dart
Normal file
@@ -0,0 +1,336 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'landmark_description.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
/// @nodoc
|
||||
mixin _$LandmarkDescription {
|
||||
String get description;
|
||||
List<String> get tags;
|
||||
|
||||
/// Create a copy of LandmarkDescription
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$LandmarkDescriptionCopyWith<LandmarkDescription> get copyWith =>
|
||||
_$LandmarkDescriptionCopyWithImpl<LandmarkDescription>(
|
||||
this as LandmarkDescription, _$identity);
|
||||
|
||||
/// Serializes this LandmarkDescription to a JSON map.
|
||||
Map<String, dynamic> toJson();
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is LandmarkDescription &&
|
||||
(identical(other.description, description) ||
|
||||
other.description == description) &&
|
||||
const DeepCollectionEquality().equals(other.tags, tags));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType, description, const DeepCollectionEquality().hash(tags));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'LandmarkDescription(description: $description, tags: $tags)';
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $LandmarkDescriptionCopyWith<$Res> {
|
||||
factory $LandmarkDescriptionCopyWith(
|
||||
LandmarkDescription value, $Res Function(LandmarkDescription) _then) =
|
||||
_$LandmarkDescriptionCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({String description, List<String> tags});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$LandmarkDescriptionCopyWithImpl<$Res>
|
||||
implements $LandmarkDescriptionCopyWith<$Res> {
|
||||
_$LandmarkDescriptionCopyWithImpl(this._self, this._then);
|
||||
|
||||
final LandmarkDescription _self;
|
||||
final $Res Function(LandmarkDescription) _then;
|
||||
|
||||
/// Create a copy of LandmarkDescription
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? description = null,
|
||||
Object? tags = null,
|
||||
}) {
|
||||
return _then(_self.copyWith(
|
||||
description: null == description
|
||||
? _self.description
|
||||
: description // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
tags: null == tags
|
||||
? _self.tags
|
||||
: tags // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds pattern-matching-related methods to [LandmarkDescription].
|
||||
extension LandmarkDescriptionPatterns on LandmarkDescription {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>(
|
||||
TResult Function(_LandmarkDescription value)? $default, {
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _LandmarkDescription() when $default != null:
|
||||
return $default(_that);
|
||||
case _:
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>(
|
||||
TResult Function(_LandmarkDescription value) $default,
|
||||
) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _LandmarkDescription():
|
||||
return $default(_that);
|
||||
case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
}
|
||||
}
|
||||
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>(
|
||||
TResult? Function(_LandmarkDescription value)? $default,
|
||||
) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _LandmarkDescription() when $default != null:
|
||||
return $default(_that);
|
||||
case _:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>(
|
||||
TResult Function(String description, List<String> tags)? $default, {
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _LandmarkDescription() when $default != null:
|
||||
return $default(_that.description, _that.tags);
|
||||
case _:
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>(
|
||||
TResult Function(String description, List<String> tags) $default,
|
||||
) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _LandmarkDescription():
|
||||
return $default(_that.description, _that.tags);
|
||||
case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
}
|
||||
}
|
||||
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>(
|
||||
TResult? Function(String description, List<String> tags)? $default,
|
||||
) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _LandmarkDescription() when $default != null:
|
||||
return $default(_that.description, _that.tags);
|
||||
case _:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _LandmarkDescription implements LandmarkDescription {
|
||||
const _LandmarkDescription({required this.description, required this.tags});
|
||||
factory _LandmarkDescription.fromJson(Map<String, dynamic> json) =>
|
||||
_$LandmarkDescriptionFromJson(json);
|
||||
|
||||
@override
|
||||
final String description;
|
||||
@override
|
||||
final List<String> tags;
|
||||
|
||||
/// Create a copy of LandmarkDescription
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$LandmarkDescriptionCopyWith<_LandmarkDescription> get copyWith =>
|
||||
__$LandmarkDescriptionCopyWithImpl<_LandmarkDescription>(
|
||||
this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$LandmarkDescriptionToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _LandmarkDescription &&
|
||||
(identical(other.description, description) ||
|
||||
other.description == description) &&
|
||||
const DeepCollectionEquality().equals(other.tags, tags));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType, description, const DeepCollectionEquality().hash(tags));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'LandmarkDescription(description: $description, tags: $tags)';
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$LandmarkDescriptionCopyWith<$Res>
|
||||
implements $LandmarkDescriptionCopyWith<$Res> {
|
||||
factory _$LandmarkDescriptionCopyWith(_LandmarkDescription value,
|
||||
$Res Function(_LandmarkDescription) _then) =
|
||||
__$LandmarkDescriptionCopyWithImpl;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({String description, List<String> tags});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$LandmarkDescriptionCopyWithImpl<$Res>
|
||||
implements _$LandmarkDescriptionCopyWith<$Res> {
|
||||
__$LandmarkDescriptionCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _LandmarkDescription _self;
|
||||
final $Res Function(_LandmarkDescription) _then;
|
||||
|
||||
/// Create a copy of LandmarkDescription
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$Res call({
|
||||
Object? description = null,
|
||||
Object? tags = null,
|
||||
}) {
|
||||
return _then(_LandmarkDescription(
|
||||
description: null == description
|
||||
? _self.description
|
||||
: description // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
tags: null == tags
|
||||
? _self.tags
|
||||
: tags // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// dart format on
|
||||
20
frontend/lib/domain/entities/landmark_description.g.dart
Normal file
20
frontend/lib/domain/entities/landmark_description.g.dart
Normal file
@@ -0,0 +1,20 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'landmark_description.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_LandmarkDescription _$LandmarkDescriptionFromJson(Map<String, dynamic> json) =>
|
||||
_LandmarkDescription(
|
||||
description: json['description'] as String,
|
||||
tags: (json['tags'] as List<dynamic>).map((e) => e as String).toList(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$LandmarkDescriptionToJson(
|
||||
_LandmarkDescription instance) =>
|
||||
<String, dynamic>{
|
||||
'description': instance.description,
|
||||
'tags': instance.tags,
|
||||
};
|
||||
27
frontend/lib/domain/entities/landmark_type.dart
Normal file
27
frontend/lib/domain/entities/landmark_type.dart
Normal file
@@ -0,0 +1,27 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'landmark_type.freezed.dart';
|
||||
part 'landmark_type.g.dart';
|
||||
|
||||
@freezed
|
||||
abstract class LandmarkType with _$LandmarkType {
|
||||
const factory LandmarkType({
|
||||
required LandmarkTypeEnum type,
|
||||
}) = _LandmarkType;
|
||||
|
||||
factory LandmarkType.fromJson(Map<String, Object?> json) => _$LandmarkTypeFromJson(json);
|
||||
}
|
||||
|
||||
@JsonEnum(alwaysCreate: true)
|
||||
enum LandmarkTypeEnum {
|
||||
@JsonValue('culture')
|
||||
culture,
|
||||
@JsonValue('nature')
|
||||
nature,
|
||||
@JsonValue('shopping')
|
||||
shopping,
|
||||
}
|
||||
|
||||
extension LandmarkTypeEnumExtension on LandmarkTypeEnum {
|
||||
String get value => _$LandmarkTypeEnumEnumMap[this]!;
|
||||
}
|
||||
315
frontend/lib/domain/entities/landmark_type.freezed.dart
Normal file
315
frontend/lib/domain/entities/landmark_type.freezed.dart
Normal file
@@ -0,0 +1,315 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'landmark_type.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
/// @nodoc
|
||||
mixin _$LandmarkType {
|
||||
LandmarkTypeEnum get type;
|
||||
|
||||
/// Create a copy of LandmarkType
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$LandmarkTypeCopyWith<LandmarkType> get copyWith =>
|
||||
_$LandmarkTypeCopyWithImpl<LandmarkType>(
|
||||
this as LandmarkType, _$identity);
|
||||
|
||||
/// Serializes this LandmarkType to a JSON map.
|
||||
Map<String, dynamic> toJson();
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is LandmarkType &&
|
||||
(identical(other.type, type) || other.type == type));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, type);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'LandmarkType(type: $type)';
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $LandmarkTypeCopyWith<$Res> {
|
||||
factory $LandmarkTypeCopyWith(
|
||||
LandmarkType value, $Res Function(LandmarkType) _then) =
|
||||
_$LandmarkTypeCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({LandmarkTypeEnum type});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$LandmarkTypeCopyWithImpl<$Res> implements $LandmarkTypeCopyWith<$Res> {
|
||||
_$LandmarkTypeCopyWithImpl(this._self, this._then);
|
||||
|
||||
final LandmarkType _self;
|
||||
final $Res Function(LandmarkType) _then;
|
||||
|
||||
/// Create a copy of LandmarkType
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? type = null,
|
||||
}) {
|
||||
return _then(_self.copyWith(
|
||||
type: null == type
|
||||
? _self.type
|
||||
: type // ignore: cast_nullable_to_non_nullable
|
||||
as LandmarkTypeEnum,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds pattern-matching-related methods to [LandmarkType].
|
||||
extension LandmarkTypePatterns on LandmarkType {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>(
|
||||
TResult Function(_LandmarkType value)? $default, {
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _LandmarkType() when $default != null:
|
||||
return $default(_that);
|
||||
case _:
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>(
|
||||
TResult Function(_LandmarkType value) $default,
|
||||
) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _LandmarkType():
|
||||
return $default(_that);
|
||||
case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
}
|
||||
}
|
||||
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>(
|
||||
TResult? Function(_LandmarkType value)? $default,
|
||||
) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _LandmarkType() when $default != null:
|
||||
return $default(_that);
|
||||
case _:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>(
|
||||
TResult Function(LandmarkTypeEnum type)? $default, {
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _LandmarkType() when $default != null:
|
||||
return $default(_that.type);
|
||||
case _:
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>(
|
||||
TResult Function(LandmarkTypeEnum type) $default,
|
||||
) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _LandmarkType():
|
||||
return $default(_that.type);
|
||||
case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
}
|
||||
}
|
||||
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>(
|
||||
TResult? Function(LandmarkTypeEnum type)? $default,
|
||||
) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _LandmarkType() when $default != null:
|
||||
return $default(_that.type);
|
||||
case _:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _LandmarkType implements LandmarkType {
|
||||
const _LandmarkType({required this.type});
|
||||
factory _LandmarkType.fromJson(Map<String, dynamic> json) =>
|
||||
_$LandmarkTypeFromJson(json);
|
||||
|
||||
@override
|
||||
final LandmarkTypeEnum type;
|
||||
|
||||
/// Create a copy of LandmarkType
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$LandmarkTypeCopyWith<_LandmarkType> get copyWith =>
|
||||
__$LandmarkTypeCopyWithImpl<_LandmarkType>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$LandmarkTypeToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _LandmarkType &&
|
||||
(identical(other.type, type) || other.type == type));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, type);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'LandmarkType(type: $type)';
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$LandmarkTypeCopyWith<$Res>
|
||||
implements $LandmarkTypeCopyWith<$Res> {
|
||||
factory _$LandmarkTypeCopyWith(
|
||||
_LandmarkType value, $Res Function(_LandmarkType) _then) =
|
||||
__$LandmarkTypeCopyWithImpl;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({LandmarkTypeEnum type});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$LandmarkTypeCopyWithImpl<$Res>
|
||||
implements _$LandmarkTypeCopyWith<$Res> {
|
||||
__$LandmarkTypeCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _LandmarkType _self;
|
||||
final $Res Function(_LandmarkType) _then;
|
||||
|
||||
/// Create a copy of LandmarkType
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$Res call({
|
||||
Object? type = null,
|
||||
}) {
|
||||
return _then(_LandmarkType(
|
||||
type: null == type
|
||||
? _self.type
|
||||
: type // ignore: cast_nullable_to_non_nullable
|
||||
as LandmarkTypeEnum,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// dart format on
|
||||
23
frontend/lib/domain/entities/landmark_type.g.dart
Normal file
23
frontend/lib/domain/entities/landmark_type.g.dart
Normal file
@@ -0,0 +1,23 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'landmark_type.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_LandmarkType _$LandmarkTypeFromJson(Map<String, dynamic> json) =>
|
||||
_LandmarkType(
|
||||
type: $enumDecode(_$LandmarkTypeEnumEnumMap, json['type']),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$LandmarkTypeToJson(_LandmarkType instance) =>
|
||||
<String, dynamic>{
|
||||
'type': _$LandmarkTypeEnumEnumMap[instance.type]!,
|
||||
};
|
||||
|
||||
const _$LandmarkTypeEnumEnumMap = {
|
||||
LandmarkTypeEnum.culture: 'culture',
|
||||
LandmarkTypeEnum.nature: 'nature',
|
||||
LandmarkTypeEnum.shopping: 'shopping',
|
||||
};
|
||||
12
frontend/lib/domain/entities/preferences.dart
Normal file
12
frontend/lib/domain/entities/preferences.dart
Normal file
@@ -0,0 +1,12 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'preferences.freezed.dart';
|
||||
part 'preferences.g.dart';
|
||||
|
||||
@freezed
|
||||
abstract class Preferences with _$Preferences {
|
||||
const factory Preferences({
|
||||
required String test,
|
||||
}) = _Preferences;
|
||||
|
||||
factory Preferences.fromJson(Map<String, Object?> json) => _$PreferencesFromJson(json);
|
||||
}
|
||||
313
frontend/lib/domain/entities/preferences.freezed.dart
Normal file
313
frontend/lib/domain/entities/preferences.freezed.dart
Normal file
@@ -0,0 +1,313 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'preferences.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
/// @nodoc
|
||||
mixin _$Preferences {
|
||||
String get test;
|
||||
|
||||
/// Create a copy of Preferences
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$PreferencesCopyWith<Preferences> get copyWith =>
|
||||
_$PreferencesCopyWithImpl<Preferences>(this as Preferences, _$identity);
|
||||
|
||||
/// Serializes this Preferences to a JSON map.
|
||||
Map<String, dynamic> toJson();
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is Preferences &&
|
||||
(identical(other.test, test) || other.test == test));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, test);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Preferences(test: $test)';
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $PreferencesCopyWith<$Res> {
|
||||
factory $PreferencesCopyWith(
|
||||
Preferences value, $Res Function(Preferences) _then) =
|
||||
_$PreferencesCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({String test});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$PreferencesCopyWithImpl<$Res> implements $PreferencesCopyWith<$Res> {
|
||||
_$PreferencesCopyWithImpl(this._self, this._then);
|
||||
|
||||
final Preferences _self;
|
||||
final $Res Function(Preferences) _then;
|
||||
|
||||
/// Create a copy of Preferences
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? test = null,
|
||||
}) {
|
||||
return _then(_self.copyWith(
|
||||
test: null == test
|
||||
? _self.test
|
||||
: test // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds pattern-matching-related methods to [Preferences].
|
||||
extension PreferencesPatterns on Preferences {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>(
|
||||
TResult Function(_Preferences value)? $default, {
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _Preferences() when $default != null:
|
||||
return $default(_that);
|
||||
case _:
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>(
|
||||
TResult Function(_Preferences value) $default,
|
||||
) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _Preferences():
|
||||
return $default(_that);
|
||||
case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
}
|
||||
}
|
||||
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>(
|
||||
TResult? Function(_Preferences value)? $default,
|
||||
) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _Preferences() when $default != null:
|
||||
return $default(_that);
|
||||
case _:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>(
|
||||
TResult Function(String test)? $default, {
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _Preferences() when $default != null:
|
||||
return $default(_that.test);
|
||||
case _:
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>(
|
||||
TResult Function(String test) $default,
|
||||
) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _Preferences():
|
||||
return $default(_that.test);
|
||||
case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
}
|
||||
}
|
||||
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>(
|
||||
TResult? Function(String test)? $default,
|
||||
) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _Preferences() when $default != null:
|
||||
return $default(_that.test);
|
||||
case _:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _Preferences implements Preferences {
|
||||
const _Preferences({required this.test});
|
||||
factory _Preferences.fromJson(Map<String, dynamic> json) =>
|
||||
_$PreferencesFromJson(json);
|
||||
|
||||
@override
|
||||
final String test;
|
||||
|
||||
/// Create a copy of Preferences
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$PreferencesCopyWith<_Preferences> get copyWith =>
|
||||
__$PreferencesCopyWithImpl<_Preferences>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$PreferencesToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _Preferences &&
|
||||
(identical(other.test, test) || other.test == test));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, test);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Preferences(test: $test)';
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$PreferencesCopyWith<$Res>
|
||||
implements $PreferencesCopyWith<$Res> {
|
||||
factory _$PreferencesCopyWith(
|
||||
_Preferences value, $Res Function(_Preferences) _then) =
|
||||
__$PreferencesCopyWithImpl;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({String test});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$PreferencesCopyWithImpl<$Res> implements _$PreferencesCopyWith<$Res> {
|
||||
__$PreferencesCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _Preferences _self;
|
||||
final $Res Function(_Preferences) _then;
|
||||
|
||||
/// Create a copy of Preferences
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$Res call({
|
||||
Object? test = null,
|
||||
}) {
|
||||
return _then(_Preferences(
|
||||
test: null == test
|
||||
? _self.test
|
||||
: test // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// dart format on
|
||||
16
frontend/lib/domain/entities/preferences.g.dart
Normal file
16
frontend/lib/domain/entities/preferences.g.dart
Normal file
@@ -0,0 +1,16 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'preferences.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_Preferences _$PreferencesFromJson(Map<String, dynamic> json) => _Preferences(
|
||||
test: json['test'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$PreferencesToJson(_Preferences instance) =>
|
||||
<String, dynamic>{
|
||||
'test': instance.test,
|
||||
};
|
||||
19
frontend/lib/domain/entities/trip.dart
Normal file
19
frontend/lib/domain/entities/trip.dart
Normal file
@@ -0,0 +1,19 @@
|
||||
import 'package:anyway/domain/entities/landmark.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'trip.freezed.dart';
|
||||
part 'trip.g.dart';
|
||||
|
||||
|
||||
@Freezed(makeCollectionsUnmodifiable: false)
|
||||
abstract class Trip with _$Trip {
|
||||
|
||||
const factory Trip({
|
||||
required String uuid,
|
||||
// Duration totalTime,
|
||||
required List<Landmark> landmarks,
|
||||
}) = _Trip;
|
||||
|
||||
factory Trip.fromJson(Map<String, Object?> json) => _$TripFromJson(json);
|
||||
|
||||
}
|
||||
327
frontend/lib/domain/entities/trip.freezed.dart
Normal file
327
frontend/lib/domain/entities/trip.freezed.dart
Normal file
@@ -0,0 +1,327 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'trip.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
/// @nodoc
|
||||
mixin _$Trip {
|
||||
String get uuid; // Duration totalTime,
|
||||
List<Landmark> get landmarks;
|
||||
|
||||
/// Create a copy of Trip
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$TripCopyWith<Trip> get copyWith =>
|
||||
_$TripCopyWithImpl<Trip>(this as Trip, _$identity);
|
||||
|
||||
/// Serializes this Trip to a JSON map.
|
||||
Map<String, dynamic> toJson();
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is Trip &&
|
||||
(identical(other.uuid, uuid) || other.uuid == uuid) &&
|
||||
const DeepCollectionEquality().equals(other.landmarks, landmarks));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType, uuid, const DeepCollectionEquality().hash(landmarks));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Trip(uuid: $uuid, landmarks: $landmarks)';
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $TripCopyWith<$Res> {
|
||||
factory $TripCopyWith(Trip value, $Res Function(Trip) _then) =
|
||||
_$TripCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({String uuid, List<Landmark> landmarks});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$TripCopyWithImpl<$Res> implements $TripCopyWith<$Res> {
|
||||
_$TripCopyWithImpl(this._self, this._then);
|
||||
|
||||
final Trip _self;
|
||||
final $Res Function(Trip) _then;
|
||||
|
||||
/// Create a copy of Trip
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? uuid = null,
|
||||
Object? landmarks = null,
|
||||
}) {
|
||||
return _then(_self.copyWith(
|
||||
uuid: null == uuid
|
||||
? _self.uuid
|
||||
: uuid // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
landmarks: null == landmarks
|
||||
? _self.landmarks
|
||||
: landmarks // ignore: cast_nullable_to_non_nullable
|
||||
as List<Landmark>,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds pattern-matching-related methods to [Trip].
|
||||
extension TripPatterns on Trip {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>(
|
||||
TResult Function(_Trip value)? $default, {
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _Trip() when $default != null:
|
||||
return $default(_that);
|
||||
case _:
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>(
|
||||
TResult Function(_Trip value) $default,
|
||||
) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _Trip():
|
||||
return $default(_that);
|
||||
case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
}
|
||||
}
|
||||
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>(
|
||||
TResult? Function(_Trip value)? $default,
|
||||
) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _Trip() when $default != null:
|
||||
return $default(_that);
|
||||
case _:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>(
|
||||
TResult Function(String uuid, List<Landmark> landmarks)? $default, {
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _Trip() when $default != null:
|
||||
return $default(_that.uuid, _that.landmarks);
|
||||
case _:
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>(
|
||||
TResult Function(String uuid, List<Landmark> landmarks) $default,
|
||||
) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _Trip():
|
||||
return $default(_that.uuid, _that.landmarks);
|
||||
case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
}
|
||||
}
|
||||
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>(
|
||||
TResult? Function(String uuid, List<Landmark> landmarks)? $default,
|
||||
) {
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _Trip() when $default != null:
|
||||
return $default(_that.uuid, _that.landmarks);
|
||||
case _:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _Trip implements Trip {
|
||||
const _Trip({required this.uuid, required this.landmarks});
|
||||
factory _Trip.fromJson(Map<String, dynamic> json) => _$TripFromJson(json);
|
||||
|
||||
@override
|
||||
final String uuid;
|
||||
// Duration totalTime,
|
||||
@override
|
||||
final List<Landmark> landmarks;
|
||||
|
||||
/// Create a copy of Trip
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$TripCopyWith<_Trip> get copyWith =>
|
||||
__$TripCopyWithImpl<_Trip>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$TripToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _Trip &&
|
||||
(identical(other.uuid, uuid) || other.uuid == uuid) &&
|
||||
const DeepCollectionEquality().equals(other.landmarks, landmarks));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType, uuid, const DeepCollectionEquality().hash(landmarks));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Trip(uuid: $uuid, landmarks: $landmarks)';
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$TripCopyWith<$Res> implements $TripCopyWith<$Res> {
|
||||
factory _$TripCopyWith(_Trip value, $Res Function(_Trip) _then) =
|
||||
__$TripCopyWithImpl;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({String uuid, List<Landmark> landmarks});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$TripCopyWithImpl<$Res> implements _$TripCopyWith<$Res> {
|
||||
__$TripCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _Trip _self;
|
||||
final $Res Function(_Trip) _then;
|
||||
|
||||
/// Create a copy of Trip
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$Res call({
|
||||
Object? uuid = null,
|
||||
Object? landmarks = null,
|
||||
}) {
|
||||
return _then(_Trip(
|
||||
uuid: null == uuid
|
||||
? _self.uuid
|
||||
: uuid // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
landmarks: null == landmarks
|
||||
? _self.landmarks
|
||||
: landmarks // ignore: cast_nullable_to_non_nullable
|
||||
as List<Landmark>,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// dart format on
|
||||
19
frontend/lib/domain/entities/trip.g.dart
Normal file
19
frontend/lib/domain/entities/trip.g.dart
Normal file
@@ -0,0 +1,19 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'trip.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_Trip _$TripFromJson(Map<String, dynamic> json) => _Trip(
|
||||
uuid: json['uuid'] as String,
|
||||
landmarks: (json['landmarks'] as List<dynamic>)
|
||||
.map((e) => Landmark.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$TripToJson(_Trip instance) => <String, dynamic>{
|
||||
'uuid': instance.uuid,
|
||||
'landmarks': instance.landmarks,
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
import 'package:anyway/domain/entities/preferences.dart';
|
||||
|
||||
abstract class PreferencesRepository {
|
||||
Future<Preferences> getPreferences();
|
||||
}
|
||||
6
frontend/lib/domain/repositories/trip_repository.dart
Normal file
6
frontend/lib/domain/repositories/trip_repository.dart
Normal file
@@ -0,0 +1,6 @@
|
||||
import 'package:anyway/domain/entities/preferences.dart';
|
||||
import 'package:anyway/domain/entities/trip.dart';
|
||||
|
||||
abstract class TripRepository {
|
||||
Future<Trip> getTrip({Preferences? preferences, String? tripUUID});
|
||||
}
|
||||
@@ -1,26 +1,39 @@
|
||||
import 'package:anyway/presentation/pages/start.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:anyway/constants.dart';
|
||||
import 'package:anyway/utils/get_first_page.dart';
|
||||
import 'package:anyway/utils/load_trips.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:anyway/core/constants.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
|
||||
void main() => runApp(const App());
|
||||
/// The app entry point.
|
||||
/// Initializes persistence, sets up dependency injection via ProviderScope,
|
||||
/// and determines which screen (login or main app) to show based on auth state.
|
||||
Future<void> main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// Some global variables
|
||||
final GlobalKey<ScaffoldMessengerState> rootScaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
|
||||
final SavedTrips savedTrips = SavedTrips();
|
||||
// the list of saved trips is then populated implicitly by getFirstPage()
|
||||
// initialize local persistence (shared preferences)
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
|
||||
// the app wrapped in ProviderScope
|
||||
runApp(const ProviderScope(child: MyApp()));
|
||||
}
|
||||
|
||||
class MyApp extends ConsumerWidget {
|
||||
const MyApp({super.key});
|
||||
|
||||
class App extends StatelessWidget {
|
||||
const App({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => MaterialApp(
|
||||
title: APP_NAME,
|
||||
home: getFirstPage(),
|
||||
theme: APP_THEME,
|
||||
scaffoldMessengerKey: rootScaffoldMessengerKey
|
||||
);
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
|
||||
return MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
title: APP_NAME,
|
||||
theme: APP_THEME,
|
||||
scaffoldMessengerKey: rootScaffoldMessengerKey,
|
||||
home: const StartPage()
|
||||
|
||||
// TODO - set up routing
|
||||
// onGenerateRoute: AppRouter.onGenerateRoute,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
50
frontend/lib/presentation/pages/create_trip.dart
Normal file
50
frontend/lib/presentation/pages/create_trip.dart
Normal file
@@ -0,0 +1,50 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class NewTripPage extends StatefulWidget {
|
||||
const NewTripPage({super.key});
|
||||
|
||||
@override
|
||||
_NewTripPageState createState() => _NewTripPageState();
|
||||
}
|
||||
|
||||
|
||||
class _NewTripPageState extends State<NewTripPage> {
|
||||
int _currentStep = 0;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Create New Trip'),
|
||||
),
|
||||
body: Stepper(
|
||||
currentStep: _currentStep,
|
||||
onStepContinue: () {
|
||||
if (_currentStep < 1) {
|
||||
setState(() {
|
||||
_currentStep += 1;
|
||||
});
|
||||
}
|
||||
},
|
||||
onStepCancel: () {
|
||||
if (_currentStep > 0) {
|
||||
setState(() {
|
||||
_currentStep -= 1;
|
||||
});
|
||||
}
|
||||
},
|
||||
steps: [
|
||||
Step(
|
||||
title: const Text('Select Location'),
|
||||
content: const MapWithSearchField(), // Replace with your map module
|
||||
isActive: _currentStep >= 0,
|
||||
),
|
||||
Step(
|
||||
title: const Text('Choose Options'),
|
||||
content: const OptionsList(), // Replace with your options module
|
||||
isActive: _currentStep >= 1,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
12
frontend/lib/presentation/pages/login.dart
Normal file
12
frontend/lib/presentation/pages/login.dart
Normal file
@@ -0,0 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class LoginPage extends StatelessWidget {
|
||||
const LoginPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Center(child: Text('Login Page')),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:anyway/constants.dart';
|
||||
import 'package:anyway/modules/onbarding_agreement_card.dart';
|
||||
import 'package:anyway/modules/onboarding_card.dart';
|
||||
import 'package:anyway/pages/new_trip_location.dart';
|
||||
import 'package:anyway/structs/agreement.dart';
|
||||
import 'package:anyway/core/constants.dart';
|
||||
import 'package:anyway/presentation/providers/onboarding_state_provider.dart';
|
||||
import 'package:anyway/presentation/widgets/onbarding_agreement_card.dart';
|
||||
import 'package:anyway/presentation/widgets/onboarding_card.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
|
||||
List<Widget> onboardingCards = [
|
||||
@@ -33,14 +32,14 @@ List<Widget> onboardingCards = [
|
||||
];
|
||||
|
||||
|
||||
class OnboardingPage extends StatefulWidget {
|
||||
class OnboardingPage extends ConsumerStatefulWidget {
|
||||
const OnboardingPage({super.key});
|
||||
|
||||
@override
|
||||
State<OnboardingPage> createState() => _OnboardingPageState();
|
||||
ConsumerState<OnboardingPage> createState() => _OnboardingPageState();
|
||||
}
|
||||
|
||||
class _OnboardingPageState extends State<OnboardingPage> {
|
||||
class _OnboardingPageState extends ConsumerState<OnboardingPage> {
|
||||
final PageController _controller = PageController();
|
||||
late List<Widget> fullCards;
|
||||
|
||||
@@ -121,41 +120,53 @@ class _OnboardingPageState extends State<OnboardingPage> {
|
||||
label: const Icon(Icons.arrow_forward)
|
||||
);
|
||||
} else {
|
||||
// only allow the user to proceed if they have agreed to the terms and conditions
|
||||
Future<bool> hasAgreed = getAgreement().then((agreement) => agreement.agreed);
|
||||
// only allow the user to proceed if they have agreed to the terms and conditions
|
||||
// the information is accessible through ref.watch(onboardingStateProvider)
|
||||
|
||||
return FutureBuilder(
|
||||
future: hasAgreed,
|
||||
builder: (context, snapshot){
|
||||
if (snapshot.hasData && snapshot.data!) {
|
||||
return FloatingActionButton.extended(
|
||||
onPressed: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const NewTripPage()
|
||||
)
|
||||
);
|
||||
},
|
||||
label: const Row(
|
||||
children: [
|
||||
Text("Start planning!"),
|
||||
Padding(padding: EdgeInsets.only(right: 8.0)),
|
||||
Icon(Icons.map_outlined)
|
||||
],
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return Container();
|
||||
}
|
||||
}
|
||||
);
|
||||
Widget disabledWidget = FloatingActionButton.extended(
|
||||
onPressed: null,
|
||||
label: const Row(
|
||||
children: [
|
||||
Text("Start planning!"),
|
||||
Padding(padding: EdgeInsets.only(right: 8.0)),
|
||||
Icon(Icons.map_outlined)
|
||||
],
|
||||
)
|
||||
);
|
||||
|
||||
return ref.watch(onboardingStateProvider).when(
|
||||
data: (isOnboarded) {
|
||||
if (isOnboarded) {
|
||||
return FloatingActionButton.extended(
|
||||
onPressed: () {
|
||||
// proceed to the next page - pop the onboarding page so the StartPage can decide what to show next
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
label: const Row(
|
||||
children: [
|
||||
Text("Start planning!"),
|
||||
Padding(padding: EdgeInsets.only(right: 8.0)),
|
||||
Icon(Icons.map_outlined)
|
||||
],
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return disabledWidget;
|
||||
}
|
||||
},
|
||||
loading: () => disabledWidget,
|
||||
error: (error, stack) => disabledWidget,
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
void onAgreementChanged(bool value) async {
|
||||
saveAgreement(value);
|
||||
// Update the state of the OnboardingPage
|
||||
setState(() {});
|
||||
await ref.read(onboardingControllerProvider).setOnboarded(value);
|
||||
|
||||
setState(() {
|
||||
// rebuild to show the next button
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
97
frontend/lib/presentation/pages/start.dart
Normal file
97
frontend/lib/presentation/pages/start.dart
Normal file
@@ -0,0 +1,97 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:anyway/presentation/providers/onboarding_state_provider.dart';
|
||||
import 'package:anyway/presentation/pages/login.dart';
|
||||
import 'package:anyway/presentation/pages/onboarding.dart';
|
||||
|
||||
// Example providers (replace these with your actual providers)
|
||||
// final onboardingStateProvider = Provider<bool>((ref) => true); // Replace with actual onboarding state logic
|
||||
final authStateProvider = FutureProvider<bool>((ref) async => true); // Replace with actual auth state logic
|
||||
final tripsAvailableProvider = FutureProvider<bool>((ref) async => false); // Replace with actual trips availability logic
|
||||
|
||||
class StartPage extends ConsumerWidget {
|
||||
const StartPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
|
||||
// the home page is dependent on the state of the providers:
|
||||
// - if the user is not onboarded, show the onboarding flow
|
||||
// - if the user is not logged in, show the login page
|
||||
// - if there are no trips available, show the trip creation page
|
||||
// - else: show the overview page that shows the last trip
|
||||
|
||||
final onboardingState = ref.watch(onboardingStateProvider);
|
||||
final authState = ref.watch(authStateProvider);
|
||||
final tripsAvailable = ref.watch(tripsAvailableProvider);
|
||||
|
||||
return onboardingState.when(
|
||||
data: (isOnboarded) {
|
||||
if (!isOnboarded) {
|
||||
return const OnboardingPage();
|
||||
}
|
||||
|
||||
return authState.when(
|
||||
data: (isLoggedIn) {
|
||||
if (!isLoggedIn) {
|
||||
return const LoginPage();
|
||||
}
|
||||
|
||||
return tripsAvailable.when(
|
||||
data: (hasTrips) {
|
||||
if (!hasTrips) {
|
||||
return const TripCreationPage();
|
||||
}
|
||||
return const OverviewPage();
|
||||
},
|
||||
loading: () => const Scaffold(
|
||||
body: Center(child: CircularProgressIndicator()),
|
||||
),
|
||||
error: (error, stack) => Scaffold(
|
||||
body: Center(child: Text('Error: $error')),
|
||||
),
|
||||
);
|
||||
},
|
||||
loading: () => const Scaffold(
|
||||
body: Center(child: CircularProgressIndicator()),
|
||||
),
|
||||
error: (error, stack) => Scaffold(
|
||||
body: Center(child: Text('Error: $error')),
|
||||
),
|
||||
);
|
||||
},
|
||||
loading: () => const Scaffold(
|
||||
body: Center(child: CircularProgressIndicator()),
|
||||
),
|
||||
error: (error, stack) => Scaffold(
|
||||
body: Center(child: Text('Error: $error')
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
class TripCreationPage extends StatelessWidget {
|
||||
const TripCreationPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Center(child: Text('Trip Creation Page')),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class OverviewPage extends StatelessWidget {
|
||||
const OverviewPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Center(child: Text('Overview Page')),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
final onboardingStateProvider = FutureProvider<bool>((ref) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getBool('onboardingCompleted') ?? false;
|
||||
});
|
||||
|
||||
|
||||
final onboardingControllerProvider = Provider<OnboardingController>((ref) {
|
||||
return OnboardingController(ref);
|
||||
});
|
||||
|
||||
class OnboardingController {
|
||||
final Ref ref;
|
||||
OnboardingController(this.ref);
|
||||
|
||||
Future<void> setOnboarded(bool value) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setBool('onboardingCompleted', value);
|
||||
// refresh the read provider so UI updates
|
||||
ref.invalidate(onboardingStateProvider);
|
||||
}
|
||||
}
|
||||
6
frontend/lib/presentation/providers/trip_provider.dart
Normal file
6
frontend/lib/presentation/providers/trip_provider.dart
Normal file
@@ -0,0 +1,6 @@
|
||||
// import 'package:anyway/data/repositories/trip_repository.dart';
|
||||
// import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
// final weatherRepositoryProvider = Provider<TripRepository>((ref) {
|
||||
// return TripRepositoryImpl(???);
|
||||
// });
|
||||
23
frontend/lib/presentation/widgets/create_trip_location.dart
Normal file
23
frontend/lib/presentation/widgets/create_trip_location.dart
Normal file
@@ -0,0 +1,23 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:your_project/models/trip_request.dart';
|
||||
import 'package:your_project/presentation/widgets/new_trip_map.dart';
|
||||
import 'package:your_project/presentation/widgets/new_trip_location_search.dart';
|
||||
|
||||
class CreateTripLocation extends StatelessWidget {
|
||||
final TripRequest tripRequest;
|
||||
|
||||
const CreateTripLocation(this.tripRequest, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
NewTripMap(tripRequest),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(15),
|
||||
child: NewTripLocationSearch(tripRequest),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
105
frontend/lib/presentation/widgets/create_trip_location_map.dart
Normal file
105
frontend/lib/presentation/widgets/create_trip_location_map.dart
Normal file
@@ -0,0 +1,105 @@
|
||||
// A map that allows the user to select a location for a new trip.
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
import 'package:widget_to_marker/widget_to_marker.dart';
|
||||
|
||||
import 'package:anyway/constants.dart';
|
||||
|
||||
import 'package:anyway/structs/trip.dart';
|
||||
import 'package:anyway/structs/landmark.dart';
|
||||
import 'package:anyway/modules/landmark_map_marker.dart';
|
||||
|
||||
|
||||
class NewTripMap extends StatefulWidget {
|
||||
|
||||
@override
|
||||
State<NewTripMap> createState() => _NewTripMapState();
|
||||
}
|
||||
|
||||
class _NewTripMapState extends State<NewTripMap> {
|
||||
final CameraPosition _cameraPosition = const CameraPosition(
|
||||
target: LatLng(48.8566, 2.3522),
|
||||
zoom: 11.0,
|
||||
);
|
||||
GoogleMapController? _mapController;
|
||||
final Set<Marker> _markers = <Marker>{};
|
||||
|
||||
_onLongPress(LatLng location) {
|
||||
// widget.trip.landmarks.clear();
|
||||
// widget.trip.addLandmark(
|
||||
// Landmark(
|
||||
// uuid: 'pending',
|
||||
// name: 'start',
|
||||
// location: [location.latitude, location.longitude],
|
||||
// type: typeStart
|
||||
// )
|
||||
// );
|
||||
}
|
||||
|
||||
updateTripDetails() async {
|
||||
_markers.clear();
|
||||
if (widget.trip.landmarks.isNotEmpty) {
|
||||
Landmark landmark = widget.trip.landmarks.first;
|
||||
_markers.add(
|
||||
Marker(
|
||||
markerId: MarkerId(landmark.uuid),
|
||||
position: LatLng(landmark.location[0], landmark.location[1]),
|
||||
icon: await ThemedMarker(landmark: landmark, position: 0).toBitmapDescriptor(
|
||||
logicalSize: const Size(150, 150),
|
||||
imageSize: const Size(150, 150)
|
||||
),
|
||||
)
|
||||
);
|
||||
// check if the controller is ready
|
||||
|
||||
if (_mapController != null) {
|
||||
_mapController!.animateCamera(
|
||||
CameraUpdate.newLatLng(
|
||||
LatLng(landmark.location[0], landmark.location[1])
|
||||
)
|
||||
);
|
||||
}
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
void _onMapCreated(GoogleMapController controller) async {
|
||||
_mapController = controller;
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
widget.trip.addListener(updateTripDetails);
|
||||
Future<SharedPreferences> preferences = SharedPreferences.getInstance();
|
||||
|
||||
|
||||
return FutureBuilder(
|
||||
future: preferences,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
SharedPreferences prefs = snapshot.data as SharedPreferences;
|
||||
bool useLocation = prefs.getBool('useLocation') ?? true;
|
||||
return _buildMap(useLocation);
|
||||
} else {
|
||||
return const CircularProgressIndicator();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMap(bool useLocation) {
|
||||
return GoogleMap(
|
||||
onMapCreated: _onMapCreated,
|
||||
initialCameraPosition: _cameraPosition,
|
||||
onLongPress: _onLongPress,
|
||||
markers: _markers,
|
||||
cloudMapId: MAP_ID,
|
||||
mapToolbarEnabled: false,
|
||||
zoomControlsEnabled: false,
|
||||
myLocationButtonEnabled: false,
|
||||
myLocationEnabled: useLocation,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
|
||||
// A search bar that allow the user to enter a city name
|
||||
import 'dart:developer';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:geocoding/geocoding.dart';
|
||||
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
const Map<String, List> debugLocations = {
|
||||
'paris': [48.8575, 2.3514],
|
||||
'london': [51.5074, -0.1278],
|
||||
'new york': [40.7128, -74.0060],
|
||||
'tokyo': [35.6895, 139.6917],
|
||||
};
|
||||
|
||||
|
||||
|
||||
class NewTripLocationSearch extends StatefulWidget {
|
||||
Future<SharedPreferences> prefs = SharedPreferences.getInstance();
|
||||
Trip trip;
|
||||
|
||||
NewTripLocationSearch(
|
||||
this.trip,
|
||||
);
|
||||
|
||||
|
||||
@override
|
||||
State<NewTripLocationSearch> createState() => _NewTripLocationSearchState();
|
||||
}
|
||||
|
||||
class _NewTripLocationSearchState extends State<NewTripLocationSearch> {
|
||||
final TextEditingController _controller = TextEditingController();
|
||||
|
||||
setTripLocation (String query) async {
|
||||
List<Location> locations = [];
|
||||
Location startLocation;
|
||||
log('Searching for: $query');
|
||||
if (GeocodingPlatform.instance != null) {
|
||||
locations.addAll(await locationFromAddress(query));
|
||||
}
|
||||
|
||||
if (locations.isNotEmpty) {
|
||||
startLocation = locations.first;
|
||||
} else {
|
||||
log('No results found for: $query. Is geocoding available?');
|
||||
log('Setting Fallback location');
|
||||
List coordinates = debugLocations[query.toLowerCase()] ?? [48.8575, 2.3514];
|
||||
startLocation = Location(
|
||||
latitude: coordinates[0],
|
||||
longitude: coordinates[1],
|
||||
timestamp: DateTime.now(),
|
||||
);
|
||||
}
|
||||
|
||||
widget.trip.landmarks.clear();
|
||||
widget.trip.addLandmark(
|
||||
Landmark(
|
||||
uuid: 'pending',
|
||||
name: query,
|
||||
location: [startLocation.latitude, startLocation.longitude],
|
||||
type: typeStart
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
late Widget locationSearchBar = SearchBar(
|
||||
hintText: 'Enter a city name or long press on the map.',
|
||||
onSubmitted: setTripLocation,
|
||||
controller: _controller,
|
||||
leading: const Icon(Icons.search),
|
||||
trailing: [
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
setTripLocation(_controller.text);
|
||||
},
|
||||
child: const Text('Search'),
|
||||
)
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
late Widget useCurrentLocationButton = ElevatedButton(
|
||||
onPressed: () async {
|
||||
// this widget is only shown if the user has already granted location permissions
|
||||
Position position = await Geolocator.getCurrentPosition();
|
||||
widget.trip.landmarks.clear();
|
||||
widget.trip.addLandmark(
|
||||
Landmark(
|
||||
uuid: 'pending',
|
||||
name: 'start',
|
||||
location: [position.latitude, position.longitude],
|
||||
type: typeStart
|
||||
)
|
||||
);
|
||||
},
|
||||
child: const Text('Use current location'),
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder(
|
||||
future: widget.prefs,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
final useLocation = snapshot.data!.getBool('useLocation') ?? false;
|
||||
if (useLocation) {
|
||||
return Column(
|
||||
children: [
|
||||
locationSearchBar,
|
||||
useCurrentLocationButton,
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return locationSearchBar;
|
||||
}
|
||||
} else {
|
||||
return locationSearchBar;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
121
frontend/lib/presentation/widgets/onbarding_agreement_card.dart
Normal file
121
frontend/lib/presentation/widgets/onbarding_agreement_card.dart
Normal file
@@ -0,0 +1,121 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_markdown_plus/flutter_markdown_plus.dart';
|
||||
import 'package:anyway/presentation/providers/onboarding_state_provider.dart';
|
||||
import 'package:anyway/presentation/widgets/onboarding_card.dart';
|
||||
|
||||
|
||||
|
||||
class OnboardingAgreementCard extends ConsumerStatefulWidget {
|
||||
final String title;
|
||||
final String description;
|
||||
final String imagePath;
|
||||
final String agreementTextPath;
|
||||
final ValueChanged<bool> onAgreementChanged;
|
||||
|
||||
|
||||
const OnboardingAgreementCard({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.description,
|
||||
required this.imagePath,
|
||||
required this.agreementTextPath,
|
||||
required this.onAgreementChanged
|
||||
});
|
||||
|
||||
@override
|
||||
ConsumerState<OnboardingAgreementCard> createState() => _OnboardingAgreementCardState();
|
||||
|
||||
}
|
||||
|
||||
class _OnboardingAgreementCardState extends ConsumerState<OnboardingAgreementCard> {
|
||||
|
||||
// @override
|
||||
// void initState() {
|
||||
// super.initState();
|
||||
// // You can use ref here if needed in initState
|
||||
// }
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
OnboardingCard(title: widget.title, description: widget.description, imagePath: widget.imagePath),
|
||||
const Padding(padding: EdgeInsets.only(top: 20)),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
// The checkbox of the agreement
|
||||
FutureBuilder(
|
||||
future: ref.read(onboardingStateProvider.future),
|
||||
builder: (context, snapshot) {
|
||||
bool agreed = false;
|
||||
if (snapshot.hasData) {
|
||||
agreed = snapshot.data!;
|
||||
}
|
||||
return Checkbox(
|
||||
value: agreed,
|
||||
onChanged: (bool? value) {
|
||||
if (value != null) {
|
||||
widget.onAgreementChanged(value);
|
||||
}
|
||||
},
|
||||
activeColor: Colors.white,
|
||||
checkColor: Colors.black,
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
// The text of the agreement
|
||||
Text(
|
||||
"I agree to the ",
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
|
||||
// The clickable text of the agreement that shows the agreement text
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
// show a dialog with the agreement text
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
scrollable: true,
|
||||
content: FutureBuilder(
|
||||
future: DefaultAssetBundle.of(context).loadString(widget.agreementTextPath),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
return MarkdownBody(
|
||||
data: snapshot.data.toString(),
|
||||
);
|
||||
} else {
|
||||
return const CircularProgressIndicator();
|
||||
}
|
||||
|
||||
},
|
||||
)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
},
|
||||
child: Text(
|
||||
"Terms of Service (click to view)",
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
45
frontend/lib/presentation/widgets/onboarding_card.dart
Normal file
45
frontend/lib/presentation/widgets/onboarding_card.dart
Normal file
@@ -0,0 +1,45 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
|
||||
class OnboardingCard extends StatelessWidget {
|
||||
final String title;
|
||||
final String description;
|
||||
final String imagePath;
|
||||
|
||||
const OnboardingCard({super.key,
|
||||
required this.title,
|
||||
required this.description,
|
||||
required this.imagePath,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: Theme.of(context).textTheme.headlineLarge!.copyWith(
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
const Padding(padding: EdgeInsets.only(top: 20)),
|
||||
SvgPicture.asset(
|
||||
imagePath,
|
||||
height: 200,
|
||||
),
|
||||
const Padding(padding: EdgeInsets.only(top: 20)),
|
||||
Text(
|
||||
description,
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,8 +19,8 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
sdk: '>=3.3.4 <4.0.0'
|
||||
flutter: '3.29.1'
|
||||
sdk: ^3.8.0
|
||||
flutter: '3.32.0'
|
||||
|
||||
# Dependencies specify other packages that your package needs in order to work.
|
||||
# To automatically upgrade your package dependencies to the latest versions
|
||||
@@ -32,7 +32,6 @@ dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
cupertino_icons: ^1.0.6
|
||||
@@ -42,18 +41,20 @@ dependencies:
|
||||
dio: ^5.5.0+1
|
||||
google_maps_flutter: ^2.7.0
|
||||
cached_network_image: ^3.4.0
|
||||
geocoding: ^3.0.0
|
||||
geocoding: ^4.0.0
|
||||
widget_to_marker: ^1.0.6
|
||||
provider: ^6.1.2
|
||||
auto_size_text: ^3.0.0
|
||||
map_launcher: ^3.3.1
|
||||
# map_launcher: ^4.4.2
|
||||
flutter_svg: ^2.0.10+1
|
||||
url_launcher: ^6.3.0
|
||||
flutter_launcher_icons: ^0.13.1
|
||||
permission_handler: ^11.3.1
|
||||
geolocator: ^13.0.1
|
||||
fuzzywuzzy: ^1.2.0
|
||||
flutter_markdown: ^0.7.6+2
|
||||
flutter_launcher_icons: ^0.14.4
|
||||
permission_handler: ^12.0.1
|
||||
geolocator: ^14.0.2
|
||||
freezed_annotation: ^3.1.0
|
||||
json_annotation: ^4.9.0
|
||||
uuid: ^4.5.1
|
||||
flutter_riverpod: ^3.0.3
|
||||
flutter_markdown_plus: ^1.0.5
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
@@ -64,7 +65,12 @@ dev_dependencies:
|
||||
# activated in the `analysis_options.yaml` file located at the root of your
|
||||
# package. See that file for information about deactivating specific lint
|
||||
# rules and activating additional ones.
|
||||
flutter_lints: ^4.0.0
|
||||
flutter_lints: ^6.0.0
|
||||
build_runner:
|
||||
freezed: ^3.2.3
|
||||
json_serializable: ^6.11.1
|
||||
custom_lint:
|
||||
riverpod_lint: ^3.0.3
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
Reference in New Issue
Block a user