diff --git a/mobile-v3/assets/icons/chevron-right.svg b/mobile-v3/assets/icons/chevron-right.svg new file mode 100644 index 0000000000..64a8a55b4b --- /dev/null +++ b/mobile-v3/assets/icons/chevron-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/mobile-v3/assets/images/shared/Frame 26085560.svg b/mobile-v3/assets/images/shared/Frame 26085560.svg new file mode 100644 index 0000000000..7f82ee3633 --- /dev/null +++ b/mobile-v3/assets/images/shared/Frame 26085560.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile-v3/lib/main.dart b/mobile-v3/lib/main.dart index cba73c46c4..bb412fb5f1 100644 --- a/mobile-v3/lib/main.dart +++ b/mobile-v3/lib/main.dart @@ -16,6 +16,7 @@ import 'package:airqo/src/app/other/places/repository/google_places_repository.d import 'package:airqo/src/app/other/theme/bloc/theme_bloc.dart'; import 'package:airqo/src/app/other/theme/repository/theme_repository.dart'; import 'package:airqo/src/app/profile/bloc/user_bloc.dart'; +import 'package:airqo/src/app/profile/pages/guest_profile%20page.dart'; import 'package:airqo/src/app/profile/repository/user_repository.dart'; import 'package:airqo/src/app/shared/bloc/connectivity_bloc.dart'; import 'package:airqo/src/app/shared/pages/nav_page.dart'; diff --git a/mobile-v3/lib/src/app/auth/bloc/auth_bloc.dart b/mobile-v3/lib/src/app/auth/bloc/auth_bloc.dart index 82bcb66c7c..65bb2f3c6e 100644 --- a/mobile-v3/lib/src/app/auth/bloc/auth_bloc.dart +++ b/mobile-v3/lib/src/app/auth/bloc/auth_bloc.dart @@ -41,6 +41,8 @@ class AuthBloc extends Bloc { ), ); } + } else if (event is UseAsGuest) { + emit(GuestUser()); } }); } diff --git a/mobile-v3/lib/src/app/auth/bloc/auth_event.dart b/mobile-v3/lib/src/app/auth/bloc/auth_event.dart index da4d0641f3..64f93b4e9c 100644 --- a/mobile-v3/lib/src/app/auth/bloc/auth_event.dart +++ b/mobile-v3/lib/src/app/auth/bloc/auth_event.dart @@ -19,3 +19,7 @@ class RegisterUser extends AuthEvent { const RegisterUser(this.model); } + +class UseAsGuest extends AuthEvent { + const UseAsGuest(); +} diff --git a/mobile-v3/lib/src/app/auth/bloc/auth_state.dart b/mobile-v3/lib/src/app/auth/bloc/auth_state.dart index 60f94cc6c4..4acd39f900 100644 --- a/mobile-v3/lib/src/app/auth/bloc/auth_state.dart +++ b/mobile-v3/lib/src/app/auth/bloc/auth_state.dart @@ -24,3 +24,6 @@ class AuthLoadingError extends AuthState { } enum AuthPurpose { LOGIN, REGISTER } + +final class GuestUser extends AuthState {} + diff --git a/mobile-v3/lib/src/app/auth/pages/welcome_screen.dart b/mobile-v3/lib/src/app/auth/pages/welcome_screen.dart index 2e5febe4c1..b19faad3f0 100644 --- a/mobile-v3/lib/src/app/auth/pages/welcome_screen.dart +++ b/mobile-v3/lib/src/app/auth/pages/welcome_screen.dart @@ -5,8 +5,13 @@ import 'package:airqo/src/app/auth/widgets/know_your_air.dart'; import 'package:airqo/src/app/auth/widgets/welcome_widget.dart'; import 'package:airqo/src/meta/utils/colors.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/flutter_svg.dart'; import 'package:smooth_page_indicator/smooth_page_indicator.dart'; +import '../../shared/pages/nav_page.dart'; +import '../bloc/auth_bloc.dart'; + class WelcomeScreen extends StatefulWidget { const WelcomeScreen({super.key}); @@ -38,101 +43,126 @@ class _WelcomeScreenState extends State { @override Widget build(BuildContext context) { - return Scaffold( - body: Column( - children: [ - Stack( - children: [ - SizedBox( - height: MediaQuery.of(context).size.height * 0.6, - child: PageView( - controller: controller, - onPageChanged: changeIndex, - children: [ - WelcomeWidget(), - BreatheClean(), - KnowYourAir(), - ], + return BlocListener( + listener: (context, state) { + if (state is GuestUser) { + Future.microtask(() => Navigator.of(context).pushReplacement( + MaterialPageRoute(builder: (context) => NavPage()), + )); + } + }, + child: Scaffold( + body: Column( + children: [ + Stack( + children: [ + SizedBox( + height: MediaQuery.of(context).size.height * 0.6, + child: PageView( + controller: controller, + onPageChanged: changeIndex, + children: [ + WelcomeWidget(), + BreatheClean(), + KnowYourAir(), + ], + ), ), - ), - Container( - width: double.infinity, - height: MediaQuery.of(context).size.height * 0.6, - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - AnimatedSmoothIndicator( - activeIndex: currentIndex, - count: 3, - effect: ExpandingDotsEffect( - dotWidth: 7, - radius: 7, - dotColor: Color(0xff60646C), - // activeDotColor: Color(0xffF6F6F7), - activeDotColor: AppColors.primaryColor, - dotHeight: 7), - ), - SizedBox(height: 16) - ], + Container( + width: double.infinity, + height: MediaQuery.of(context).size.height * 0.6, + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + AnimatedSmoothIndicator( + activeIndex: currentIndex, + count: 3, + effect: ExpandingDotsEffect( + dotWidth: 7, + radius: 7, + dotColor: Color(0xff60646C), + // activeDotColor: Color(0xffF6F6F7), + activeDotColor: AppColors.primaryColor, + dotHeight: 7), + ), + SizedBox(height: 16) + ], + ), ), - ), - ], - ), - SizedBox( - height: MediaQuery.of(context).size.height * 0.4, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16 * 2), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - InkWell( - onTap: () => Navigator.of(context).push(MaterialPageRoute( - builder: (context) => CreateAccountScreen())), - child: Container( - height: 56, - decoration: BoxDecoration( - color: AppColors.primaryColor, - borderRadius: BorderRadius.circular(4)), - child: Center( - child: Text( - "Create Account", - style: TextStyle( - fontWeight: FontWeight.w500, - color: Colors.white, + ], + ), + SizedBox( + height: MediaQuery.of(context).size.height * 0.4, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16 * 2), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + InkWell( + onTap: () => Navigator.of(context).push(MaterialPageRoute( + builder: (context) => CreateAccountScreen())), + child: Container( + height: 56, + decoration: BoxDecoration( + color: AppColors.primaryColor, + borderRadius: BorderRadius.circular(4)), + child: Center( + child: Text( + "Create Account", + style: TextStyle( + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + ), + ), + ), + SizedBox(height: 18), + InkWell( + onTap: () => Navigator.of(context).push( + MaterialPageRoute(builder: (context) => LoginPage())), + child: Container( + height: 56, + decoration: BoxDecoration( + color: Theme.of(context).brightness == Brightness.dark + ? Colors.white + : Theme.of(context).highlightColor, + borderRadius: BorderRadius.circular(4)), + child: Center( + child: Text( + "Login Here", + style: TextStyle( + fontWeight: FontWeight.w500, color: Colors.black), ), ), ), ), - ), - SizedBox(height: 16), - InkWell( - onTap: () => Navigator.of(context).push( - MaterialPageRoute(builder: (context) => LoginPage())), - child: Container( - height: 56, - decoration: BoxDecoration( - color: Theme.of(context).brightness == Brightness.dark - ? Colors.white - : Theme.of(context).highlightColor, - borderRadius: BorderRadius.circular(4)), + SizedBox(height: 18), + + InkWell( + onTap: () =>context.read().add(UseAsGuest()), child: Center( - child: Text( - "Login Here", - style: TextStyle( - fontWeight: FontWeight.w500, color: Colors.black), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text("Continue as guest ", + style: TextStyle( + color: AppColors.boldHeadlineColor2, + fontWeight: FontWeight.w500)), + SvgPicture.asset('assets/icons/chevron-right.svg', + height: 16.0, + width: 16.0, + color: AppColors.boldHeadlineColor2,) + ], ), ), ), - ), - SizedBox(height: 16), - Text( - "", - style: TextStyle(color: AppColors.boldHeadlineColor), - ) - ], - ), - )) - ], - )); + + ], + ), + )) + ], + )), + ); } } diff --git a/mobile-v3/lib/src/app/dashboard/pages/dashboard_page.dart b/mobile-v3/lib/src/app/dashboard/pages/dashboard_page.dart index c9c4a77146..e22c235614 100644 --- a/mobile-v3/lib/src/app/dashboard/pages/dashboard_page.dart +++ b/mobile-v3/lib/src/app/dashboard/pages/dashboard_page.dart @@ -1,3 +1,4 @@ +import 'package:airqo/src/app/auth/bloc/auth_bloc.dart'; import 'package:airqo/src/app/dashboard/bloc/dashboard/dashboard_bloc.dart'; import 'package:airqo/src/app/dashboard/widgets/analytics_card.dart'; import 'package:airqo/src/app/dashboard/widgets/countries_chip.dart'; @@ -14,6 +15,8 @@ import 'package:flutter_sticky_header/flutter_sticky_header.dart'; import 'package:flutter_svg/svg.dart'; import 'package:intl/intl.dart'; +import '../../profile/pages/guest_profile page.dart'; +import '../../profile/pages/profile_page.dart'; import '../models/airquality_response.dart'; class DashboardPage extends StatefulWidget { @@ -27,6 +30,7 @@ class _DashboardPageState extends State { DashboardBloc? dashboardBloc; UserBloc? userBloc; ThemeBloc? themeBloc; + AuthBloc? authBloc; List filteredMeasurements = []; String currentFilter = "All"; @@ -38,6 +42,12 @@ class _DashboardPageState extends State { themeBloc = context.read(); userBloc = context.read()..add(LoadUser()); + + final authState = context.read().state; + if (authState is AuthInitial || authState is GuestUser) { + // Only set guest mode if no user is authenticated + context.read().add(UseAsGuest()); + } super.initState(); } @@ -96,33 +106,69 @@ class _DashboardPageState extends State { ), SizedBox(width: 8), GestureDetector( - // onTap: () => Navigator.of(context).push( - // MaterialPageRoute( - // builder: (context) { - // return ProfilePage(); - // }, - // ), - // ), - child: BlocBuilder( - builder: (context, state) { - if (state is UserLoaded) { - String firstName = - state.model.users[0].firstName[0].toUpperCase(); - String lastName = - state.model.users[0].lastName[0].toUpperCase(); - return CircleAvatar( - child: Center(child: Text("${firstName}${lastName}")), - radius: 24, - backgroundColor: Theme.of(context).highlightColor, - ); - } else if (state is UserLoadingError) { - return Container(); - } else { - return ShimmerContainer( - height: 44, borderRadius: 1000, width: 44); - } - }, - ), + onTap: () { + final authState = context.read().state; + if (authState is GuestUser) { + + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => GuestProfilePage(), + ), + ); + } else { + // Navigate to the regular profile page + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => ProfilePage(), + ), + ); + } + }, + child: BlocBuilder( + builder: (context, authState) { + if (authState is GuestUser) { + // Display default avatar for guest users + return Container( + //margin: const EdgeInsets.symmetric(horizontal: 20), + child: CircleAvatar( + backgroundColor: Theme.of(context).highlightColor, + + child: Center( + child: SvgPicture.asset( + "assets/icons/user_icon.svg", + height:22, + width: 22, + ), + ), + radius: 24, + ), + ); + } else { + return BlocBuilder( + builder: (context, userState) { + if (userState is UserLoaded) { + String firstName = userState.model.users[0].firstName[0].toUpperCase(); + String lastName = userState.model.users[0].lastName[0].toUpperCase(); + return CircleAvatar( + child: Center(child: Text("${firstName}${lastName}")), + radius: 24, + backgroundColor: Theme.of(context).highlightColor, + ); + } else if (userState is UserLoadingError) { + return Container(); // Handle error state (optional) + } else { + return ShimmerContainer( + height: 44, + borderRadius: 1000, + width: 44, + ); + } + }, + ); + } + }, + ) + ), SizedBox(width: 8), ], @@ -138,26 +184,44 @@ class _DashboardPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 16), - BlocBuilder( - builder: (context, state) { - if (state is UserLoaded) { + BlocBuilder( + builder: (context, authState) { + if (authState is GuestUser) { return Text( - "Hello ${state.model.users[0].firstName} πŸ‘‹πŸΌ", + "Hi, Guest πŸ‘‹πŸΌ", style: TextStyle( - fontSize: 28, - fontWeight: FontWeight.w700, - color: AppColors.boldHeadlineColor), - ); - } - return Text( - "Hello πŸ‘‹πŸΌ", - style: TextStyle( fontSize: 28, fontWeight: FontWeight.w700, - color: AppColors.boldHeadlineColor), - ); + color: AppColors.boldHeadlineColor2, + ), + ); + } else { + return BlocBuilder( + builder: (context, userState) { + if (userState is UserLoaded) { + return Text( + "Hi ${userState.model.users[0].firstName} πŸ‘‹πŸΌ", + style: TextStyle( + fontSize: 28, + fontWeight: FontWeight.w700, + color: AppColors.boldHeadlineColor, + ), + ); + } + return Text( + "Hi,πŸ‘‹πŸΌ", + style: TextStyle( + fontSize: 28, + fontWeight: FontWeight.w700, + color: AppColors.boldHeadlineColor, + ), + ); + }, + ); + } }, ), + Text( "Today’s Air Quality β€’ ${DateFormat.MMMMd().format(DateTime.now())}", style: TextStyle( diff --git a/mobile-v3/lib/src/app/dashboard/widgets/analytics_card.dart b/mobile-v3/lib/src/app/dashboard/widgets/analytics_card.dart index 6613987f3a..6ad860b629 100644 --- a/mobile-v3/lib/src/app/dashboard/widgets/analytics_card.dart +++ b/mobile-v3/lib/src/app/dashboard/widgets/analytics_card.dart @@ -58,13 +58,16 @@ class AnalyticsCard extends StatelessWidget { ? measurement.pm25!.value! .toStringAsFixed(2) : "-", - style: Theme.of(context).textTheme.titleLarge + style: TextStyle( + fontWeight: FontWeight.w700, + fontSize: 40, + color: AppColors.boldHeadlineColor2), ), Text(" ΞΌg/m3", style: TextStyle( fontWeight: FontWeight.w600, fontSize: 20, - color: AppColors.secondaryHeadlineColor)) + color: AppColors.boldHeadlineColor2)) ]), ]), SizedBox( @@ -96,12 +99,12 @@ class AnalyticsCard extends StatelessWidget { style: TextStyle( fontSize: 28, fontWeight: FontWeight.w700, - color: AppColors.boldHeadlineColor)), + color: AppColors.secondaryHeadlineColor3)), Text(measurement.healthTips![0].description!, style: TextStyle( fontSize: 20, fontWeight: FontWeight.w600, - color: AppColors.secondaryHeadlineColor)) + color: AppColors.secondaryHeadlineColor2)) ], ), ), diff --git a/mobile-v3/lib/src/app/profile/pages/guest_profile page.dart b/mobile-v3/lib/src/app/profile/pages/guest_profile page.dart new file mode 100644 index 0000000000..46dfe2fa93 --- /dev/null +++ b/mobile-v3/lib/src/app/profile/pages/guest_profile page.dart @@ -0,0 +1,154 @@ +import 'package:airqo/src/app/profile/pages/widgets/guest_settings_widget.dart'; +//import 'package:airqo/src/app/profile/pages/widgets/settings_tile.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; + +import '../../../meta/utils/colors.dart'; +import '../../auth/pages/register_page.dart'; +import '../../shared/widgets/spinner.dart'; + +class GuestProfilePage extends StatefulWidget { + const GuestProfilePage({super.key}); + + @override + State createState() => _GuestProfilePageState(); +} + +class _GuestProfilePageState extends State { + // Declare a loading state variable + bool isLoading = false; + + void handleCreateAccount() async { + setState(() { + isLoading = true; // Set loading to true + }); + + // Simulate a delay for account creation or call an API here + await Future.delayed(Duration(seconds: 2)); + + setState(() { + isLoading = false; // Set loading to false once done + }); + + // Navigate to CreateAccountScreen or handle success + Navigator.of(context).push(MaterialPageRoute(builder: (context) => CreateAccountScreen())); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + automaticallyImplyLeading: false, + actions: [ + IconButton( + onPressed: () => Navigator.pop(context), + icon: Icon(Icons.close)), + SizedBox(width: 16) + ], + ), + body: SingleChildScrollView( + child: Container( + padding: const EdgeInsets.only(left: 16, right: 16, top: 8), + child: Column( + + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + + Container( + margin: const EdgeInsets.symmetric(horizontal: 20), + child: CircleAvatar( + backgroundColor: Theme.of(context).highlightColor, + child: Center( + child: SvgPicture.asset( + "assets/icons/user_icon.svg"), + ), + radius: 50, + ), + ), + + + Text( + "Guest User", + style: TextStyle( + color: AppColors.boldHeadlineColor2, + fontSize: 24, + fontWeight: FontWeight.w700, + ), + ), + ],), + SizedBox(height: 32), + Container( + margin: const EdgeInsets.symmetric(horizontal: 20), + child: Text( + 'Settings', + style:TextStyle( + color: AppColors.boldHeadlineColor2, + fontSize: 18, + fontWeight: FontWeight.w500, + ), + + ), + ), + SizedBox( + height: 5, + ), + + Divider( + color:Theme.of(context).highlightColor, + indent: 20, + ), + + + + + GuestSettingsWidget(), + + + + SizedBox( + height: 18, + ), + InkWell( + onTap: () => Navigator.of(context).push(MaterialPageRoute( + builder: (context) => CreateAccountScreen())), + child: Container( + height: 56, + decoration: BoxDecoration( + color: AppColors.primaryColor, + borderRadius: + BorderRadius.circular(4)), + child: Center( + child: isLoading + ? Spinner() + : Text( + "Create Account", + style: TextStyle( + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + ), + ), + ), + SizedBox( + height: 76, + ), + + Center( + child: SvgPicture.asset('assets/images/shared/Frame 26085560.svg') + ), + SizedBox( + height: 220, + ) + ], + ), + ), + ), + ); + } +} + diff --git a/mobile-v3/lib/src/app/profile/pages/widgets/guest_settings_widget.dart b/mobile-v3/lib/src/app/profile/pages/widgets/guest_settings_widget.dart new file mode 100644 index 0000000000..4104643366 --- /dev/null +++ b/mobile-v3/lib/src/app/profile/pages/widgets/guest_settings_widget.dart @@ -0,0 +1,56 @@ +import 'package:airqo/src/app/profile/pages/widgets/settings_tile.dart'; +import 'package:flutter/material.dart'; + +class GuestSettingsWidget extends StatelessWidget { + const GuestSettingsWidget({super.key}); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + SizedBox(height: 8), + SettingsTile( + switchValue: true, + iconPath: "assets/images/shared/location_icon.svg", + title: "Location", + onChanged: (value) { + print(value); + }, + description: + "AirQo to use your precise location to locate the Air Quality of your nearest location"), + SettingsTile( + + iconPath: "assets/icons/notification.svg", + title: "Notifications", + onChanged: (value) { + print(value); + }, + description: + "Create an account to get air quality alerts"), + SettingsTile( + iconPath: "assets/images/shared/feedback_icon.svg", + title: "Send Feedback", + onChanged: (value) { + print(value); + }, + ), + SettingsTile( + iconPath: "assets/images/shared/airqo_story_icon.svg", + title: "Our Story", + onChanged: (value) { + print(value); + }, + ), + + SettingsTile( + iconPath: "assets/images/shared/terms_and_privacy.svg", + title: "Terms and Privacy Policy", + onChanged: (value) { + print(value); + }, + ), + + ], + ); + } +} diff --git a/mobile-v3/lib/src/app/profile/pages/widgets/settings_tile.dart b/mobile-v3/lib/src/app/profile/pages/widgets/settings_tile.dart index e1eae5ea06..4911dbec69 100644 --- a/mobile-v3/lib/src/app/profile/pages/widgets/settings_tile.dart +++ b/mobile-v3/lib/src/app/profile/pages/widgets/settings_tile.dart @@ -38,7 +38,9 @@ class SettingsTile extends StatelessWidget { subtitle: description != null ? Text(description!, style: TextStyle( - color: AppColors.secondaryHeadlineColor, fontSize: 13)) + color: AppColors.secondaryHeadlineColor2, + fontSize: 14, + fontWeight: FontWeight.w400)) : null, leading: Column( mainAxisAlignment: MainAxisAlignment.start, @@ -55,11 +57,11 @@ class SettingsTile extends StatelessWidget { title: Text(title, style: TextStyle( fontSize: 18, - fontWeight: FontWeight.w700, - color: AppColors.boldHeadlineColor)), + fontWeight: FontWeight.w500, + color: AppColors.secondaryHeadlineColor3)), ), Divider( - color: Theme.of(context).highlightColor, + color:Theme.of(context).highlightColor, indent: 80, ) ], diff --git a/mobile-v3/lib/src/meta/utils/colors.dart b/mobile-v3/lib/src/meta/utils/colors.dart index 443047ec49..03cae8558e 100644 --- a/mobile-v3/lib/src/meta/utils/colors.dart +++ b/mobile-v3/lib/src/meta/utils/colors.dart @@ -14,6 +14,10 @@ class AppColors { static Color highlightColor = Color(0xffF3F6F8); static Color boldHeadlineColor = Color(0xff6F87A1); static Color secondaryHeadlineColor = Color(0xff6F87A1); + static Color boldHeadlineColor2 = Color(0xff9EA3AA); + static Color secondaryHeadlineColor2 = Color(0xff60646C); + static Color secondaryHeadlineColor3 = Color(0xff7A7F87); + } class AppTheme {