added settings

This commit is contained in:
Komek Hayytnazarov 2024-07-23 11:37:42 +05:00
parent a4527fbb73
commit c512f143cb
39 changed files with 837 additions and 58 deletions

View File

@ -0,0 +1 @@
export 'user_bloc/user_bloc.dart';

View File

@ -0,0 +1,78 @@
import 'dart:async';
import 'package:equatable/equatable.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../core/core.dart';
import '../../domain/domain.dart';
part 'user_event.dart';
part 'user_state.dart';
class UserBloc extends Bloc<UserEvent, UserState> {
final GetCachedUserUseCase _getCachedUserUseCase;
final SignInUseCase _signInUseCase;
final SignUpUseCase _signUpUseCase;
final SignOutUseCase _signOutUseCase;
UserBloc(
this._signInUseCase,
this._getCachedUserUseCase,
this._signOutUseCase,
this._signUpUseCase,
) : super(UserInitial()) {
on<SignInUser>(_onSignIn);
on<SignUpUser>(_onSignUp);
on<CheckUser>(_onCheckUser);
on<SignOutUser>(_onSignOut);
}
void _onSignIn(SignInUser event, Emitter<UserState> emit) async {
try {
emit(UserLoading());
final result = await _signInUseCase(event.params);
result.fold(
(failure) => emit(UserLoggedFail(failure)),
(user) => emit(UserLogged(user)),
);
} catch (e) {
emit(UserLoggedFail(ExceptionFailure()));
}
}
void _onCheckUser(CheckUser event, Emitter<UserState> emit) async {
try {
emit(UserLoading());
final result = await _getCachedUserUseCase(NoParams());
result.fold(
(failure) => emit(UserLoggedFail(failure)),
(user) => emit(UserLogged(user)),
);
} catch (e) {
emit(UserLoggedFail(ExceptionFailure()));
}
}
FutureOr<void> _onSignUp(SignUpUser event, Emitter<UserState> emit) async {
try {
emit(UserLoading());
final result = await _signUpUseCase(event.params);
result.fold(
(failure) => emit(UserLoggedFail(failure)),
(user) => emit(UserLogged(user)),
);
} catch (e) {
emit(UserLoggedFail(ExceptionFailure()));
}
}
void _onSignOut(SignOutUser event, Emitter<UserState> emit) async {
try {
emit(UserLoading());
await _signOutUseCase(NoParams());
emit(UserLoggedOut());
} catch (e) {
emit(UserLoggedFail(ExceptionFailure()));
}
}
}

View File

@ -0,0 +1,18 @@
part of 'user_bloc.dart';
@immutable
abstract class UserEvent {}
class SignInUser extends UserEvent {
final SignInParams params;
SignInUser(this.params);
}
class SignUpUser extends UserEvent {
final SignUpParams params;
SignUpUser(this.params);
}
class SignOutUser extends UserEvent {}
class CheckUser extends UserEvent {}

View File

@ -0,0 +1,33 @@
part of 'user_bloc.dart';
@immutable
abstract class UserState extends Equatable {}
class UserInitial extends UserState {
@override
List<Object> get props => [];
}
class UserLoading extends UserState {
@override
List<Object> get props => [];
}
class UserLogged extends UserState {
final User user;
UserLogged(this.user);
@override
List<Object> get props => [user];
}
class UserLoggedFail extends UserState {
final Failure failure;
UserLoggedFail(this.failure);
@override
List<Object> get props => [failure];
}
class UserLoggedOut extends UserState {
@override
List<Object> get props => [];
}

View File

@ -1,17 +1,28 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../application/application.dart';
import '../core.dart';
import '../../di/di.dart' as di;
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
return MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => di.sl<UserBloc>()..add(CheckUser()),
),
],
child: const MaterialApp(
debugShowCheckedModeBanner: false,
title: appTitle,
onGenerateRoute: AppRouter.onGenerateRoute,
initialRoute: AppRouter.splash,
),
);
}
}

View File

@ -3,3 +3,6 @@ export 'constants/constants.dart';
export 'errors/errors.dart';
export 'router/router.dart';
export 'observer/observer.dart';
export 'usecases/usecases.dart';
export 'networkchecker/network_info.dart';
export 'utils/utils.dart';

View File

@ -0,0 +1,14 @@
import 'package:internet_connection_checker/internet_connection_checker.dart';
abstract class NetworkInfo {
Future<bool> get isConnected;
}
class NetworkInfoImpl implements NetworkInfo {
final InternetConnectionChecker connectionChecker;
NetworkInfoImpl(this.connectionChecker);
@override
Future<bool> get isConnected => connectionChecker.hasConnection;
}

View File

@ -0,0 +1,13 @@
import 'package:dartz/dartz.dart';
import 'package:equatable/equatable.dart';
import '../errors/failures.dart';
abstract class UseCase<Type, Params> {
Future<Either<Failure, Type>> call(Params params);
}
class NoParams extends Equatable {
@override
List<Object> get props => [];
}

View File

@ -0,0 +1 @@
// TODO Implement this library.

10
lib/core/utils/utils.dart Normal file
View File

@ -0,0 +1,10 @@
export 'form_validator.dart';
class FormValidator {
static String? validateField(String? val) {
if (val == null || val.isEmpty) {
return 'This field can\'t be empty';
}
return null;
}
}

3
lib/data/data.dart Normal file
View File

@ -0,0 +1,3 @@
export 'data_sources/data_sources.dart';
export 'models/models.dart';
export 'repositories/repositories.dart';

View File

@ -0,0 +1,2 @@
export 'local/local.dart';
export 'remote/remote.dart';

View File

@ -0,0 +1 @@
export 'user_local_data_source.dart';

View File

@ -0,0 +1,74 @@
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../../core/core.dart';
import '../../models/models.dart';
abstract class UserLocalDataSource {
Future<String> getToken();
Future<UserModel> getUser();
Future<void> saveToken(String token);
Future<void> saveUser(UserModel user);
Future<void> clearCache();
Future<bool> isTokenAvailable();
}
const cachedToken = 'TOKEN';
const cachedUser = 'USER';
class UserLocalDataSourceImpl implements UserLocalDataSource {
final FlutterSecureStorage secureStorage;
final SharedPreferences sharedPreferences;
UserLocalDataSourceImpl({required this.sharedPreferences, required this.secureStorage});
@override
Future<String> getToken() async {
String? token = await secureStorage.read(key: cachedToken);
return Future.value(token);
}
@override
Future<void> saveToken(String token) async {
await secureStorage.write(key: cachedToken, value: token);
}
@override
Future<UserModel> getUser() async {
if (sharedPreferences.getBool('first_run') ?? true) {
await secureStorage.deleteAll();
sharedPreferences.setBool('first_run', false);
}
final jsonString = sharedPreferences.getString(cachedUser);
if (jsonString != null) {
return Future.value(userModelFromJson(jsonString));
} else {
throw CacheException();
}
}
@override
Future<void> saveUser(UserModel user) {
return sharedPreferences.setString(
cachedUser,
userModelToJson(user),
);
}
@override
Future<bool> isTokenAvailable() async {
String? token = await secureStorage.read(key: cachedToken);
return Future.value((token != null));
}
@override
Future<void> clearCache() async {
await secureStorage.deleteAll();
// await sharedPreferences.remove(cachedCart);
await sharedPreferences.remove(cachedUser);
}
}

View File

@ -0,0 +1 @@
export 'user_remote_data_source.dart';

View File

@ -0,0 +1,59 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import '../../../core/core.dart';
import '../../../domain/domain.dart';
import '../../data.dart';
abstract class UserRemoteDataSource {
Future<AuthenticationResponseModel> signIn(SignInParams params);
Future<AuthenticationResponseModel> signUp(SignUpParams params);
}
class UserRemoteDataSourceImpl implements UserRemoteDataSource {
final http.Client client;
UserRemoteDataSourceImpl({required this.client});
@override
Future<AuthenticationResponseModel> signIn(SignInParams params) async {
debugPrint('signIn');
final response = await client.post(Uri.parse('$baseUrl/authentication/local/sign-in'),
headers: {
'Content-Type': 'application/json',
},
body: json.encode({
'identifier': params.username,
'password': params.password,
}));
if (response.statusCode == 200) {
return authenticationResponseModelFromJson(response.body);
} else if (response.statusCode == 400 || response.statusCode == 401) {
throw CredentialFailure();
} else {
throw ServerException();
}
}
@override
Future<AuthenticationResponseModel> signUp(SignUpParams params) async {
final response = await client.post(Uri.parse('$baseUrl/authentication/local/sign-up'),
headers: {
'Content-Type': 'application/json',
},
body: json.encode({
'firstName': params.firstName,
'lastName': params.lastName,
'email': params.email,
'password': params.password,
}));
if (response.statusCode == 201) {
return authenticationResponseModelFromJson(response.body);
} else if (response.statusCode == 400 || response.statusCode == 401) {
throw CredentialFailure();
} else {
throw ServerException();
}
}
}

View File

@ -0,0 +1,2 @@
export 'user/user_model.dart';
export 'user/authentication_response_model.dart';

View File

@ -0,0 +1,28 @@
import 'dart:convert';
import 'user_model.dart';
AuthenticationResponseModel authenticationResponseModelFromJson(String str) =>
AuthenticationResponseModel.fromJson(json.decode(str));
String authenticationResponseModelToJson(AuthenticationResponseModel data) => json.encode(data.toJson());
class AuthenticationResponseModel {
final String token;
final UserModel user;
const AuthenticationResponseModel({
required this.token,
required this.user,
});
factory AuthenticationResponseModel.fromJson(Map<String, dynamic> json) => AuthenticationResponseModel(
token: json['token'],
user: UserModel.fromJson(json['user']),
);
Map<String, dynamic> toJson() => {
'token': token,
'user': user.toJson(),
};
}

View File

@ -0,0 +1,30 @@
import 'dart:convert';
import '../../../domain/entities/user/user.dart';
UserModel userModelFromJson(String str) => UserModel.fromJson(json.decode(str));
String userModelToJson(UserModel data) => json.encode(data.toJson());
class UserModel extends User {
const UserModel({
required super.id,
required super.firstName,
required super.lastName,
required super.email,
});
factory UserModel.fromJson(Map<String, dynamic> json) => UserModel(
id: json['_id'],
firstName: json['firstName'],
lastName: json['lastName'],
email: json['email'],
);
Map<String, dynamic> toJson() => {
'_id': id,
'firstName': firstName,
'lastName': lastName,
'email': email,
};
}

View File

@ -0,0 +1 @@
export 'user_repository_impl.dart';

View File

@ -0,0 +1,71 @@
import 'package:dartz/dartz.dart';
import '../../core/core.dart';
import '../../domain/domain.dart';
import '../data_sources/data_sources.dart';
import '../models/user/authentication_response_model.dart';
typedef _DataSourceChooser = Future<AuthenticationResponseModel> Function();
class UserRepositoryImpl implements UserRepository {
final UserRemoteDataSource remoteDataSource;
final UserLocalDataSource localDataSource;
final NetworkInfo networkInfo;
UserRepositoryImpl({
required this.remoteDataSource,
required this.localDataSource,
required this.networkInfo,
});
@override
Future<Either<Failure, User>> signIn(params) async {
return await _authenticate(() {
return remoteDataSource.signIn(params);
});
}
@override
Future<Either<Failure, User>> signUp(params) async {
return await _authenticate(() {
return remoteDataSource.signUp(params);
});
}
@override
Future<Either<Failure, User>> getCachedUser() async {
try {
final user = await localDataSource.getUser();
return Right(user);
} on CacheFailure {
return Left(CacheFailure());
}
}
@override
Future<Either<Failure, NoParams>> signOut() async {
try {
await localDataSource.clearCache();
return Right(NoParams());
} on CacheFailure {
return Left(CacheFailure());
}
}
Future<Either<Failure, User>> _authenticate(
_DataSourceChooser getDataSource,
) async {
if (await networkInfo.isConnected) {
try {
final remoteResponse = await getDataSource();
localDataSource.saveToken(remoteResponse.token);
localDataSource.saveUser(remoteResponse.user);
return Right(remoteResponse.user);
} on Failure catch (failure) {
return Left(failure);
}
} else {
return Left(NetworkFailure());
}
}
}

20
lib/di/common.dart Normal file
View File

@ -0,0 +1,20 @@
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:internet_connection_checker/internet_connection_checker.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http/http.dart' as http;
import '../core/networkchecker/network_info.dart';
import 'di.dart';
void registerCommonDependencies() async {
final sharedPreferences = await SharedPreferences.getInstance();
const secureStorage = FlutterSecureStorage();
sl.registerLazySingleton(() => sharedPreferences);
sl.registerLazySingleton(() => secureStorage);
sl.registerLazySingleton(() => http.Client());
sl.registerLazySingleton(() => InternetConnectionChecker());
// Register NetworkInfo
sl.registerLazySingleton<NetworkInfo>(() => NetworkInfoImpl(sl()));
}

23
lib/di/di.dart Normal file
View File

@ -0,0 +1,23 @@
import 'package:get_it/get_it.dart';
import 'common.dart';
import 'user.dart';
final sl = GetIt.instance;
// Main Initialization
Future<void> init() async {
// Register features
registerUserFeature();
// registerCategoryFeature();
// registerProductFeature();
// registerDeliveryInfoFeature();
// registerCartFeature();
// registerOrderFeature();
// Register Cubits
// registerCubits();
// Register common dependencies
registerCommonDependencies();
}

32
lib/di/user.dart Normal file
View File

@ -0,0 +1,32 @@
// Feature: User
import '../application/user_bloc/user_bloc.dart';
import '../data/data.dart';
import '../domain/domain.dart';
import 'di.dart';
void registerUserFeature() {
// User BLoC and Use Cases
sl.registerFactory(() => UserBloc(sl(), sl(), sl(), sl()));
sl.registerLazySingleton(() => GetCachedUserUseCase(sl()));
sl.registerLazySingleton(() => SignInUseCase(sl()));
sl.registerLazySingleton(() => SignUpUseCase(sl()));
sl.registerLazySingleton(() => SignOutUseCase(sl()));
// User Repository and Data Sources
sl.registerLazySingleton<UserRepository>(
() => UserRepositoryImpl(
remoteDataSource: sl(),
localDataSource: sl(),
networkInfo: sl(),
),
);
sl.registerLazySingleton<UserLocalDataSource>(
() => UserLocalDataSourceImpl(sharedPreferences: sl(), secureStorage: sl()),
);
sl.registerLazySingleton<UserRemoteDataSource>(
() => UserRemoteDataSourceImpl(client: sl()),
);
}

3
lib/domain/domain.dart Normal file
View File

@ -0,0 +1,3 @@
export 'entities/entities.dart';
export 'repositories/repositories.dart';
export 'usecases/usecases.dart';

View File

@ -0,0 +1 @@
export 'user/user.dart';

View File

@ -0,0 +1,25 @@
import 'package:equatable/equatable.dart';
class User extends Equatable {
final String id;
final String firstName;
final String lastName;
final String? image;
final String email;
const User({
required this.id,
required this.firstName,
required this.lastName,
this.image,
required this.email,
});
@override
List<Object> get props => [
id,
firstName,
lastName,
email,
];
}

View File

@ -0,0 +1 @@
export 'user_repository.dart';

View File

@ -0,0 +1,11 @@
import 'package:dartz/dartz.dart';
import '../../core/core.dart';
import '../domain.dart';
abstract class UserRepository {
Future<Either<Failure, User>> signIn(SignInParams params);
Future<Either<Failure, User>> signUp(SignUpParams params);
Future<Either<Failure, NoParams>> signOut();
Future<Either<Failure, User>> getCachedUser();
}

View File

@ -0,0 +1 @@
export 'user/user.dart';

View File

@ -0,0 +1,14 @@
import 'package:dartz/dartz.dart';
import '../../../core/core.dart';
import '../../domain.dart';
class GetCachedUserUseCase implements UseCase<User, NoParams> {
final UserRepository repository;
GetCachedUserUseCase(this.repository);
@override
Future<Either<Failure, User>> call(NoParams params) async {
return await repository.getCachedUser();
}
}

View File

@ -0,0 +1,23 @@
import 'package:dartz/dartz.dart';
import '../../../core/core.dart';
import '../../domain.dart';
class SignInUseCase implements UseCase<User, SignInParams> {
final UserRepository repository;
SignInUseCase(this.repository);
@override
Future<Either<Failure, User>> call(SignInParams params) async {
return await repository.signIn(params);
}
}
class SignInParams {
final String username;
final String password;
const SignInParams({
required this.username,
required this.password,
});
}

View File

@ -0,0 +1,14 @@
import 'package:dartz/dartz.dart';
import '../../../core/core.dart';
import '../../domain.dart';
class SignOutUseCase implements UseCase<NoParams, NoParams> {
final UserRepository repository;
SignOutUseCase(this.repository);
@override
Future<Either<Failure, NoParams>> call(NoParams params) async {
return await repository.signOut();
}
}

View File

@ -0,0 +1,27 @@
import 'package:dartz/dartz.dart';
import '../../../core/core.dart';
import '../../domain.dart';
class SignUpUseCase implements UseCase<User, SignUpParams> {
final UserRepository repository;
SignUpUseCase(this.repository);
@override
Future<Either<Failure, User>> call(SignUpParams params) async {
return await repository.signUp(params);
}
}
class SignUpParams {
final String firstName;
final String lastName;
final String email;
final String password;
const SignUpParams({
required this.firstName,
required this.lastName,
required this.email,
required this.password,
});
}

View File

@ -0,0 +1,4 @@
export 'get_cached_user_usecase.dart';
export 'sign_in_usecase.dart';
export 'sign_out_usecase.dart';
export 'sign_up_usecase.dart';

View File

@ -4,13 +4,14 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_phoenix/flutter_phoenix.dart';
import 'core/core.dart';
import 'di/di.dart' as di;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// await GetStorage.init();
// await di.init();
await di.init();
Bloc.observer = MyBlocObserver();

View File

@ -1,14 +1,33 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import '../../application/user_bloc/user_bloc.dart';
import '../../configs/app.dart';
import '../../configs/configs.dart';
import '../../core/core.dart';
import '../../domain/domain.dart';
import '../widgets/button.dart';
class LoginScreen extends StatelessWidget {
class LoginScreen extends StatefulWidget {
const LoginScreen({super.key});
@override
State<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final TextEditingController _userNameController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
final _formKey = GlobalKey<FormState>();
@override
void dispose() {
_userNameController.dispose();
_passwordController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
App.init(context);
@ -82,6 +101,8 @@ class LoginScreen extends StatelessWidget {
),
child: Padding(
padding: Space.hf().copyWith(top: 30),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -105,14 +126,15 @@ class LoginScreen extends StatelessWidget {
/// username field
const Text('Login'),
Space.yf(0.30),
const TextField(
decoration: InputDecoration(
Space.y!,
TextFormField(
controller: _userNameController,
decoration: const InputDecoration(
hintText: 'Öz loginiňizi ýazyň',
prefixIcon: Icon(Icons.person_outline),
// suffixIcon: Icon(Icons.visibility_off),
border: OutlineInputBorder(),
),
validator: (val) => FormValidator.validateField(val),
),
/// gap
@ -120,15 +142,17 @@ class LoginScreen extends StatelessWidget {
/// password field
const Text('Açar sözi'),
Space.yf(0.30),
const TextField(
Space.y!,
TextFormField(
controller: _passwordController,
obscureText: true,
decoration: InputDecoration(
decoration: const InputDecoration(
hintText: 'Öz açar sözüňi ýazyň',
prefixIcon: Icon(Icons.lock_outline),
suffixIcon: Icon(Icons.visibility_off),
border: OutlineInputBorder(),
),
validator: (val) => FormValidator.validateField(val),
),
/// gap
@ -139,7 +163,18 @@ class LoginScreen extends StatelessWidget {
child: AppButton(
textColor: AppColors.primary,
btnColor: AppColors.yellow,
onPressed: () {},
onPressed: () {
if (_formKey.currentState!.validate()) {
context.read<UserBloc>().add(
SignInUser(
SignInParams(
username: _userNameController.text,
password: _passwordController.text,
),
),
);
}
},
text: 'Yzarlap başlaň',
),
)
@ -148,6 +183,7 @@ class LoginScreen extends StatelessWidget {
),
),
),
),
],
),
),

View File

@ -134,6 +134,54 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.1"
flutter_secure_storage:
dependency: "direct main"
description:
name: flutter_secure_storage
sha256: "165164745e6afb5c0e3e3fcc72a012fb9e58496fb26ffb92cf22e16a821e85d0"
url: "https://pub.dev"
source: hosted
version: "9.2.2"
flutter_secure_storage_linux:
dependency: transitive
description:
name: flutter_secure_storage_linux
sha256: "4d91bfc23047422cbcd73ac684bc169859ee766482517c22172c86596bf1464b"
url: "https://pub.dev"
source: hosted
version: "1.2.1"
flutter_secure_storage_macos:
dependency: transitive
description:
name: flutter_secure_storage_macos
sha256: "1693ab11121a5f925bbea0be725abfcfbbcf36c1e29e571f84a0c0f436147a81"
url: "https://pub.dev"
source: hosted
version: "3.1.2"
flutter_secure_storage_platform_interface:
dependency: transitive
description:
name: flutter_secure_storage_platform_interface
sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8
url: "https://pub.dev"
source: hosted
version: "1.1.2"
flutter_secure_storage_web:
dependency: transitive
description:
name: flutter_secure_storage_web
sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9
url: "https://pub.dev"
source: hosted
version: "1.2.1"
flutter_secure_storage_windows:
dependency: transitive
description:
name: flutter_secure_storage_windows
sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709
url: "https://pub.dev"
source: hosted
version: "3.1.2"
flutter_svg:
dependency: "direct main"
description:
@ -184,6 +232,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.0+1"
js:
dependency: transitive
description:
name: js
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
url: "https://pub.dev"
source: hosted
version: "0.6.7"
leak_tracker:
dependency: transitive
description:
@ -264,6 +320,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.1"
path_provider:
dependency: transitive
description:
name: path_provider
sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161
url: "https://pub.dev"
source: hosted
version: "2.1.3"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: "30c5aa827a6ae95ce2853cdc5fe3971daaac00f6f081c419c013f7f57bff2f5e"
url: "https://pub.dev"
source: hosted
version: "2.2.7"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
url: "https://pub.dev"
source: hosted
version: "2.4.0"
path_provider_linux:
dependency: transitive
description:
@ -485,6 +565,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.5.1"
win32:
dependency: transitive
description:
name: win32
sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4
url: "https://pub.dev"
source: hosted
version: "5.5.1"
xdg_directories:
dependency: transitive
description:

View File

@ -22,6 +22,7 @@ dependencies:
internet_connection_checker: ^1.0.0+1
flutter_phoenix: ^1.1.1
flutter_svg: ^2.0.10+1
flutter_secure_storage: ^9.2.2
dev_dependencies:
flutter_test: