diff --git a/README.md b/README.md
index 26d69e2..c22d3e5 100644
--- a/README.md
+++ b/README.md
@@ -2,4 +2,5 @@
The most incredible 🏨 app in the 🌎
+
⚠️ I'm reimagining and restructuring various aspects of the Hotelyn app. This includes improvements to the user interface, performance optimizations, and the incorporation of exciting new functionalities ⚠️
diff --git a/assets/images/messages/empty.png b/assets/images/messages/empty.png
new file mode 100644
index 0000000..f4abeb4
Binary files /dev/null and b/assets/images/messages/empty.png differ
diff --git a/ios/Flutter/flutter_export_environment.sh b/ios/Flutter/flutter_export_environment.sh
index b5decaf..a0c3889 100755
--- a/ios/Flutter/flutter_export_environment.sh
+++ b/ios/Flutter/flutter_export_environment.sh
@@ -3,11 +3,12 @@
export "FLUTTER_ROOT=/Users/enzolizama/Development/flutter"
export "FLUTTER_APPLICATION_PATH=/Users/enzolizama/Projects/hotelyn"
export "COCOAPODS_PARALLEL_CODE_SIGN=true"
-export "FLUTTER_TARGET=lib/main.dart"
+export "FLUTTER_TARGET=/Users/enzolizama/Projects/hotelyn/lib/main.dart"
export "FLUTTER_BUILD_DIR=build"
export "FLUTTER_BUILD_NAME=1.0.0"
export "FLUTTER_BUILD_NUMBER=1"
+export "DART_DEFINES=RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ==,RkxVVFRFUl9XRUJfQ0FOVkFTS0lUX1VSTD1odHRwczovL3d3dy5nc3RhdGljLmNvbS9mbHV0dGVyLWNhbnZhc2tpdC8zZjNlNTYwMjM2NTM5YjdlMjcwMmY1YWM3OTBiMmE0NjkxYjMyZDQ5Lw=="
export "DART_OBFUSCATION=false"
export "TRACK_WIDGET_CREATION=true"
export "TREE_SHAKE_ICONS=false"
-export "PACKAGE_CONFIG=.dart_tool/package_config.json"
+export "PACKAGE_CONFIG=/Users/enzolizama/Projects/hotelyn/.dart_tool/package_config.json"
diff --git a/lib/components/app_bar.dart b/lib/components/app_bar.dart
new file mode 100644
index 0000000..cf80704
--- /dev/null
+++ b/lib/components/app_bar.dart
@@ -0,0 +1,35 @@
+import 'package:flutter/widgets.dart';
+import 'package:hotelyn/components/text_style/hotelyn_text_style.dart';
+
+class HotelynHomeAppBar extends StatelessWidget implements PreferredSizeWidget {
+ const HotelynHomeAppBar({
+ super.key,
+ required this.title,
+ required this.iconData,
+ this.onIconPressed,
+ });
+
+ final String title;
+ final IconData iconData;
+ final VoidCallback? onIconPressed;
+
+ @override
+ Widget build(BuildContext context) {
+ return Padding(
+ padding: const EdgeInsets.only(top: 60, left: 20, right: 20),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ title,
+ style: HotelynTextStyle.h1,
+ ),
+ Icon(iconData, size: 24),
+ ],
+ ),
+ );
+ }
+
+ @override
+ Size get preferredSize => const Size(double.infinity, 120);
+}
diff --git a/lib/components/text_input/hotelyn_search_input.dart b/lib/components/text_input/hotelyn_search_input.dart
new file mode 100644
index 0000000..abb0f56
--- /dev/null
+++ b/lib/components/text_input/hotelyn_search_input.dart
@@ -0,0 +1,30 @@
+import 'package:flutter/material.dart';
+import 'package:hotelyn/components/theme/hotelyn_colors.dart';
+
+class HotelynSearchInput extends StatelessWidget {
+ const HotelynSearchInput({
+ super.key,
+ required this.hintText,
+ });
+
+ final String hintText;
+
+ final _radius = 30.0;
+
+ @override
+ Widget build(BuildContext context) {
+ return TextField(
+ autofocus: false,
+ decoration: InputDecoration(
+ filled: true,
+ fillColor: HotelynAppColors.lightGrey,
+ prefixIcon: const Icon(Icons.search),
+ border: OutlineInputBorder(
+ borderRadius: BorderRadius.all(Radius.circular(_radius)),
+ borderSide: BorderSide.none,
+ ),
+ hintText: hintText,
+ ),
+ );
+ }
+}
diff --git a/lib/features/home/home_tab.dart b/lib/features/home/home_tab.dart
index 1793e6d..3dfec41 100644
--- a/lib/features/home/home_tab.dart
+++ b/lib/features/home/home_tab.dart
@@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hotelyn/components/navigation_bar/navigation_bar.dart';
import 'package:hotelyn/components/navigation_bar/navigation_bar_cubit.dart';
import 'package:hotelyn/components/navigation_bar/navigation_bar_state.dart';
+import 'package:hotelyn/features/messages/messages_cubit.dart';
import 'package:hotelyn/features/messages/messages_tab.dart';
import 'package:hotelyn/features/profile/profile_cubit.dart';
import 'package:hotelyn/features/profile/profile_tab.dart';
@@ -25,6 +26,9 @@ class HomePage extends StatelessWidget {
BlocProvider(
create: (_) => ProfileCubit(),
),
+ BlocProvider(
+ create: (_) => MessagesCubit(),
+ ),
],
child: BlocBuilder(
builder: (context, state) {
@@ -34,7 +38,7 @@ class HomePage extends StatelessWidget {
body: [
const HomeTab(),
const SearchTab(),
- const MesssagesTab(),
+ const MessagesTab(),
const ProfileTab()
].elementAt(index),
);
diff --git a/lib/features/home/widgets/home_header.dart b/lib/features/home/widgets/home_header.dart
index f2c84a8..211246f 100644
--- a/lib/features/home/widgets/home_header.dart
+++ b/lib/features/home/widgets/home_header.dart
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
+import 'package:hotelyn/components/text_input/hotelyn_search_input.dart';
import 'package:hotelyn/components/text_style/hotelyn_text_style.dart';
import 'package:hotelyn/components/theme/hotelyn_colors.dart';
@@ -44,19 +45,7 @@ class HotelynHeader extends SliverPersistentHeaderDelegate {
),
SizedBox(height: 32),
// TODO: Split in different widget and pass controller
- TextField(
- autofocus: false,
- decoration: InputDecoration(
- filled: true,
- fillColor: HotelynAppColors.lightGrey,
- prefixIcon: Icon(Icons.search),
- border: OutlineInputBorder(
- borderRadius: BorderRadius.all(Radius.circular(30)),
- borderSide: BorderSide.none,
- ),
- hintText: 'Search hotel',
- ),
- ),
+ HotelynSearchInput(hintText: 'Search hotel'),
],
),
),
diff --git a/lib/features/messages/messages_cubit.dart b/lib/features/messages/messages_cubit.dart
new file mode 100644
index 0000000..39807b8
--- /dev/null
+++ b/lib/features/messages/messages_cubit.dart
@@ -0,0 +1,10 @@
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:hotelyn/features/messages/messages_state.dart';
+
+class MessagesCubit extends Cubit {
+ MessagesCubit() : super(MessagesEmpty()) {
+ _load();
+ }
+
+ void _load() {}
+}
diff --git a/lib/features/messages/messages_state.dart b/lib/features/messages/messages_state.dart
new file mode 100644
index 0000000..c072f26
--- /dev/null
+++ b/lib/features/messages/messages_state.dart
@@ -0,0 +1,9 @@
+sealed class MessagesState {}
+
+class MessagesLoading extends MessagesState {}
+
+class MessagesEmpty extends MessagesState {}
+
+class MessagesLoadSuccess extends MessagesState {}
+
+class MessagesError extends MessagesState {}
diff --git a/lib/features/messages/messages_tab.dart b/lib/features/messages/messages_tab.dart
index 41c7310..24929b7 100644
--- a/lib/features/messages/messages_tab.dart
+++ b/lib/features/messages/messages_tab.dart
@@ -1,10 +1,104 @@
import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:hotelyn/components/app_bar.dart';
+import 'package:hotelyn/components/text_style/hotelyn_text_style.dart';
+import 'package:hotelyn/features/messages/messages_cubit.dart';
+import 'package:hotelyn/features/messages/messages_state.dart';
-class MesssagesTab extends StatelessWidget {
- const MesssagesTab({super.key});
+class MessagesTab extends StatelessWidget {
+ const MessagesTab({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return BlocConsumer(
+ listener: (context, state) {
+ // TODO: implement listener
+ },
+ builder: (context, state) {
+ return Scaffold(
+ appBar: HotelynHomeAppBar(
+ title: 'Messages',
+ iconData: Icons.notifications,
+ onIconPressed: () {},
+ ),
+ body: Builder(
+ builder: (context) {
+ return switch (state) {
+ MessagesLoading() => const MessagesLoadingScreen(),
+ MessagesEmpty() => const MessagesEmptyScreen(),
+ MessagesError() => const MessagesErrorScreen(),
+ MessagesLoadSuccess() => const MessagesLoadSuccessScreen(),
+ };
+ },
+ ),
+ floatingActionButton: state is MessagesEmpty
+ ? FloatingActionButton(
+ onPressed: () {},
+ child: const Icon(Icons.message_rounded),
+ )
+ : null,
+ );
+ },
+ );
+ }
+}
+
+class MessagesLoadSuccessScreen extends StatelessWidget {
+ const MessagesLoadSuccessScreen({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return const Placeholder();
+ }
+}
+
+class MessagesErrorScreen extends StatelessWidget {
+ const MessagesErrorScreen({super.key});
@override
Widget build(BuildContext context) {
return const Placeholder();
}
}
+
+class MessagesLoadingScreen extends StatelessWidget {
+ const MessagesLoadingScreen({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return const Center(
+ child: CircularProgressIndicator(),
+ );
+ }
+}
+
+class MessagesEmptyScreen extends StatelessWidget {
+ const MessagesEmptyScreen({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return Center(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Image.asset(
+ 'assets/images/messages/empty.png',
+ width: 200,
+ height: 200,
+ ),
+ const SizedBox(height: 30),
+ const Text(
+ 'No Messages Here',
+ style: HotelynTextStyle.h1,
+ ),
+ const SizedBox(height: 12),
+ const Text(
+ 'Lets start messaging with others or with seller',
+ style: HotelynTextStyle.description,
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/features/profile/profile_tab.dart b/lib/features/profile/profile_tab.dart
index 784f3c4..37182f5 100644
--- a/lib/features/profile/profile_tab.dart
+++ b/lib/features/profile/profile_tab.dart
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:hotelyn/components/app_bar.dart';
import 'package:hotelyn/components/avatar/hotelyn_avatar.dart';
import 'package:hotelyn/components/text_style/hotelyn_text_style.dart';
import 'package:hotelyn/features/profile/profile_cubit.dart';
@@ -16,7 +17,11 @@ class ProfileTab extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
- appBar: AppBar(),
+ appBar: HotelynHomeAppBar(
+ title: 'Profile',
+ iconData: Icons.settings,
+ onIconPressed: () {},
+ ),
body: BlocBuilder(
builder: (context, state) {
return switch (state) {
@@ -54,7 +59,6 @@ class ProfileDataScreen extends StatelessWidget {
child: SingleChildScrollView(
child: Column(
children: [
- ProfileTabAppBar(),
SizedBox(height: 32),
ProfileUserInformationSection(),
SizedBox(height: 24),
@@ -85,18 +89,3 @@ class ProfileUserInformationSection extends StatelessWidget {
);
}
}
-
-class ProfileTabAppBar extends StatelessWidget {
- const ProfileTabAppBar({super.key});
-
- @override
- Widget build(BuildContext context) {
- return const Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text('Profile', style: HotelynTextStyle.h1),
- Icon(Icons.settings_outlined),
- ],
- );
- }
-}
diff --git a/pubspec.yaml b/pubspec.yaml
index 43fdd51..14e1a3a 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -41,6 +41,7 @@ flutter:
- assets/images/
- assets/images/hotelyn/
- assets/images/onboarding/
+ - assets/images/messages/
- assets/icons/
fonts:
diff --git a/test/features/messages/messages_cubit_test.dart b/test/features/messages/messages_cubit_test.dart
new file mode 100644
index 0000000..d8ce63d
--- /dev/null
+++ b/test/features/messages/messages_cubit_test.dart
@@ -0,0 +1,12 @@
+import 'package:flutter_test/flutter_test.dart';
+import 'package:hotelyn/features/messages/messages_cubit.dart';
+import 'package:hotelyn/features/messages/messages_state.dart';
+
+void main() {
+ group('MessagesCubit', () {
+ test('constructor', () {
+ final cubit = MessagesCubit();
+ expect(cubit.state, isA());
+ });
+ });
+}
diff --git a/test/features/messages/messages_tab_test.dart b/test/features/messages/messages_tab_test.dart
new file mode 100644
index 0000000..3a1a175
--- /dev/null
+++ b/test/features/messages/messages_tab_test.dart
@@ -0,0 +1,84 @@
+import 'package:bloc_test/bloc_test.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:hotelyn/features/messages/messages_cubit.dart';
+import 'package:hotelyn/features/messages/messages_state.dart';
+import 'package:hotelyn/features/messages/messages_tab.dart';
+import 'package:mocktail/mocktail.dart';
+
+class MockMessagesCubit extends Mock implements MessagesCubit {}
+
+void main() {
+ group('MessagesTab::', () {
+ late MessagesCubit cubit;
+
+ setUp(() {
+ cubit = MockMessagesCubit();
+ });
+
+ Future buildWidget(WidgetTester tester) async {
+ await tester.pumpWidget(
+ BlocProvider.value(
+ value: cubit,
+ child: const MaterialApp(
+ home: MessagesTab(),
+ ),
+ ),
+ );
+ }
+
+ testWidgets('Messages tab when state is MessagesEmpty', (tester) async {
+ final state = MessagesEmpty();
+ whenListen(
+ cubit,
+ Stream.fromIterable([]),
+ initialState: state,
+ );
+ await buildWidget(tester);
+ final screenType = find.byType(MessagesEmptyScreen);
+ expect(cubit.state, isA());
+ expect(screenType, findsOneWidget);
+ });
+
+ testWidgets('Messages tab when state is MessagesLoading', (tester) async {
+ final state = MessagesLoading();
+ whenListen(
+ cubit,
+ Stream.fromIterable([]),
+ initialState: state,
+ );
+ await buildWidget(tester);
+ final screenType = find.byType(MessagesLoadingScreen);
+ expect(cubit.state, isA());
+ expect(screenType, findsOneWidget);
+ });
+
+ testWidgets('Messages tab when state is MessagesError', (tester) async {
+ final state = MessagesError();
+ whenListen(
+ cubit,
+ Stream.fromIterable([]),
+ initialState: state,
+ );
+ await buildWidget(tester);
+ final screenType = find.byType(MessagesErrorScreen);
+ expect(cubit.state, isA());
+ expect(screenType, findsOneWidget);
+ });
+
+ testWidgets('Messages tab when state is MessagesLoadSuccess',
+ (tester) async {
+ final state = MessagesLoadSuccess();
+ whenListen(
+ cubit,
+ Stream.fromIterable([]),
+ initialState: state,
+ );
+ await buildWidget(tester);
+ final screenType = find.byType(MessagesLoadSuccessScreen);
+ expect(cubit.state, isA());
+ expect(screenType, findsOneWidget);
+ });
+ });
+}