mirror of https://github.com/flutter/samples.git
[Compass App] User name and profile picture (#2435)
This PR completes the home screen, adding the username and the profile picture. ![Screenshot from 2024-09-11 14-52-32](https://github.com/user-attachments/assets/197f9932-ae86-4277-92ac-8fd413b52010) ![Screenshot from 2024-09-11 14-52-23](https://github.com/user-attachments/assets/915b8c54-ac85-40d9-adb8-3bf0521b78f5) This feature follows the basic structure: - Added repository, both local and remote. - Added API call + API model. - Added Domain model (reduced version only containing name and profile picture). - Modified the ViewModel to obtain the user and expose it to the Widget. - Updated Widget to display the username and profile picture. - Added `/user` endpoint to server project. - Updated widget tests, as well as integration tests. The Compass App is basically completed with it. Maybe the next step is to merge it to the `main` branch? And then we can do a full review, remove TODOs and setup CI jobs. ## Pre-launch Checklist - [x] I read the [Flutter Style Guide] _recently_, and have followed its advice. - [x] I signed the [CLA]. - [x] I read the [Contributors Guide]. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-devrel channel on [Discord]. <!-- Links --> [Flutter Style Guide]: https://github.com/flutter/flutter/blob/master/docs/contributing/Style-guide-for-Flutter-repo.md [CLA]: https://cla.developers.google.com/ [Discord]: https://github.com/flutter/flutter/blob/master/docs/contributing/Chat.md [Contributors Guide]: https://github.com/flutter/samples/blob/main/CONTRIBUTING.mdpull/2437/head
parent
93b86b86f3
commit
b00ce6c7b3
After Width: | Height: | Size: 41 KiB |
@ -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,
|
||||
};
|
@ -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,
|
||||
};
|
@ -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,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);
|
||||
}
|
||||
}
|
@ -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',
|
||||
);
|
@ -0,0 +1,23 @@
|
||||
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 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,
|
||||
}) = _User;
|
||||
|
||||
factory User.fromJson(Map<String, Object?> json) => _$UserFromJson(json);
|
||||
}
|
@ -0,0 +1,236 @@
|
||||
// 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 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 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 id, String name, String email, 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? 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 _$$UserImplCopyWith<$Res> implements $UserCopyWith<$Res> {
|
||||
factory _$$UserImplCopyWith(
|
||||
_$UserImpl value, $Res Function(_$UserImpl) then) =
|
||||
__$$UserImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({String id, String name, String email, 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? id = null,
|
||||
Object? name = null,
|
||||
Object? email = null,
|
||||
Object? picture = null,
|
||||
}) {
|
||||
return _then(_$UserImpl(
|
||||
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 _$UserImpl implements _User {
|
||||
const _$UserImpl(
|
||||
{required this.id,
|
||||
required this.name,
|
||||
required this.email,
|
||||
required this.picture});
|
||||
|
||||
factory _$UserImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$UserImplFromJson(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 'User(id: $id, name: $name, email: $email, picture: $picture)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$UserImpl &&
|
||||
(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 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 id,
|
||||
required final String name,
|
||||
required final String email,
|
||||
required final String picture}) = _$UserImpl;
|
||||
|
||||
factory _User.fromJson(Map<String, dynamic> json) = _$UserImpl.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 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,22 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'user.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$UserImpl _$$UserImplFromJson(Map<String, dynamic> json) => _$UserImpl(
|
||||
id: json['id'] as String,
|
||||
name: json['name'] as String,
|
||||
email: json['email'] as String,
|
||||
picture: json['picture'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$UserImplToJson(_$UserImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'name': instance.name,
|
||||
'email': instance.email,
|
||||
'picture': instance.picture,
|
||||
};
|
@ -0,0 +1,23 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:compass_server/config/constants.dart';
|
||||
import 'package:shelf/shelf.dart';
|
||||
import 'package:shelf_router/shelf_router.dart';
|
||||
|
||||
/// Implements a simple user API.
|
||||
///
|
||||
/// This API only returns a hardcoded user for demonstration purposes.
|
||||
class UserApi {
|
||||
Router get router {
|
||||
final router = Router();
|
||||
|
||||
router.get('/', (Request request) async {
|
||||
return Response.ok(
|
||||
json.encode(Constants.user),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
);
|
||||
});
|
||||
|
||||
return router;
|
||||
}
|
||||
}
|
Loading…
Reference in new issue