mirror of https://github.com/flutter/samples.git
Merge branch 'compass-app' of https://github.com/flutter/samples into compass-app
commit
3c7ee30631
After Width: | Height: | Size: 41 KiB |
@ -1,11 +1,8 @@
|
||||
import 'package:compass_model/model.dart';
|
||||
|
||||
import '../../../domain/models/destination/destination.dart';
|
||||
import '../../../utils/result.dart';
|
||||
|
||||
/// Data source with all possible destinations
|
||||
abstract class DestinationRepository {
|
||||
/// Get complete list of destinations
|
||||
Future<Result<List<Destination>>> getDestinations();
|
||||
|
||||
// TODO: Consider creating getByContinent instead of filtering in ViewModel
|
||||
}
|
||||
|
@ -0,0 +1,8 @@
|
||||
import '../../../domain/models/user/user.dart';
|
||||
import '../../../utils/result.dart';
|
||||
|
||||
/// Data source for user related data
|
||||
abstract class UserRepository {
|
||||
/// Get current user
|
||||
Future<Result<User>> getUser();
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
import '../../../domain/models/user/user.dart';
|
||||
import '../../../utils/result.dart';
|
||||
import '../../services/local/local_data_service.dart';
|
||||
import 'user_repository.dart';
|
||||
|
||||
class UserRepositoryLocal implements UserRepository {
|
||||
UserRepositoryLocal({
|
||||
required LocalDataService localDataService,
|
||||
}) : _localDataService = localDataService;
|
||||
|
||||
final LocalDataService _localDataService;
|
||||
|
||||
@override
|
||||
Future<Result<User>> getUser() async {
|
||||
return Result.ok(_localDataService.getUser());
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
import '../../../domain/models/user/user.dart';
|
||||
import '../../../utils/result.dart';
|
||||
import '../../services/api/api_client.dart';
|
||||
import '../../services/api/model/user/user_api_model.dart';
|
||||
import 'user_repository.dart';
|
||||
|
||||
class UserRepositoryRemote implements UserRepository {
|
||||
UserRepositoryRemote({
|
||||
required ApiClient apiClient,
|
||||
}) : _apiClient = apiClient;
|
||||
|
||||
final ApiClient _apiClient;
|
||||
|
||||
User? _cachedData;
|
||||
|
||||
@override
|
||||
Future<Result<User>> getUser() async {
|
||||
if (_cachedData != null) {
|
||||
return Future.value(Result.ok(_cachedData!));
|
||||
}
|
||||
|
||||
final result = await _apiClient.getUser();
|
||||
switch (result) {
|
||||
case Ok<UserApiModel>():
|
||||
final user = User(
|
||||
name: result.value.name,
|
||||
picture: result.value.picture,
|
||||
);
|
||||
_cachedData = user;
|
||||
return Result.ok(user);
|
||||
case Error<UserApiModel>():
|
||||
return Result.error(result.error);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'user_api_model.freezed.dart';
|
||||
part 'user_api_model.g.dart';
|
||||
|
||||
@freezed
|
||||
abstract class UserApiModel with _$UserApiModel {
|
||||
const factory UserApiModel({
|
||||
/// The user's ID.
|
||||
required String id,
|
||||
|
||||
/// The user's name.
|
||||
required String name,
|
||||
|
||||
/// The user's email.
|
||||
required String email,
|
||||
|
||||
/// The user's picture URL.
|
||||
required String picture,
|
||||
}) = _UserApiModel;
|
||||
|
||||
factory UserApiModel.fromJson(Map<String, Object?> json) =>
|
||||
_$UserApiModelFromJson(json);
|
||||
}
|
@ -0,0 +1,241 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// 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 'user_api_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||
|
||||
UserApiModel _$UserApiModelFromJson(Map<String, dynamic> json) {
|
||||
return _UserApiModel.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$UserApiModel {
|
||||
/// The user's ID.
|
||||
String get id => throw _privateConstructorUsedError;
|
||||
|
||||
/// The user's name.
|
||||
String get name => throw _privateConstructorUsedError;
|
||||
|
||||
/// The user's email.
|
||||
String get email => throw _privateConstructorUsedError;
|
||||
|
||||
/// The user's picture URL.
|
||||
String get picture => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this UserApiModel to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of UserApiModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$UserApiModelCopyWith<UserApiModel> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $UserApiModelCopyWith<$Res> {
|
||||
factory $UserApiModelCopyWith(
|
||||
UserApiModel value, $Res Function(UserApiModel) then) =
|
||||
_$UserApiModelCopyWithImpl<$Res, UserApiModel>;
|
||||
@useResult
|
||||
$Res call({String id, String name, String email, String picture});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$UserApiModelCopyWithImpl<$Res, $Val extends UserApiModel>
|
||||
implements $UserApiModelCopyWith<$Res> {
|
||||
_$UserApiModelCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of UserApiModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? id = null,
|
||||
Object? name = null,
|
||||
Object? email = null,
|
||||
Object? picture = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
id: null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
name: null == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
email: null == email
|
||||
? _value.email
|
||||
: email // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
picture: null == picture
|
||||
? _value.picture
|
||||
: picture // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$UserApiModelImplCopyWith<$Res>
|
||||
implements $UserApiModelCopyWith<$Res> {
|
||||
factory _$$UserApiModelImplCopyWith(
|
||||
_$UserApiModelImpl value, $Res Function(_$UserApiModelImpl) then) =
|
||||
__$$UserApiModelImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({String id, String name, String email, String picture});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$UserApiModelImplCopyWithImpl<$Res>
|
||||
extends _$UserApiModelCopyWithImpl<$Res, _$UserApiModelImpl>
|
||||
implements _$$UserApiModelImplCopyWith<$Res> {
|
||||
__$$UserApiModelImplCopyWithImpl(
|
||||
_$UserApiModelImpl _value, $Res Function(_$UserApiModelImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of UserApiModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? id = null,
|
||||
Object? name = null,
|
||||
Object? email = null,
|
||||
Object? picture = null,
|
||||
}) {
|
||||
return _then(_$UserApiModelImpl(
|
||||
id: null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
name: null == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
email: null == email
|
||||
? _value.email
|
||||
: email // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
picture: null == picture
|
||||
? _value.picture
|
||||
: picture // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$UserApiModelImpl implements _UserApiModel {
|
||||
const _$UserApiModelImpl(
|
||||
{required this.id,
|
||||
required this.name,
|
||||
required this.email,
|
||||
required this.picture});
|
||||
|
||||
factory _$UserApiModelImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$UserApiModelImplFromJson(json);
|
||||
|
||||
/// The user's ID.
|
||||
@override
|
||||
final String id;
|
||||
|
||||
/// The user's name.
|
||||
@override
|
||||
final String name;
|
||||
|
||||
/// The user's email.
|
||||
@override
|
||||
final String email;
|
||||
|
||||
/// The user's picture URL.
|
||||
@override
|
||||
final String picture;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'UserApiModel(id: $id, name: $name, email: $email, picture: $picture)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$UserApiModelImpl &&
|
||||
(identical(other.id, id) || other.id == id) &&
|
||||
(identical(other.name, name) || other.name == name) &&
|
||||
(identical(other.email, email) || other.email == email) &&
|
||||
(identical(other.picture, picture) || other.picture == picture));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, id, name, email, picture);
|
||||
|
||||
/// Create a copy of UserApiModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$UserApiModelImplCopyWith<_$UserApiModelImpl> get copyWith =>
|
||||
__$$UserApiModelImplCopyWithImpl<_$UserApiModelImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$UserApiModelImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _UserApiModel implements UserApiModel {
|
||||
const factory _UserApiModel(
|
||||
{required final String id,
|
||||
required final String name,
|
||||
required final String email,
|
||||
required final String picture}) = _$UserApiModelImpl;
|
||||
|
||||
factory _UserApiModel.fromJson(Map<String, dynamic> json) =
|
||||
_$UserApiModelImpl.fromJson;
|
||||
|
||||
/// The user's ID.
|
||||
@override
|
||||
String get id;
|
||||
|
||||
/// The user's name.
|
||||
@override
|
||||
String get name;
|
||||
|
||||
/// The user's email.
|
||||
@override
|
||||
String get email;
|
||||
|
||||
/// The user's picture URL.
|
||||
@override
|
||||
String get picture;
|
||||
|
||||
/// Create a copy of UserApiModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$UserApiModelImplCopyWith<_$UserApiModelImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'user_api_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$UserApiModelImpl _$$UserApiModelImplFromJson(Map<String, dynamic> json) =>
|
||||
_$UserApiModelImpl(
|
||||
id: json['id'] as String,
|
||||
name: json['name'] as String,
|
||||
email: json['email'] as String,
|
||||
picture: json['picture'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$UserApiModelImplToJson(_$UserApiModelImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'name': instance.name,
|
||||
'email': instance.email,
|
||||
'picture': instance.picture,
|
||||
};
|
@ -1,12 +1,18 @@
|
||||
import 'package:compass_model/model.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import '../activity/activity.dart';
|
||||
import '../destination/destination.dart';
|
||||
|
||||
part 'booking.freezed.dart';
|
||||
part 'booking.g.dart';
|
||||
|
||||
@freezed
|
||||
class Booking with _$Booking {
|
||||
const factory Booking({
|
||||
/// Optional ID of the booking.
|
||||
/// May be null if the booking is not yet stored.
|
||||
int? id,
|
||||
|
||||
/// Start date of the trip
|
||||
required DateTime startDate,
|
||||
|
@ -0,0 +1,17 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'user.freezed.dart';
|
||||
part 'user.g.dart';
|
||||
|
||||
@freezed
|
||||
abstract class User with _$User {
|
||||
const factory User({
|
||||
/// The user's name.
|
||||
required String name,
|
||||
|
||||
/// The user's picture URL.
|
||||
required String picture,
|
||||
}) = _User;
|
||||
|
||||
factory User.fromJson(Map<String, Object?> json) => _$UserFromJson(json);
|
||||
}
|
@ -0,0 +1,185 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// 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 'user.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||
|
||||
User _$UserFromJson(Map<String, dynamic> json) {
|
||||
return _User.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$User {
|
||||
/// The user's name.
|
||||
String get name => throw _privateConstructorUsedError;
|
||||
|
||||
/// The user's picture URL.
|
||||
String get picture => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this User to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of User
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$UserCopyWith<User> get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $UserCopyWith<$Res> {
|
||||
factory $UserCopyWith(User value, $Res Function(User) then) =
|
||||
_$UserCopyWithImpl<$Res, User>;
|
||||
@useResult
|
||||
$Res call({String name, String picture});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$UserCopyWithImpl<$Res, $Val extends User>
|
||||
implements $UserCopyWith<$Res> {
|
||||
_$UserCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of User
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? name = null,
|
||||
Object? picture = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
name: null == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
picture: null == picture
|
||||
? _value.picture
|
||||
: picture // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$UserImplCopyWith<$Res> implements $UserCopyWith<$Res> {
|
||||
factory _$$UserImplCopyWith(
|
||||
_$UserImpl value, $Res Function(_$UserImpl) then) =
|
||||
__$$UserImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({String name, String picture});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$UserImplCopyWithImpl<$Res>
|
||||
extends _$UserCopyWithImpl<$Res, _$UserImpl>
|
||||
implements _$$UserImplCopyWith<$Res> {
|
||||
__$$UserImplCopyWithImpl(_$UserImpl _value, $Res Function(_$UserImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of User
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? name = null,
|
||||
Object? picture = null,
|
||||
}) {
|
||||
return _then(_$UserImpl(
|
||||
name: null == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
picture: null == picture
|
||||
? _value.picture
|
||||
: picture // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$UserImpl implements _User {
|
||||
const _$UserImpl({required this.name, required this.picture});
|
||||
|
||||
factory _$UserImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$UserImplFromJson(json);
|
||||
|
||||
/// The user's name.
|
||||
@override
|
||||
final String name;
|
||||
|
||||
/// The user's picture URL.
|
||||
@override
|
||||
final String picture;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'User(name: $name, picture: $picture)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$UserImpl &&
|
||||
(identical(other.name, name) || other.name == name) &&
|
||||
(identical(other.picture, picture) || other.picture == picture));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, name, picture);
|
||||
|
||||
/// Create a copy of User
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$UserImplCopyWith<_$UserImpl> get copyWith =>
|
||||
__$$UserImplCopyWithImpl<_$UserImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$UserImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _User implements User {
|
||||
const factory _User(
|
||||
{required final String name, required final String picture}) = _$UserImpl;
|
||||
|
||||
factory _User.fromJson(Map<String, dynamic> json) = _$UserImpl.fromJson;
|
||||
|
||||
/// The user's name.
|
||||
@override
|
||||
String get name;
|
||||
|
||||
/// The user's picture URL.
|
||||
@override
|
||||
String get picture;
|
||||
|
||||
/// Create a copy of User
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$UserImplCopyWith<_$UserImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'user.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$UserImpl _$$UserImplFromJson(Map<String, dynamic> json) => _$UserImpl(
|
||||
name: json['name'] as String,
|
||||
picture: json['picture'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$UserImplToJson(_$UserImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'name': instance.name,
|
||||
'picture': instance.picture,
|
||||
};
|
@ -1,24 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
// TODO: Maybe the text styles here should be moved to the respective widgets
|
||||
class TextStyles {
|
||||
// Note: original Figma file uses Nikkei Maru
|
||||
// which is not available on GoogleFonts
|
||||
// Note: Card title theme doesn't change based on light/dark mode
|
||||
static final cardTitleStyle = GoogleFonts.rubik(
|
||||
textStyle: const TextStyle(
|
||||
fontWeight: FontWeight.w800,
|
||||
fontSize: 15.0,
|
||||
color: Colors.white,
|
||||
letterSpacing: 1,
|
||||
shadows: [
|
||||
// Helps to read the text a bit better
|
||||
Shadow(
|
||||
blurRadius: 3.0,
|
||||
color: Colors.black,
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../auth/logout/view_models/logout_viewmodel.dart';
|
||||
import '../../auth/logout/widgets/logout_button.dart';
|
||||
import '../../core/localization/applocalization.dart';
|
||||
import '../../core/themes/dimens.dart';
|
||||
import '../view_models/home_viewmodel.dart';
|
||||
|
||||
class HomeHeader extends StatelessWidget {
|
||||
const HomeHeader({
|
||||
super.key,
|
||||
required this.viewModel,
|
||||
});
|
||||
|
||||
final HomeViewModel viewModel;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final user = viewModel.user;
|
||||
if (user == null) {
|
||||
return const SizedBox();
|
||||
}
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ClipOval(
|
||||
child: Image.asset(
|
||||
user.picture,
|
||||
width: Dimens.of(context).profilePictureSize,
|
||||
height: Dimens.of(context).profilePictureSize,
|
||||
),
|
||||
),
|
||||
LogoutButton(
|
||||
viewModel: LogoutViewModel(
|
||||
authRepository: context.read(),
|
||||
itineraryConfigRepository: context.read(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: Dimens.paddingVertical),
|
||||
_Title(
|
||||
text: AppLocalization.of(context).nameTrips(user.name),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _Title extends StatelessWidget {
|
||||
const _Title({
|
||||
required this.text,
|
||||
});
|
||||
|
||||
final String text;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ShaderMask(
|
||||
blendMode: BlendMode.srcIn,
|
||||
shaderCallback: (bounds) => RadialGradient(
|
||||
center: Alignment.bottomLeft,
|
||||
radius: 2,
|
||||
colors: [
|
||||
Colors.purple.shade700,
|
||||
Colors.purple.shade400,
|
||||
],
|
||||
).createShader(
|
||||
Rect.fromLTWH(0, 0, bounds.width, bounds.height),
|
||||
),
|
||||
child: Text(
|
||||
text,
|
||||
style: GoogleFonts.rubik(
|
||||
textStyle: Theme.of(context).textTheme.headlineLarge,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
import 'package:compass_app/data/services/api/api_client.dart';
|
||||
import 'package:compass_app/domain/models/continent/continent.dart';
|
||||
import 'package:compass_app/utils/result.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../../../../testing/mocks.dart';
|
||||
import '../../../../testing/models/activity.dart';
|
||||
import '../../../../testing/models/booking.dart';
|
||||
import '../../../../testing/models/destination.dart';
|
||||
import '../../../../testing/models/user.dart';
|
||||
|
||||
void main() {
|
||||
group('ApiClient', () {
|
||||
late MockHttpClient mockHttpClient;
|
||||
late ApiClient apiClient;
|
||||
|
||||
setUp(() {
|
||||
mockHttpClient = MockHttpClient();
|
||||
apiClient = ApiClient(clientFactory: () => mockHttpClient);
|
||||
});
|
||||
|
||||
test('should get continents', () async {
|
||||
final continents = [const Continent(name: 'NAME', imageUrl: 'URL')];
|
||||
mockHttpClient.mockGet('/continent', continents);
|
||||
final result = await apiClient.getContinents();
|
||||
expect(result.asOk.value, continents);
|
||||
});
|
||||
|
||||
test('should get activities by destination', () async {
|
||||
final activites = [kActivity];
|
||||
mockHttpClient.mockGet(
|
||||
'/destination/${kDestination1.ref}/activity',
|
||||
activites,
|
||||
);
|
||||
final result =
|
||||
await apiClient.getActivityByDestination(kDestination1.ref);
|
||||
expect(result.asOk.value, activites);
|
||||
});
|
||||
|
||||
test('should get booking', () async {
|
||||
mockHttpClient.mockGet(
|
||||
'/booking/${kBookingApiModel.id}',
|
||||
kBookingApiModel,
|
||||
);
|
||||
final result = await apiClient.getBooking(kBookingApiModel.id!);
|
||||
expect(result.asOk.value, kBookingApiModel);
|
||||
});
|
||||
|
||||
test('should get bookings', () async {
|
||||
mockHttpClient.mockGet('/booking', [kBookingApiModel]);
|
||||
final result = await apiClient.getBookings();
|
||||
expect(result.asOk.value, [kBookingApiModel]);
|
||||
});
|
||||
|
||||
test('should get destinations', () async {
|
||||
mockHttpClient.mockGet('/destination', [kDestination1]);
|
||||
final result = await apiClient.getDestinations();
|
||||
expect(result.asOk.value, [kDestination1]);
|
||||
});
|
||||
|
||||
test('should get user', () async {
|
||||
mockHttpClient.mockGet('/user', userApiModel);
|
||||
final result = await apiClient.getUser();
|
||||
expect(result.asOk.value, userApiModel);
|
||||
});
|
||||
|
||||
test('should post booking', () async {
|
||||
mockHttpClient.mockPost('/booking', kBookingApiModel);
|
||||
final result = await apiClient.postBooking(kBookingApiModel);
|
||||
expect(result.asOk.value, kBookingApiModel);
|
||||
});
|
||||
|
||||
test('should delete booking', () async {
|
||||
mockHttpClient.mockDelete('/booking/0');
|
||||
final result = await apiClient.deleteBooking(0);
|
||||
expect(result, isA<Ok<void>>());
|
||||
});
|
||||
});
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
import 'package:compass_app/data/services/api/auth_api_client.dart';
|
||||
import 'package:compass_app/data/services/api/model/login_request/login_request.dart';
|
||||
import 'package:compass_app/data/services/api/model/login_response/login_response.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../../../../testing/mocks.dart';
|
||||
|
||||
void main() {
|
||||
group('AuthApiClient', () {
|
||||
late MockHttpClient mockHttpClient;
|
||||
late AuthApiClient apiClient;
|
||||
|
||||
setUp(() {
|
||||
mockHttpClient = MockHttpClient();
|
||||
apiClient = AuthApiClient(clientFactory: () => mockHttpClient);
|
||||
});
|
||||
|
||||
test('should post login', () async {
|
||||
const loginResponse = LoginResponse(
|
||||
token: 'TOKEN',
|
||||
userId: '123',
|
||||
);
|
||||
mockHttpClient.mockPost(
|
||||
'/login',
|
||||
loginResponse,
|
||||
200,
|
||||
);
|
||||
final result = await apiClient.login(
|
||||
const LoginRequest(
|
||||
email: 'EMAIL',
|
||||
password: 'PASSWORD',
|
||||
),
|
||||
);
|
||||
expect(result.asOk.value, loginResponse);
|
||||
});
|
||||
});
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
import 'package:compass_app/data/repositories/user/user_repository.dart';
|
||||
import 'package:compass_app/domain/models/user/user.dart';
|
||||
import 'package:compass_app/utils/result.dart';
|
||||
|
||||
import '../../models/user.dart';
|
||||
|
||||
class FakeUserRepository implements UserRepository {
|
||||
@override
|
||||
Future<Result<User>> getUser() async {
|
||||
return Result.ok(user);
|
||||
}
|
||||
}
|
@ -1,4 +1,54 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
class MockGoRouter extends Mock implements GoRouter {}
|
||||
|
||||
class MockHttpClient extends Mock implements HttpClient {}
|
||||
|
||||
class MockHttpHeaders extends Mock implements HttpHeaders {}
|
||||
|
||||
class MockHttpClientRequest extends Mock implements HttpClientRequest {}
|
||||
|
||||
class MockHttpClientResponse extends Mock implements HttpClientResponse {}
|
||||
|
||||
extension HttpMethodMocks on MockHttpClient {
|
||||
void mockGet(String path, Object object) {
|
||||
when(() => get(any(), any(), path)).thenAnswer((invocation) {
|
||||
final request = MockHttpClientRequest();
|
||||
final response = MockHttpClientResponse();
|
||||
when(() => request.close()).thenAnswer((_) => Future.value(response));
|
||||
when(() => request.headers).thenReturn(MockHttpHeaders());
|
||||
when(() => response.statusCode).thenReturn(200);
|
||||
when(() => response.transform(utf8.decoder))
|
||||
.thenAnswer((_) => Stream.value(jsonEncode(object)));
|
||||
return Future.value(request);
|
||||
});
|
||||
}
|
||||
|
||||
void mockPost(String path, Object object, [int statusCode = 201]) {
|
||||
when(() => post(any(), any(), path)).thenAnswer((invocation) {
|
||||
final request = MockHttpClientRequest();
|
||||
final response = MockHttpClientResponse();
|
||||
when(() => request.close()).thenAnswer((_) => Future.value(response));
|
||||
when(() => request.headers).thenReturn(MockHttpHeaders());
|
||||
when(() => response.statusCode).thenReturn(statusCode);
|
||||
when(() => response.transform(utf8.decoder))
|
||||
.thenAnswer((_) => Stream.value(jsonEncode(object)));
|
||||
return Future.value(request);
|
||||
});
|
||||
}
|
||||
|
||||
void mockDelete(String path) {
|
||||
when(() => delete(any(), any(), path)).thenAnswer((invocation) {
|
||||
final request = MockHttpClientRequest();
|
||||
final response = MockHttpClientResponse();
|
||||
when(() => request.close()).thenAnswer((_) => Future.value(response));
|
||||
when(() => request.headers).thenReturn(MockHttpHeaders());
|
||||
when(() => response.statusCode).thenReturn(204);
|
||||
return Future.value(request);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,14 @@
|
||||
import 'package:compass_app/data/services/api/model/user/user_api_model.dart';
|
||||
import 'package:compass_app/domain/models/user/user.dart';
|
||||
|
||||
const userApiModel = UserApiModel(
|
||||
id: 'ID',
|
||||
name: 'NAME',
|
||||
email: 'EMAIL',
|
||||
picture: 'assets/user.jpg',
|
||||
);
|
||||
|
||||
const user = User(
|
||||
name: 'NAME',
|
||||
picture: 'assets/user.jpg',
|
||||
);
|
@ -1,7 +0,0 @@
|
||||
# https://dart.dev/guides/libraries/private-files
|
||||
# Created by `dart pub`
|
||||
.dart_tool/
|
||||
|
||||
# Avoid committing pubspec.lock for library packages; see
|
||||
# https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||
pubspec.lock
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue