diff --git a/lib/core/router/routers_config.dart b/lib/core/router/routers_config.dart index 7e4b1eb..fce3c07 100644 --- a/lib/core/router/routers_config.dart +++ b/lib/core/router/routers_config.dart @@ -6,6 +6,7 @@ import 'package:recipes/features/drinks/infrastructure/dto/drink_details/drink_d import 'package:recipes/features/home/presentation/screens/home.dart'; import 'package:recipes/features/drinks/presentation/screens/drinks_by_category.dart'; import 'package:recipes/features/on_boarding/presentation/screens/on_boarding_screen.dart'; +import 'package:recipes/features/register/presentation/screens/register.dart'; import '../../features/drinks/presentation/screens/drinkDetails.dart'; import '../../features/favourites/presentation/screens/favourites.dart'; diff --git a/lib/features/home/presentation/screens/home.dart b/lib/features/home/presentation/screens/home.dart index 95b9d14..ae6a3c6 100644 --- a/lib/features/home/presentation/screens/home.dart +++ b/lib/features/home/presentation/screens/home.dart @@ -13,6 +13,7 @@ import 'package:recipes/features/categories/presentation/widgets/category_item.d import 'package:recipes/features/drinks/infrastructure/dto/drink_details/drink_details_model.dart'; import 'package:recipes/features/home/presentation/widgets/common_row.dart'; import 'package:recipes/features/home/presentation/widgets/custom_app_bar.dart'; +import 'package:recipes/features/home/presentation/widgets/custom_drawer.dart'; import 'package:recipes/features/home/presentation/widgets/custom_textfield.dart'; import 'package:recipes/features/search/presentation/provider/search_value_provider.dart'; import '../../../drinks/presentation/riverpod/drink_details/selected_drink_provider.dart'; @@ -29,6 +30,7 @@ class HomeScreen extends ConsumerStatefulWidget { class HomeScreenState extends ConsumerState { final TextEditingController controller = TextEditingController(); + final GlobalKey scaffoldKey = GlobalKey(); @override void initState() { @@ -50,8 +52,12 @@ class HomeScreenState extends ConsumerState { final searchState = ref.watch(searchProvider); log(AppDimensions.normalize(39.38).toString()); return Scaffold( + key: scaffoldKey, backgroundColor: Colors.white, - appBar: customAppBar(), + appBar: customAppBar( + scaffoldKey: scaffoldKey, + ), + drawer: const CustomDrawer(), body: SingleChildScrollView( child: Column( children: [ diff --git a/lib/features/home/presentation/widgets/custom_app_bar.dart b/lib/features/home/presentation/widgets/custom_app_bar.dart index 2df04a4..2523a44 100644 --- a/lib/features/home/presentation/widgets/custom_app_bar.dart +++ b/lib/features/home/presentation/widgets/custom_app_bar.dart @@ -1,22 +1,32 @@ +import 'dart:developer'; + import 'package:flutter/material.dart'; import 'package:recipes/configs/configs.dart'; import 'package:recipes/core/core.dart'; -PreferredSizeWidget customAppBar(){ +PreferredSizeWidget customAppBar({ + required GlobalKey scaffoldKey, +}) { return PreferredSize( preferredSize: Size( double.infinity, AppDimensions.normalize(35), ), child: Padding( - padding: Space.all(1.5,2.2), + padding: Space.all(1.5, 2.2), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [ - Image.asset( - AppAssets.menu, - width: AppDimensions.normalize(10), + GestureDetector( + onTap: () { + log("drawer tapped"); + scaffoldKey.currentState?.openDrawer(); + }, + child: Image.asset( + AppAssets.menu, + width: AppDimensions.normalize(10), + ), ), Image.asset( AppAssets.appBar, @@ -30,4 +40,4 @@ PreferredSizeWidget customAppBar(){ ), ), ); -} \ No newline at end of file +} diff --git a/lib/features/home/presentation/widgets/custom_drawer.dart b/lib/features/home/presentation/widgets/custom_drawer.dart new file mode 100644 index 0000000..d5fb0d8 --- /dev/null +++ b/lib/features/home/presentation/widgets/custom_drawer.dart @@ -0,0 +1,97 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:recipes/core/core.dart'; + +import '../../../login/presentation/screens/login.dart'; + + + +class CustomDrawer extends ConsumerWidget { + const CustomDrawer({ super.key}); + + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Drawer( + child: ListView( + padding: EdgeInsets.zero, + children: [ + const DrawerHeader( + decoration: BoxDecoration( + color: AppColors.deepBlue, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CircleAvatar( + radius: 30, + backgroundImage: AssetImage(AppAssets.profile), + ), + SizedBox(height: 10), + Text( + 'John Doe', + style: TextStyle( + color: Colors.white, + fontSize: 20, + ), + ), + Text( + 'johndoe@example.com', + style: TextStyle( + color: Colors.white70, + fontSize: 14, + ), + ), + ], + ), + ), + ListTile( + leading: const Icon(Icons.home), + title: const Text('Home'), + onTap: () { + Navigator.pop(context); // Close the drawer + context.goNamed(Routes.home.name); + }, + ), + ListTile( + leading: const Icon(Icons.favorite), + title: const Text('Favorites'), + onTap: () { + Navigator.pop(context); // Close the drawer + context.goNamed(Routes.favorites.name); + }, + ), + ListTile( + leading: const Icon(Icons.settings), + title: const Text('Settings'), + onTap: () { + Navigator.pop(context); // Close the drawer + // context.goNamed(Routes.settings.name); + }, + ), + ListTile( + leading: const Icon(Icons.person), + title: const Text('Profile'), + onTap: () { + Navigator.pop(context); // Close the drawer + //context.goNamed(Routes.profile.name); + }, + ), + const Divider(), + ListTile( + leading: const Icon(Icons.logout), + title: const Text('Logout'), + onTap: () { + /* Navigator.pushReplacement( + context, + MaterialPageRoute(builder: (context) => const LoginScreen()), + );*/ + // Handle logout + }, + ), + ], + ), + ); + } +} diff --git a/lib/features/login/presentation/screens/login.dart b/lib/features/login/presentation/screens/login.dart new file mode 100644 index 0000000..3a043ac --- /dev/null +++ b/lib/features/login/presentation/screens/login.dart @@ -0,0 +1,100 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:hive/hive.dart'; +import 'package:recipes/configs/configs.dart'; +import 'package:collection/collection.dart'; +import '../../../home/presentation/screens/home.dart'; +import '../../../register/infrastructure/dto/user_model.dart'; + +class LoginScreen extends ConsumerStatefulWidget { + const LoginScreen({Key? key}) : super(key: key); + + @override + _LoginScreenState createState() => _LoginScreenState(); +} + +class _LoginScreenState extends ConsumerState { + final TextEditingController _emailController = TextEditingController(); + final TextEditingController _passwordController = TextEditingController(); + final GlobalKey _formKey = GlobalKey(); + + Future _login() async { + if (_formKey.currentState?.validate() ?? false) { + final Box userBox = Hive.box('users'); + final String email = _emailController.text; + final String password = _passwordController.text; + + final User? user = userBox.values.firstWhereOrNull( + (user) => user.email == email && user.password == password, + //orElse: () => null, + ); + + if (user != null) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Login Successful')), + ); + Navigator.pushReplacement( + context, + MaterialPageRoute(builder: (context) => const HomeScreen()), + ); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Invalid email or password')), + ); + } + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Login'), + ), + body: Padding( + padding: EdgeInsets.all(AppDimensions.normalize(4)), + child: Form( + key: _formKey, + child: ListView( + children: [ + TextFormField( + controller: _emailController, + decoration: const InputDecoration( + labelText: 'Email', + ), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter your email'; + } + if (!RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(value)) { + return 'Please enter a valid email'; + } + return null; + }, + ), + Space.yf(1.0), + TextFormField( + controller: _passwordController, + decoration: const InputDecoration( + labelText: 'Password', + ), + obscureText: true, + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter your password'; + } + return null; + }, + ), + Space.yf(2.0), + ElevatedButton( + onPressed: _login, + child: const Text('Login'), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/features/register/infrastructure/dto/user_model.dart b/lib/features/register/infrastructure/dto/user_model.dart new file mode 100644 index 0000000..5044fda --- /dev/null +++ b/lib/features/register/infrastructure/dto/user_model.dart @@ -0,0 +1,41 @@ +import 'package:hive/hive.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'user_model.freezed.dart'; +part 'user_model.g.dart'; + +@freezed +class User with _$User { + @HiveType(typeId: 1) + const factory User({ + @HiveField(0) required String id, + @HiveField(1) required String name, + @HiveField(2) required String email, + @HiveField(3) required String password, + }) = _User; + + factory User.fromJson(Map json) => _$UserFromJson(json); +} + +class UserAdapter extends TypeAdapter { + @override + final int typeId = 1; + + @override + User read(BinaryReader reader) { + return User( + id: reader.readString(), + name: reader.readString(), + email: reader.readString(), + password: reader.readString(), + ); + } + + @override + void write(BinaryWriter writer, User obj) { + writer.writeString(obj.id); + writer.writeString(obj.name); + writer.writeString(obj.email); + writer.writeString(obj.password); + } +} diff --git a/lib/features/register/infrastructure/dto/user_model.freezed.dart b/lib/features/register/infrastructure/dto/user_model.freezed.dart new file mode 100644 index 0000000..187cdb1 --- /dev/null +++ b/lib/features/register/infrastructure/dto/user_model.freezed.dart @@ -0,0 +1,224 @@ +// 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_model.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(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 json) { + return _User.fromJson(json); +} + +/// @nodoc +mixin _$User { + @HiveField(0) + String get id => throw _privateConstructorUsedError; + @HiveField(1) + String get name => throw _privateConstructorUsedError; + @HiveField(2) + String get email => throw _privateConstructorUsedError; + @HiveField(3) + String get password => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $UserCopyWith get copyWith => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $UserCopyWith<$Res> { + factory $UserCopyWith(User value, $Res Function(User) then) = + _$UserCopyWithImpl<$Res, User>; + @useResult + $Res call( + {@HiveField(0) String id, + @HiveField(1) String name, + @HiveField(2) String email, + @HiveField(3) String password}); +} + +/// @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; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? name = null, + Object? email = null, + Object? password = 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, + password: null == password + ? _value.password + : password // 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( + {@HiveField(0) String id, + @HiveField(1) String name, + @HiveField(2) String email, + @HiveField(3) String password}); +} + +/// @nodoc +class __$$UserImplCopyWithImpl<$Res> + extends _$UserCopyWithImpl<$Res, _$UserImpl> + implements _$$UserImplCopyWith<$Res> { + __$$UserImplCopyWithImpl(_$UserImpl _value, $Res Function(_$UserImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? name = null, + Object? email = null, + Object? password = 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, + password: null == password + ? _value.password + : password // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc +@JsonSerializable() +@HiveType(typeId: 1) +class _$UserImpl implements _User { + const _$UserImpl( + {@HiveField(0) required this.id, + @HiveField(1) required this.name, + @HiveField(2) required this.email, + @HiveField(3) required this.password}); + + factory _$UserImpl.fromJson(Map json) => + _$$UserImplFromJson(json); + + @override + @HiveField(0) + final String id; + @override + @HiveField(1) + final String name; + @override + @HiveField(2) + final String email; + @override + @HiveField(3) + final String password; + + @override + String toString() { + return 'User(id: $id, name: $name, email: $email, password: $password)'; + } + + @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.password, password) || + other.password == password)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash(runtimeType, id, name, email, password); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$UserImplCopyWith<_$UserImpl> get copyWith => + __$$UserImplCopyWithImpl<_$UserImpl>(this, _$identity); + + @override + Map toJson() { + return _$$UserImplToJson( + this, + ); + } +} + +abstract class _User implements User { + const factory _User( + {@HiveField(0) required final String id, + @HiveField(1) required final String name, + @HiveField(2) required final String email, + @HiveField(3) required final String password}) = _$UserImpl; + + factory _User.fromJson(Map json) = _$UserImpl.fromJson; + + @override + @HiveField(0) + String get id; + @override + @HiveField(1) + String get name; + @override + @HiveField(2) + String get email; + @override + @HiveField(3) + String get password; + @override + @JsonKey(ignore: true) + _$$UserImplCopyWith<_$UserImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/features/register/infrastructure/dto/user_model.g.dart b/lib/features/register/infrastructure/dto/user_model.g.dart new file mode 100644 index 0000000..d59d37e --- /dev/null +++ b/lib/features/register/infrastructure/dto/user_model.g.dart @@ -0,0 +1,69 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'user_model.dart'; + +// ************************************************************************** +// TypeAdapterGenerator +// ************************************************************************** + +class UserImplAdapter extends TypeAdapter<_$UserImpl> { + @override + final int typeId = 1; + + @override + _$UserImpl read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = { + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), + }; + return _$UserImpl( + id: fields[0] as String, + name: fields[1] as String, + email: fields[2] as String, + password: fields[3] as String, + ); + } + + @override + void write(BinaryWriter writer, _$UserImpl obj) { + writer + ..writeByte(4) + ..writeByte(0) + ..write(obj.id) + ..writeByte(1) + ..write(obj.name) + ..writeByte(2) + ..write(obj.email) + ..writeByte(3) + ..write(obj.password); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is UserImplAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$UserImpl _$$UserImplFromJson(Map json) => _$UserImpl( + id: json['id'] as String, + name: json['name'] as String, + email: json['email'] as String, + password: json['password'] as String, + ); + +Map _$$UserImplToJson(_$UserImpl instance) => + { + 'id': instance.id, + 'name': instance.name, + 'email': instance.email, + 'password': instance.password, + }; diff --git a/lib/features/register/presentation/screens/register.dart b/lib/features/register/presentation/screens/register.dart new file mode 100644 index 0000000..dbf4cd2 --- /dev/null +++ b/lib/features/register/presentation/screens/register.dart @@ -0,0 +1,123 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:hive/hive.dart'; +import 'package:uuid/uuid.dart'; +import 'package:recipes/core/core.dart'; +import 'package:recipes/configs/configs.dart'; + +import '../../infrastructure/dto/user_model.dart'; + +class RegisterScreen extends ConsumerStatefulWidget { + const RegisterScreen({Key? key}) : super(key: key); + + @override + _RegisterScreenState createState() => _RegisterScreenState(); +} + +class _RegisterScreenState extends ConsumerState { + final TextEditingController _nameController = TextEditingController(); + final TextEditingController _emailController = TextEditingController(); + final TextEditingController _passwordController = TextEditingController(); + final TextEditingController _confirmPasswordController = TextEditingController(); + final GlobalKey _formKey = GlobalKey(); + + Future _register() async { + if (_formKey.currentState?.validate() ?? false) { + final Box userBox = Hive.box('users'); + final User newUser = User( + id: const Uuid().v4(), + name: _nameController.text, + email: _emailController.text, + password: _passwordController.text, + ); + await userBox.put(newUser.id, newUser); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: const Text('User Registered Successfully')), + ); + Navigator.pop(context); // Redirect to the previous screen or home + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Register'), + ), + body: Padding( + padding: EdgeInsets.all(AppDimensions.normalize(4)), + child: Form( + key: _formKey, + child: ListView( + children: [ + TextFormField( + controller: _nameController, + decoration: const InputDecoration( + labelText: 'Name', + ), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter your name'; + } + return null; + }, + ), + Space.yf(1.0), + TextFormField( + controller: _emailController, + decoration: const InputDecoration( + labelText: 'Email', + ), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter your email'; + } + if (!RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(value)) { + return 'Please enter a valid email'; + } + return null; + }, + ), + Space.yf(1.0), + TextFormField( + controller: _passwordController, + decoration: const InputDecoration( + labelText: 'Password', + ), + obscureText: true, + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter your password'; + } + return null; + }, + ), + Space.yf(1.0), + TextFormField( + controller: _confirmPasswordController, + decoration: const InputDecoration( + labelText: 'Confirm Password', + ), + obscureText: true, + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please confirm your password'; + } + if (value != _passwordController.text) { + return 'Passwords do not match'; + } + return null; + }, + ), + Space.yf(2.0), + ElevatedButton( + onPressed: _register, + child: const Text('Register'), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index 6bdc54b..99be6c2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,6 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:recipes/core/core.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'features/drinks/infrastructure/dto/drink_details/drink_details_model.dart'; +import 'features/register/infrastructure/dto/user_model.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -13,7 +14,8 @@ void main() async { Hive.registerAdapter(DrinkDetailsAdapter()); await Hive.openBox('favorites'); - + Hive.registerAdapter(UserAdapter()); + await Hive.openBox('users'); SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); await SystemChrome.setPreferredOrientations([ diff --git a/pubspec.lock b/pubspec.lock index 9bc7ec5..5b45af3 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -516,26 +516,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.0" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "2.0.1" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "2.0.1" lints: dependency: transitive description: @@ -572,10 +572,10 @@ packages: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.11.0" mime: dependency: transitive description: @@ -881,10 +881,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.6.1" timing: dependency: transitive description: @@ -910,7 +910,7 @@ packages: source: hosted version: "2.2.2" uuid: - dependency: transitive + dependency: "direct main" description: name: uuid sha256: "814e9e88f21a176ae1359149021870e87f7cddaf633ab678a5d2b0bff7fd1ba8" @@ -953,10 +953,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "13.0.0" watcher: dependency: transitive description: @@ -1014,5 +1014,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.3.0 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + dart: ">=3.2.3 <4.0.0" + flutter: ">=3.16.0" diff --git a/pubspec.yaml b/pubspec.yaml index 16319dd..d884a4c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -50,6 +50,7 @@ dependencies: flutter_staggered_grid_view: hive: hive_flutter: + uuid: dev_dependencies: flutter_test: