From 2ac0e09309c75047ba3081c6ea75a020e2cfa7f3 Mon Sep 17 00:00:00 2001 From: pahmiudahgede Date: Thu, 1 May 2025 13:10:48 +0700 Subject: [PATCH] feat: shared preferenced session auth, and customable widget --- lib/core/guide.dart | 62 ++++++++++------- lib/core/router.dart | 15 +++-- .../app/home/components/popular_product.dart | 49 +++++++------- .../app/home/components/section_title.dart | 34 ---------- .../app/home/components/special_offers.dart | 23 +++++-- .../app/requestpick/requestpickup_screen.dart | 3 +- lib/screen/auth/otp_screen.dart | 2 +- lib/screen/launch/onboardingpage_screen.dart | 67 ++++++++++++++----- lib/screen/launch/splash_screen.dart | 21 ++++-- lib/viewmodel/auth_vmod.dart | 1 + lib/widget/appbar.dart | 33 +++++++++ 11 files changed, 193 insertions(+), 117 deletions(-) delete mode 100644 lib/screen/app/home/components/section_title.dart create mode 100644 lib/widget/appbar.dart diff --git a/lib/core/guide.dart b/lib/core/guide.dart index af883ed..d3e3f36 100644 --- a/lib/core/guide.dart +++ b/lib/core/guide.dart @@ -3,15 +3,15 @@ import 'package:google_fonts/google_fonts.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; // =====================color schema===================== -Color whiteColor = const Color(0xffF0F1EA); -Color blackNavyColor = const Color(0xff101010); -Color primaryColor = const Color(0xff018558); -Color secondaryColor = const Color(0xffBDE902); -Color tersierColor = const Color(0xffFEF031); -Color redColor = const Color(0xffFF4438); -Color blueColor = const Color(0xff00B5FF); -Color greyColor = const Color(0xffCBD4E6); -Color greyAbsolutColor = const Color(0xff5C707A); +Color whiteColor = Color(0xffF0F1EA); +Color blackNavyColor = Color(0xff101010); +Color primaryColor = Color(0xff018558); +Color secondaryColor = Color(0xffBDE902); +Color tersierColor = Color(0xffFEF031); +Color redColor = Color(0xffFF4438); +Color blueColor = Color(0xff00B5FF); +Color greyColor = Color(0xffCBD4E6); +Color greyAbsolutColor = Color(0xff5C707A); // =====================font weight===================== FontWeight light = FontWeight.w300; @@ -24,23 +24,29 @@ FontWeight superBold = FontWeight.w900; // =====================text behavior===================== class Tulisan { - static TextStyle heading = GoogleFonts.spaceGrotesk( - fontSize: 24.sp, - fontWeight: bold, - color: blackNavyColor, - ); + static TextStyle heading({Color? color}) { + return GoogleFonts.spaceGrotesk( + fontSize: 24.sp, + fontWeight: FontWeight.bold, + color: color ?? blackNavyColor, + ); + } - static TextStyle body = GoogleFonts.spaceMono( - fontSize: 16.sp, - fontWeight: regular, - color: blackNavyColor, - ); + static TextStyle body({Color? color}) { + return GoogleFonts.spaceMono( + fontSize: 16.sp, + fontWeight: FontWeight.w400, + color: color ?? blackNavyColor, + ); + } - static TextStyle subheading = GoogleFonts.spaceGrotesk( - fontSize: 18.sp, - fontWeight: medium, - color: blackNavyColor, - ); + static TextStyle subheading({Color? color}) { + return GoogleFonts.spaceGrotesk( + fontSize: 18.sp, + fontWeight: FontWeight.w500, + color: color ?? blackNavyColor, + ); + } } // =====================padding custom===================== @@ -68,7 +74,11 @@ class PaddingCustom { double bottom = 0.0, }) { return EdgeInsets.only( - left: left.sp, top: top.sp, right: right.sp, bottom: bottom.sp); + left: left.sp, + top: top.sp, + right: right.sp, + bottom: bottom.sp, + ); } } @@ -81,4 +91,4 @@ class GapCustom { return SizedBox(width: value.w); } } -} \ No newline at end of file +} diff --git a/lib/core/router.dart b/lib/core/router.dart index 14f1eff..06dc796 100644 --- a/lib/core/router.dart +++ b/lib/core/router.dart @@ -1,19 +1,23 @@ import 'package:go_router/go_router.dart'; import 'package:rijig_mobile/core/navigation.dart'; +import 'package:rijig_mobile/screen/app/activity/activity_screen.dart'; +import 'package:rijig_mobile/screen/app/cart/cart_screen.dart'; import 'package:rijig_mobile/screen/app/home/home_screen.dart'; +import 'package:rijig_mobile/screen/app/profil/profil_screen.dart'; import 'package:rijig_mobile/screen/app/requestpick/requestpickup_screen.dart'; -// import 'package:rijig_mobile/screen/auth/login_screen.dart'; +import 'package:rijig_mobile/screen/auth/login_screen.dart'; import 'package:rijig_mobile/screen/auth/otp_screen.dart'; import 'package:rijig_mobile/screen/launch/onboardingpage_screen.dart'; +import 'package:rijig_mobile/screen/launch/splash_screen.dart'; final router = GoRouter( routes: [ - // GoRoute(path: '/', builder: (context, state) => SplashScreen()), - GoRoute(path: '/', builder: (context, state) => NavigationPage()), + GoRoute(path: '/', builder: (context, state) => SplashScreen()), GoRoute( path: '/onboarding', - builder: (context, state) => OnboardongPageScreen(), + builder: (context, state) => OnboardingPageScreen(), ), + GoRoute(path: '/login', builder: (context, state) => LoginScreen()), GoRoute( path: '/navigasi', builder: (context, state) { @@ -29,9 +33,12 @@ final router = GoRouter( }, ), GoRoute(path: '/home', builder: (context, state) => HomeScreen()), + GoRoute(path: '/activity', builder: (context, state) => ActivityScreen()), GoRoute( path: '/requestpickup', builder: (context, state) => RequestPickScreen(), ), + GoRoute(path: '/cart', builder: (context, state) => CartScreen()), + GoRoute(path: '/profil', builder: (context, state) => ProfilScreen()), ], ); diff --git a/lib/screen/app/home/components/popular_product.dart b/lib/screen/app/home/components/popular_product.dart index 58e39e3..a2dfca2 100644 --- a/lib/screen/app/home/components/popular_product.dart +++ b/lib/screen/app/home/components/popular_product.dart @@ -2,8 +2,6 @@ import 'package:flutter/material.dart'; import 'package:rijig_mobile/model/product.dart'; import 'package:rijig_mobile/screen/app/home/components/product_card.dart'; -import 'section_title.dart'; - class PopularProducts extends StatelessWidget { const PopularProducts({super.key}); @@ -13,38 +11,41 @@ class PopularProducts extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 20), - child: SectionTitle( - title: "Popular Products", - // press: () { - // Navigator.pushNamed(context, ProductsScreen.routeName); - // }, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + "Artikel", + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + ), + ], ), ), SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: [ - ...List.generate( - demoProducts.length, - (index) { - if (demoProducts[index].isPopular) { - return Padding( - padding: const EdgeInsets.only(left: 20), - child: ProductCard( - product: demoProducts[index], - onPress: (){}, - ), - ); - } + ...List.generate(demoProducts.length, (index) { + if (demoProducts[index].isPopular) { + return Padding( + padding: const EdgeInsets.only(left: 20), + child: ProductCard( + product: demoProducts[index], + onPress: () {}, + ), + ); + } - return const SizedBox - .shrink(); // here by default width and height is 0 - }, - ), + return const SizedBox.shrink(); // here by default width and height is 0 + }), const SizedBox(width: 20), ], ), - ) + ), ], ); } diff --git a/lib/screen/app/home/components/section_title.dart b/lib/screen/app/home/components/section_title.dart deleted file mode 100644 index 0f6ce83..0000000 --- a/lib/screen/app/home/components/section_title.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:flutter/material.dart'; - -class SectionTitle extends StatelessWidget { - const SectionTitle({ - super.key, - required this.title, - // this.press, - }); - - final String title; - // GestureTapCallback press; - - @override - Widget build(BuildContext context) { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - title, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - color: Colors.black, - ), - ), - TextButton( - onPressed: (){}, - style: TextButton.styleFrom(foregroundColor: Colors.grey), - child: const Text("See more"), - ), - ], - ); - } -} diff --git a/lib/screen/app/home/components/special_offers.dart b/lib/screen/app/home/components/special_offers.dart index 79325ac..b41e850 100644 --- a/lib/screen/app/home/components/special_offers.dart +++ b/lib/screen/app/home/components/special_offers.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; - -import 'section_title.dart'; +import 'package:gap/gap.dart'; class SpecialOffers extends StatelessWidget { const SpecialOffers({super.key}); @@ -11,11 +10,25 @@ class SpecialOffers extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 20), - child: SectionTitle( - title: "Special for you", - // press: () {}, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + "Important!", + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + ), + ], ), + // child: SectionTitle( + // title: "Special for you", + // // press: () {}, + // ), ), + const Gap(15), SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( diff --git a/lib/screen/app/requestpick/requestpickup_screen.dart b/lib/screen/app/requestpick/requestpickup_screen.dart index 88d172e..8255903 100644 --- a/lib/screen/app/requestpick/requestpickup_screen.dart +++ b/lib/screen/app/requestpick/requestpickup_screen.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:rijig_mobile/widget/appbar.dart'; class RequestPickScreen extends StatelessWidget { const RequestPickScreen({super.key}); @@ -6,7 +7,7 @@ class RequestPickScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: Text("Request Pickup")), + appBar: CustomAppBar(judul: "Pilih Sampah"), body: Center( child: ElevatedButton( onPressed: () { diff --git a/lib/screen/auth/otp_screen.dart b/lib/screen/auth/otp_screen.dart index 59765cb..7e99c4f 100644 --- a/lib/screen/auth/otp_screen.dart +++ b/lib/screen/auth/otp_screen.dart @@ -46,7 +46,7 @@ class VerifotpScreen extends StatelessWidget { if (userVM.authModel?.status == 200) { debugPrint("routing ke halaman home"); - router.go('/home'); + router.go('/navigasi'); } } }, diff --git a/lib/screen/launch/onboardingpage_screen.dart b/lib/screen/launch/onboardingpage_screen.dart index aac54d3..e7b55b3 100644 --- a/lib/screen/launch/onboardingpage_screen.dart +++ b/lib/screen/launch/onboardingpage_screen.dart @@ -1,5 +1,7 @@ import 'package:concentric_transition/concentric_transition.dart'; import 'package:flutter/material.dart'; +import 'package:rijig_mobile/core/guide.dart'; +import 'package:rijig_mobile/core/router.dart'; final pages = [ const PageData( @@ -22,8 +24,26 @@ final pages = [ ), ]; -class OnboardongPageScreen extends StatelessWidget { - const OnboardongPageScreen({super.key}); +class OnboardingPageScreen extends StatefulWidget { + const OnboardingPageScreen({super.key}); + + @override + OnboardingPageScreenState createState() => OnboardingPageScreenState(); +} + +class OnboardingPageScreenState extends State { + final PageController _controller = PageController(); + int _currentIndex = 0; + + @override + void initState() { + super.initState(); + _controller.addListener(() { + setState(() { + _currentIndex = _controller.page!.round(); + }); + }); + } @override Widget build(BuildContext context) { @@ -31,24 +51,35 @@ class OnboardongPageScreen extends StatelessWidget { return Scaffold( body: ConcentricPageView( colors: pages.map((p) => p.bgColor).toList(), + pageController: _controller, radius: screenWidth * 0.1, - nextButtonBuilder: - (context) => Padding( - padding: const EdgeInsets.only(left: 3), // visual center - child: Icon(Icons.navigate_next, size: screenWidth * 0.08), - ), - // enable itemcount to disable infinite scroll - // itemCount: pages.length, - // opacityFactor: 2.0, + nextButtonBuilder: (context) { + if (_currentIndex == pages.length - 1) { + return InkWell( + onTap: () => router.go('/login'), + child: Center( + child: Text( + "Go to Login", + style: TextStyle( + fontSize: screenWidth * 0.04, + color: whiteColor, + ), + // textAlign: TextAlign.center, + ), + ), + ); + } + return Padding( + padding: const EdgeInsets.only(left: 3), + child: Icon(Icons.navigate_next, size: screenWidth * 0.08), + ); + }, + itemCount: pages.length, scaleFactor: 2, duration: Duration(milliseconds: 500), - // verticalPosition: 0.7, - // direction: Axis.vertical, - // itemCount: pages.length, - // physics: NeverScrollableScrollPhysics(), itemBuilder: (index) { - final page = pages[index % pages.length]; - return SafeArea(child: _Page(page: page)); + final page = pages[index]; + return SafeArea(child: _Page(page: page, index: index)); }, ), ); @@ -71,12 +102,14 @@ class PageData { class _Page extends StatelessWidget { final PageData page; + final int index; - const _Page({required this.page}); + const _Page({required this.page, required this.index}); @override Widget build(BuildContext context) { final screenHeight = MediaQuery.of(context).size.height; + return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ diff --git a/lib/screen/launch/splash_screen.dart b/lib/screen/launch/splash_screen.dart index c667ec8..bfe9c2a 100644 --- a/lib/screen/launch/splash_screen.dart +++ b/lib/screen/launch/splash_screen.dart @@ -1,15 +1,14 @@ import 'package:flutter/material.dart'; import 'package:rijig_mobile/core/guide.dart'; import 'package:rijig_mobile/core/router.dart'; +import 'package:shared_preferences/shared_preferences.dart'; class SplashScreen extends StatelessWidget { const SplashScreen({super.key}); @override Widget build(BuildContext context) { - Future.delayed(Duration(seconds: 3), () { - router.go('/onboarding'); - }); + _checkLoginStatus(context); return Scaffold( backgroundColor: whiteColor, @@ -21,7 +20,6 @@ class SplashScreen extends StatelessWidget { right: 0, child: Image.asset('assets/image/Go_Ride.png', height: 200), ), - Align( alignment: Alignment.center, child: Padding( @@ -31,7 +29,7 @@ class SplashScreen extends StatelessWidget { style: TextStyle( fontSize: 36, fontWeight: FontWeight.bold, - color: primaryColor, + color: Colors.blue, fontFamily: 'Roboto', ), ), @@ -41,4 +39,17 @@ class SplashScreen extends StatelessWidget { ), ); } + + Future _checkLoginStatus(BuildContext context) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + bool isLoggedIn = prefs.getBool('isLoggedIn') ?? false; + + await Future.delayed(Duration(seconds: 3)); + + if (isLoggedIn) { + router.go('/navigasi'); + } else { + router.go('/onboarding'); + } + } } diff --git a/lib/viewmodel/auth_vmod.dart b/lib/viewmodel/auth_vmod.dart index 302a43f..8125d94 100644 --- a/lib/viewmodel/auth_vmod.dart +++ b/lib/viewmodel/auth_vmod.dart @@ -53,6 +53,7 @@ class UserViewModel extends ChangeNotifier { await prefs.setString('token', response['data']['token']); await prefs.setString('user_id', response['data']['user_id']); await prefs.setString('user_role', response['data']['user_role']); + await prefs.setBool('isLoggedIn', true); debugPrint("berhasil login"); } else { diff --git a/lib/widget/appbar.dart b/lib/widget/appbar.dart new file mode 100644 index 0000000..e64dc16 --- /dev/null +++ b/lib/widget/appbar.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:iconsax_flutter/iconsax_flutter.dart'; +import 'package:rijig_mobile/core/guide.dart'; +import 'package:rijig_mobile/core/router.dart'; + +class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { + final String judul; + + final List? actions; + + const CustomAppBar({super.key, required this.judul, this.actions}); + + @override + Widget build(BuildContext context) { + return AppBar( + titleSpacing: 3, + title: Text(judul, style: Tulisan.subheading(color: whiteColor)), + leading: IconButton( + onPressed: () { + router.pop(); + }, + icon: Icon(Iconsax.arrow_left_3_copy, color: whiteColor, size: 26), + ), + backgroundColor: primaryColor, + elevation: 0, + automaticallyImplyLeading: false, + actions: actions, + ); + } + + @override + Size get preferredSize => const Size.fromHeight(kToolbarHeight); +}