diff --git a/assets/images/welcome_screen/png/First_Onboarding.png b/assets/images/welcome_screen/png/First_Onboarding.png new file mode 100644 index 0000000..44f32a8 Binary files /dev/null and b/assets/images/welcome_screen/png/First_Onboarding.png differ diff --git a/assets/images/welcome_screen/png/Second_Onboarding.png b/assets/images/welcome_screen/png/Second_Onboarding.png new file mode 100644 index 0000000..35de1b0 Binary files /dev/null and b/assets/images/welcome_screen/png/Second_Onboarding.png differ diff --git a/assets/images/welcome_screen/png/Third_Onboarding.png b/assets/images/welcome_screen/png/Third_Onboarding.png new file mode 100644 index 0000000..f2f344e Binary files /dev/null and b/assets/images/welcome_screen/png/Third_Onboarding.png differ diff --git a/assets/images/welcome_screen/svg/Splash_BG.svg b/assets/images/welcome_screen/svg/Splash_BG.svg new file mode 100644 index 0000000..77ce035 --- /dev/null +++ b/assets/images/welcome_screen/svg/Splash_BG.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/assets/images/welcome_screen/svg/Tanami_Capital_Logo.svg b/assets/images/welcome_screen/svg/Tanami_Capital_Logo.svg new file mode 100644 index 0000000..849eb85 --- /dev/null +++ b/assets/images/welcome_screen/svg/Tanami_Capital_Logo.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/welcome_screen/svg/Tanami_Capital_Splash_Logo.svg b/assets/images/welcome_screen/svg/Tanami_Capital_Splash_Logo.svg new file mode 100644 index 0000000..cd943ea --- /dev/null +++ b/assets/images/welcome_screen/svg/Tanami_Capital_Splash_Logo.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/core/routes/route_name.dart b/lib/core/routes/route_name.dart index 3d56a97..ed31c5b 100644 --- a/lib/core/routes/route_name.dart +++ b/lib/core/routes/route_name.dart @@ -11,4 +11,7 @@ class RouteName { //No Internet static const String noInternetScreen = 'noInternet'; + + //Welcome + static const String welcomeScreen = 'welcome'; } diff --git a/lib/core/routes/routes.dart b/lib/core/routes/routes.dart index cfea0f4..eca3876 100644 --- a/lib/core/routes/routes.dart +++ b/lib/core/routes/routes.dart @@ -2,6 +2,7 @@ import 'package:go_router/go_router.dart'; import 'package:tanami_app/core/routes/route_name.dart'; +import 'package:tanami_app/features/welcome/presentation/pages/weclome_screen.dart'; import '../../features/login/presentation/pages/login_screen.dart'; import '../../features/splash/presentation/pages/splash_screen.dart'; @@ -35,6 +36,13 @@ final goRouter = GoRouter( return const LoginScreen(); }, ), + GoRoute( + name: RouteName.welcomeScreen, + path: RouteName.welcomeScreen, + builder: (context, state) { + return const WelcomeScreen(); + }, + ), ]), // GoRoute( diff --git a/lib/core/styles/app_color.dart b/lib/core/styles/app_color.dart new file mode 100644 index 0000000..0de7396 --- /dev/null +++ b/lib/core/styles/app_color.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; + +class AppColor { + //Primary Color + static const Color primaryColor = Color(0xFF002F0F); + + //Secondary Color + + //Welcome Color + static const Color indicatorActiveColor = Color(0xFF002F0F); + static const Color indicatorInactiveColor = Color(0xFFb8c1bb); + + //Common Color + static const Color plainWhite = Color(0xFFFFFFFF); + static const Color darkGreyColor = Color(0xFF343434); +} diff --git a/lib/core/styles/app_images.dart b/lib/core/styles/app_images.dart new file mode 100644 index 0000000..9627984 --- /dev/null +++ b/lib/core/styles/app_images.dart @@ -0,0 +1,19 @@ +class AppImages { + //Splash + static const String splashBg = + "assets/images/welcome_screen/svg/Splash_BG.svg"; + static const String splashLogo = + "assets/images/welcome_screen/svg/Tanami_Capital_Splash_Logo.svg"; + + //Welcome + static const String weclomeLogo = + "assets/images/welcome_screen/svg/Tanami_Capital_Logo.svg"; + static const String firstWelcome = + "assets/images/welcome_screen/png/First_Onboarding.png"; + + static const String secondWelcome = + "assets/images/welcome_screen/png/Second_Onboarding.png"; + + static const String thirdWelcome = + "assets/images/welcome_screen/png/Third_Onboarding.png"; +} diff --git a/lib/core/styles/app_text.dart b/lib/core/styles/app_text.dart new file mode 100644 index 0000000..033f058 --- /dev/null +++ b/lib/core/styles/app_text.dart @@ -0,0 +1,17 @@ +class AppText { + //Splash + static const String splashVersionText = "APP: v."; + static const String splashCopyrightText = '© 2024 Tanami'; + + //Welcome + static const String welcomeTitle1Text = "Tanami offers"; + static const String welcomeTitle2Text = "Invest alongside"; + static const String welcomeTitle3Text = "Start investing today "; + static const String weclomeDescription1Text = + "access to best-in-class, exclusive, global private investments"; + static const String weclomeDescription2Text = + "experienced investment experts with a long-standing track record"; + static const String weclomeDescription3Text = "with only SAR 1,000"; + static const String loginText = "Login In"; + static const String signUpText = "Sign Up"; +} diff --git a/lib/core/utils/connectivity/network_connectivity.dart b/lib/core/utils/connectivity/network_connectivity.dart index e657ca3..9926267 100644 --- a/lib/core/utils/connectivity/network_connectivity.dart +++ b/lib/core/utils/connectivity/network_connectivity.dart @@ -26,7 +26,9 @@ class NetworkConnectivity { if (result[0] == ConnectivityResult.wifi || result[0] == ConnectivityResult.mobile) { - goRouter.pop(true); + if (goRouter.canPop()) { + goRouter.pop(true); + } } else { goRouter.go(RouteName.noInternetScreen); } diff --git a/lib/features/splash/presentation/bloc/app_version/app_version_bloc.dart b/lib/features/splash/presentation/bloc/app_version/app_version_bloc.dart new file mode 100644 index 0000000..741e194 --- /dev/null +++ b/lib/features/splash/presentation/bloc/app_version/app_version_bloc.dart @@ -0,0 +1,29 @@ +import 'package:bloc/bloc.dart'; +import 'package:package_info_plus/package_info_plus.dart'; +import 'app_version_event.dart'; +import 'app_version_state.dart'; + +/// The AppVersionBloc class that handles app version-related events and states. +class AppVersionBloc extends Bloc { + AppVersionBloc() : super(AppVersionInitial()) { + // Define the event handler for the LoadAppVersion event. + on(_onLoadAppVersion); + } + + /// The event handler for the LoadAppVersion event. + Future _onLoadAppVersion( + LoadAppVersion event, Emitter emit) async { + try { + // Get the package info from the PackageInfoPlus package. + final packageInfo = await PackageInfo.fromPlatform(); + + // Extract the app version from the package info. + final version = packageInfo.version; + // Emit the AppVersionLoaded state with the app version. + emit(AppVersionLoaded(version)); + } catch (e) { + // Emit the AppVersionError state with an error message. + emit(const AppVersionError('Failed to fetch app version')); + } + } +} diff --git a/lib/features/splash/presentation/bloc/app_version/app_version_event.dart b/lib/features/splash/presentation/bloc/app_version/app_version_event.dart new file mode 100644 index 0000000..699df4b --- /dev/null +++ b/lib/features/splash/presentation/bloc/app_version/app_version_event.dart @@ -0,0 +1,13 @@ +import 'package:equatable/equatable.dart'; + +/// The AppVersionEvent class that represents app version-related events. +abstract class AppVersionEvent extends Equatable { + const AppVersionEvent(); + + @override + List get props => []; +} + +/// The LoadAppVersion class that represents the LoadAppVersion event. + +class LoadAppVersion extends AppVersionEvent {} diff --git a/lib/features/splash/presentation/bloc/app_version/app_version_state.dart b/lib/features/splash/presentation/bloc/app_version/app_version_state.dart new file mode 100644 index 0000000..60ae9c6 --- /dev/null +++ b/lib/features/splash/presentation/bloc/app_version/app_version_state.dart @@ -0,0 +1,32 @@ +import 'package:equatable/equatable.dart'; + +/// The AppVersionState class that represents app version-related states. +abstract class AppVersionState extends Equatable { + const AppVersionState(); + + @override + List get props => []; +} + +/// The AppVersionInitial class that represents the initial app version state. +class AppVersionInitial extends AppVersionState {} + +/// The AppVersionLoaded class that represents the app version loaded state. +class AppVersionLoaded extends AppVersionState { + final String version; + + const AppVersionLoaded(this.version); + + @override + List get props => [version]; +} + +/// The AppVersionError class that represents the app version error state. +class AppVersionError extends AppVersionState { + final String message; + + const AppVersionError(this.message); + + @override + List get props => [message]; +} diff --git a/lib/features/splash/presentation/bloc/splash/splash_bloc.dart b/lib/features/splash/presentation/bloc/splash/splash_bloc.dart new file mode 100644 index 0000000..a7ca4bb --- /dev/null +++ b/lib/features/splash/presentation/bloc/splash/splash_bloc.dart @@ -0,0 +1,14 @@ +import 'package:bloc/bloc.dart'; +import 'splash_event.dart'; +import 'splash_state.dart'; + +/// The SplashBloc class that handles splash screen-related events and states. +class SplashBloc extends Bloc { + SplashBloc() : super(SplashInitial()) { + /// The event handler for the StartSplashScreenTimer event. + on((event, emit) async { + await Future.delayed(const Duration(seconds: 3)); + emit(SplashCompleted()); + }); + } +} diff --git a/lib/features/splash/presentation/bloc/splash/splash_event.dart b/lib/features/splash/presentation/bloc/splash/splash_event.dart new file mode 100644 index 0000000..5765b0c --- /dev/null +++ b/lib/features/splash/presentation/bloc/splash/splash_event.dart @@ -0,0 +1,12 @@ +import 'package:equatable/equatable.dart'; + +/// The SplashEvent class that represents splash screen-related events. +abstract class SplashEvent extends Equatable { + const SplashEvent(); + + @override + List get props => []; +} + +/// The StartSplashScreenTimer class that represents the StartSplashScreenTimer event. +class StartSplashScreenTimer extends SplashEvent {} diff --git a/lib/features/splash/presentation/bloc/splash/splash_state.dart b/lib/features/splash/presentation/bloc/splash/splash_state.dart new file mode 100644 index 0000000..1b5d1a2 --- /dev/null +++ b/lib/features/splash/presentation/bloc/splash/splash_state.dart @@ -0,0 +1,15 @@ +import 'package:equatable/equatable.dart'; + +/// The SplashState class that represents splash screen-related states. +abstract class SplashState extends Equatable { + const SplashState(); + + @override + List get props => []; +} + +/// The SplashInitial class that represents the initial splash screen state. +class SplashInitial extends SplashState {} + +/// The SplashCompleted class that represents the splash screen completed state. +class SplashCompleted extends SplashState {} diff --git a/lib/features/splash/presentation/pages/splash_layout.dart b/lib/features/splash/presentation/pages/splash_layout.dart new file mode 100644 index 0000000..d7701d4 --- /dev/null +++ b/lib/features/splash/presentation/pages/splash_layout.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +import '../../../../core/styles/app_images.dart'; +import '../widgets/bottom_version_widget.dart'; + +class SplashLayout extends StatelessWidget { + const SplashLayout({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SizedBox( + width: 1.sw, + height: 1.sh, + child: Stack( + children: [ + Positioned.fill( + child: SvgPicture.asset( + AppImages.splashBg, + ), + ), + Positioned.fill( + child: Align( + alignment: Alignment.center, + child: SvgPicture.asset( + AppImages.splashLogo, + ), + ), + ), + const Positioned( + bottom: 0, + child: Align( + alignment: Alignment.bottomCenter, + child: BottomVersionWidget(), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/features/splash/presentation/pages/splash_screen.dart b/lib/features/splash/presentation/pages/splash_screen.dart index da3a703..f93f165 100644 --- a/lib/features/splash/presentation/pages/splash_screen.dart +++ b/lib/features/splash/presentation/pages/splash_screen.dart @@ -1,10 +1,45 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:tanami_app/core/routes/route_name.dart'; +import 'package:tanami_app/core/routes/routes.dart'; + +import '../bloc/app_version/app_version_bloc.dart'; +import '../bloc/app_version/app_version_event.dart'; +import '../bloc/splash/splash_bloc.dart'; +import '../bloc/splash/splash_event.dart'; +import '../bloc/splash/splash_state.dart'; +import 'splash_layout.dart'; class SplashScreen extends StatelessWidget { const SplashScreen({super.key}); @override Widget build(BuildContext context) { - return Scaffold(); + return Scaffold( + body: MultiBlocProvider( + providers: [ + // Add the LoadAppVersion event. + BlocProvider( + create: (_) => AppVersionBloc()..add(LoadAppVersion()), + ), + // Add the StartSplashScreenTimer event. + BlocProvider( + create: (_) => SplashBloc()..add(StartSplashScreenTimer()), + ), + ], + // Set the child of the MultiBlocProvider to a BlocListener widget + child: BlocListener( + // Define the listener function for the BlocListener widget + listener: (context, state) { + // Check if the current state is SplashCompleted + if (state is SplashCompleted) { + // Navigate to the WelcomeScreen using the goRouter + goRouter.goNamed(RouteName.welcomeScreen); + } + }, + child: const SplashLayout(), + ), + ), + ); } } diff --git a/lib/features/splash/presentation/widgets/bottom_version_widget.dart b/lib/features/splash/presentation/widgets/bottom_version_widget.dart new file mode 100644 index 0000000..6c8c9bc --- /dev/null +++ b/lib/features/splash/presentation/widgets/bottom_version_widget.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:tanami_app/core/styles/app_text.dart'; +import '../bloc/app_version/app_version_bloc.dart'; +import '../bloc/app_version/app_version_state.dart'; +import '../../../../shared/components/text_widget.dart'; + +class BottomVersionWidget extends StatelessWidget { + const BottomVersionWidget({super.key}); + + @override + Widget build(BuildContext context) { + // A BlocBuilder widget that builds the UI based on the state of the AppVersionBloc + return BlocBuilder( + builder: (context, state) { + // If the state is AppVersionInitial, display a CircularProgressIndicator + if (state is AppVersionInitial) { + return const CircularProgressIndicator(); + } else if (state is AppVersionLoaded) { + return Container( + width: 0.9.sw, + margin: EdgeInsets.only( + left: 24.w, + bottom: 34.h, + right: 24.w, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // Display the application version text + TextWidget().text12W400( + '${AppText.splashVersionText}${state.version}'), + TextWidget().text12W400(AppText.splashCopyrightText), + ], + )); + // If the state is AppVersionError, display an error message + } else if (state is AppVersionError) { + return Text('Error: ${state.message}'); + } + return Container(); + }, + ); + } +} diff --git a/lib/features/welcome/presentation/bloc/onboarding_bloc.dart b/lib/features/welcome/presentation/bloc/onboarding_bloc.dart new file mode 100644 index 0000000..b2d333f --- /dev/null +++ b/lib/features/welcome/presentation/bloc/onboarding_bloc.dart @@ -0,0 +1,28 @@ +import 'package:bloc/bloc.dart'; +import 'onboarding_event.dart'; +import 'onboarding_state.dart'; + +// A Bloc class that manages the onboarding process +class OnboardingBloc extends Bloc { + int _currentPage = 0; + + OnboardingBloc() : super(OnboardingInitial()) { + // Define the event handler for the NextPage event + on((event, emit) { + _currentPage++; + emit(OnboardingPageChanged(_currentPage)); + }); + + // Define the event handler for the PreviousPage event + on((event, emit) { + _currentPage--; + emit(OnboardingPageChanged(_currentPage)); + }); + + // Define the event handler for the JumpToPage event + on((event, emit) { + _currentPage = event.pageIndex; + emit(OnboardingPageChanged(_currentPage)); + }); + } +} diff --git a/lib/features/welcome/presentation/bloc/onboarding_event.dart b/lib/features/welcome/presentation/bloc/onboarding_event.dart new file mode 100644 index 0000000..4f0993e --- /dev/null +++ b/lib/features/welcome/presentation/bloc/onboarding_event.dart @@ -0,0 +1,24 @@ +import 'package:equatable/equatable.dart'; + +abstract class OnboardingEvent extends Equatable { + const OnboardingEvent(); + + @override + List get props => []; +} + +// The event to navigate to the next page during the onboarding process +class NextPage extends OnboardingEvent {} + +// The event to navigate to the previous page during the onboarding process +class PreviousPage extends OnboardingEvent {} + +// The event to jump to a specific page during the onboarding process +class JumpToPage extends OnboardingEvent { + final int pageIndex; + + const JumpToPage(this.pageIndex); + + @override + List get props => [pageIndex]; +} diff --git a/lib/features/welcome/presentation/bloc/onboarding_state.dart b/lib/features/welcome/presentation/bloc/onboarding_state.dart new file mode 100644 index 0000000..fa6bf3f --- /dev/null +++ b/lib/features/welcome/presentation/bloc/onboarding_state.dart @@ -0,0 +1,22 @@ +import 'package:equatable/equatable.dart'; + +abstract class OnboardingState extends Equatable { + // The base state for the onboarding process + const OnboardingState(); + + @override + List get props => []; +} + +// The initial state of the onboarding process +class OnboardingInitial extends OnboardingState {} + +// The state when the page is changed during the onboarding process +class OnboardingPageChanged extends OnboardingState { + final int currentPage; + + const OnboardingPageChanged(this.currentPage); + + @override + List get props => [currentPage]; +} diff --git a/lib/features/welcome/presentation/pages/weclome_screen.dart b/lib/features/welcome/presentation/pages/weclome_screen.dart new file mode 100644 index 0000000..91286c4 --- /dev/null +++ b/lib/features/welcome/presentation/pages/weclome_screen.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:tanami_app/features/welcome/presentation/pages/welcome_layout.dart'; + +import '../bloc/onboarding_bloc.dart'; + +class WelcomeScreen extends StatelessWidget { + const WelcomeScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + body: MultiBlocProvider( + // Define the providers for the OnboardingBloc and other blocs + providers: [ + BlocProvider( + // Create an instance of the OnboardingBloc + create: (_) => OnboardingBloc(), + ), + ], + child: WelcomeLayout(), + ), + ); + } +} diff --git a/lib/features/welcome/presentation/pages/welcome_layout.dart b/lib/features/welcome/presentation/pages/welcome_layout.dart new file mode 100644 index 0000000..ef4178c --- /dev/null +++ b/lib/features/welcome/presentation/pages/welcome_layout.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../bloc/onboarding_bloc.dart'; +import '../bloc/onboarding_event.dart'; +import '../bloc/onboarding_state.dart'; +import '../widgets/build_page_widget.dart'; + +class WelcomeLayout extends StatelessWidget { + final PageController _pageController = PageController(); + WelcomeLayout({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + body: BlocListener( + // Define the listener for the BlocListener + listener: (context, state) { + // If the state is OnboardingPageChanged, animate to the corresponding page + if (state is OnboardingPageChanged) { + _pageController.animateToPage( + state.currentPage, + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + ); + } + }, + child: PageView.builder( + controller: _pageController, + itemCount: 3, // Number of onboarding pages + itemBuilder: (context, index) { + return buildPage(context, index, _pageController); + }, + onPageChanged: (index) { + // Dispatch the JumpToPage event to update the current page + context.read().add(JumpToPage(index)); + }, + ), + ), + ); + } +} diff --git a/lib/features/welcome/presentation/widgets/build_onboarding_page_widget.dart b/lib/features/welcome/presentation/widgets/build_onboarding_page_widget.dart new file mode 100644 index 0000000..18149e6 --- /dev/null +++ b/lib/features/welcome/presentation/widgets/build_onboarding_page_widget.dart @@ -0,0 +1,54 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:tanami_app/core/styles/app_color.dart'; +import 'package:tanami_app/core/styles/app_images.dart'; +import 'package:gap/gap.dart'; +import 'package:tanami_app/shared/components/text_widget.dart'; + +import 'login_signup_button.dart'; +import 'welcome_indicator.dart'; + +Widget buildOnboardingPage( + BuildContext context, { + required int index, + required String imageAsset, + required String title, + required String description, + bool isLastPage = false, + required PageController pageController, +}) { + return Column( + children: [ + const Gap(80), + SvgPicture.asset(AppImages.weclomeLogo), + Image.asset( + imageAsset, + width: 1.sw, + height: index == 2 ? 380 : 450, + fit: BoxFit.cover, + ), + const Gap(15), + TextWidget().tex22W700( + title, + clr: AppColor.primaryColor, + ), + const Gap(18), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 50, + ), + child: TextWidget().tex15W500( + description, + clr: AppColor.darkGreyColor, + ), + ), + const Gap(40), + welcomeIndicator(pageController), + if (isLastPage) const LoginSignUpButton(), + ], + ); +} diff --git a/lib/features/welcome/presentation/widgets/build_page_widget.dart b/lib/features/welcome/presentation/widgets/build_page_widget.dart new file mode 100644 index 0000000..7ad1f48 --- /dev/null +++ b/lib/features/welcome/presentation/widgets/build_page_widget.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; +import 'package:tanami_app/core/styles/app_images.dart'; +import 'package:tanami_app/core/styles/app_text.dart'; + +import 'build_onboarding_page_widget.dart'; + +Widget buildPage( + BuildContext context, int index, PageController pageController) { + switch (index) { + case 0: + return buildOnboardingPage( + pageController: pageController, + index: index, + context, + imageAsset: AppImages.firstWelcome, + title: AppText.welcomeTitle1Text, + description: AppText.weclomeDescription1Text, + ); + case 1: + return buildOnboardingPage( + pageController: pageController, + index: index, + context, + imageAsset: AppImages.secondWelcome, + title: AppText.welcomeTitle2Text, + description: AppText.weclomeDescription2Text, + ); + case 2: + return buildOnboardingPage( + pageController: pageController, + index: index, + context, + imageAsset: AppImages.thirdWelcome, + title: AppText.welcomeTitle3Text, + description: AppText.weclomeDescription3Text, + isLastPage: true, + ); + default: + return Container(); + } +} diff --git a/lib/features/welcome/presentation/widgets/login_signup_button.dart b/lib/features/welcome/presentation/widgets/login_signup_button.dart new file mode 100644 index 0000000..1f74da4 --- /dev/null +++ b/lib/features/welcome/presentation/widgets/login_signup_button.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:gap/gap.dart'; +import 'package:tanami_app/core/styles/app_color.dart'; +import 'package:tanami_app/shared/components/button_widget.dart'; + +import '../../../../core/styles/app_text.dart'; + +class LoginSignUpButton extends StatelessWidget { + const LoginSignUpButton({super.key}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 40.0), + child: Column( + children: [ + Container( + margin: const EdgeInsets.symmetric( + horizontal: 16, + ), + width: 1.sw, + height: 56.h, + child: ButtonWidget().elevatedBtn( + function: () {}, + text: AppText.signUpText, + clr: AppColor.primaryColor, + ), + ), + const Gap(16), + ButtonWidget().textBtn(function: () {}, text: AppText.loginText) + ], + ), + ); + } +} diff --git a/lib/features/welcome/presentation/widgets/welcome_indicator.dart b/lib/features/welcome/presentation/widgets/welcome_indicator.dart new file mode 100644 index 0000000..fe5aef1 --- /dev/null +++ b/lib/features/welcome/presentation/widgets/welcome_indicator.dart @@ -0,0 +1,17 @@ +import 'package:flutter/cupertino.dart'; +import 'package:smooth_page_indicator/smooth_page_indicator.dart'; + +import '../../../../core/styles/app_color.dart'; + +Widget welcomeIndicator(PageController pageController) { + return SmoothPageIndicator( + controller: pageController, + count: 3, + effect: const ExpandingDotsEffect( + activeDotColor: AppColor.indicatorActiveColor, + dotColor: AppColor.indicatorInactiveColor, + dotHeight: 10, + dotWidth: 10, + ), + ); +} diff --git a/lib/main.dart b/lib/main.dart index 49d51a9..3e6ceb7 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'core/routes/routes.dart'; @@ -17,8 +16,7 @@ Future main() async { // Set the preferred orientations of the device. SystemChrome.setPreferredOrientations([ DeviceOrientation.portraitUp, - ]).then((value) => - runApp(MultiBlocProvider(providers: [], child: const MyApp()))); + ]).then((value) => runApp(const MyApp())); } class MyApp extends StatefulWidget { @@ -29,7 +27,8 @@ class MyApp extends StatefulWidget { } class _MyAppState extends State with WidgetsBindingObserver { - late NetworkConnectivity _networkConnectivity; + final NetworkConnectivity _networkConnectivity = + NetworkConnectivity(onStatusChange: (String) {}); @override void initState() { super.initState(); diff --git a/lib/shared/components/button_widget.dart b/lib/shared/components/button_widget.dart new file mode 100644 index 0000000..c5f4913 --- /dev/null +++ b/lib/shared/components/button_widget.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:tanami_app/core/styles/app_color.dart'; +import 'package:tanami_app/shared/components/text_widget.dart'; + +class ButtonWidget { + //Text Button + Widget textBtn({ + required String text, + required VoidCallback function, + }) { + return TextButton( + onPressed: function, + child: TextWidget().tex14W700(text, + clr: AppColor.darkGreyColor, + textDecoration: TextDecoration.underline), + ); + } + + //Elevated Button + Widget elevatedBtn({ + required String text, + required Color clr, + required VoidCallback function, + }) { + return ElevatedButton( + onPressed: function, + style: ElevatedButton.styleFrom( + backgroundColor: clr, + ), + child: TextWidget().tex14W700( + text, + clr: AppColor.plainWhite, + ), + ); + } +} diff --git a/lib/shared/components/text_widget.dart b/lib/shared/components/text_widget.dart new file mode 100644 index 0000000..0278c92 --- /dev/null +++ b/lib/shared/components/text_widget.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:tanami_app/core/styles/app_color.dart'; + +class TextWidget { + //Text Size 12 + Widget text12W400(String text, {Color? clr}) { + return Text(text, + style: GoogleFonts.dmSans( + fontSize: 12, + fontWeight: FontWeight.w400, + color: clr ?? AppColor.plainWhite)); + } + + //Text Size 14 + Widget tex14W700(String text, {Color? clr, TextDecoration? textDecoration}) { + return Text(text, + textAlign: TextAlign.center, + style: GoogleFonts.dmSans( + fontSize: 14, + fontWeight: FontWeight.w700, + decoration: textDecoration ?? TextDecoration.none, + color: clr ?? AppColor.plainWhite)); + } + + //Text Size 15 + Widget tex15W500(String text, {Color? clr}) { + return Text(text, + textAlign: TextAlign.center, + style: GoogleFonts.dmSans( + fontSize: 15, + fontWeight: FontWeight.w500, + color: clr ?? AppColor.plainWhite)); + } + + //Text Size 22 + Widget tex22W700(String text, {Color? clr}) { + return Text(text, + style: GoogleFonts.dmSans( + fontSize: 22, + fontWeight: FontWeight.w700, + color: clr ?? AppColor.plainWhite)); + } +} diff --git a/pubspec.lock b/pubspec.lock index 8e01f79..52e3a74 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -226,7 +226,7 @@ packages: source: hosted version: "5.4.3+1" equatable: - dependency: transitive + dependency: "direct main" description: name: equatable sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 @@ -925,6 +925,14 @@ packages: description: flutter source: sdk version: "0.0.99" + smooth_page_indicator: + dependency: "direct main" + description: + name: smooth_page_indicator + sha256: "725bc638d5e79df0c84658e1291449996943f93bacbc2cec49963dbbab48d8ae" + url: "https://pub.dev" + source: hosted + version: "1.1.0" source_gen: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 75b923b..0e3e8f0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -26,6 +26,7 @@ dependencies: # State Management bloc: ^8.1.4 flutter_bloc: ^8.1.5 + equatable: ^2.0.5 # Permissions permission_handler: ^11.3.1 @@ -46,6 +47,7 @@ dependencies: # Animation lottie: ^3.1.2 shimmer: ^3.0.0 + smooth_page_indicator: ^1.1.0 # Device Responsiveness gap: ^3.0.1 @@ -77,3 +79,9 @@ dev_dependencies: flutter: uses-material-design: true + + assets: + - assets/images/ + - assets/images/welcome_screen/ + - assets/images/welcome_screen/svg/ + - assets/images/welcome_screen/png/