feat: shared preferenced session auth, and customable widget

This commit is contained in:
pahmiudahgede 2025-05-01 13:10:48 +07:00
parent fbde7c0ba6
commit 2ac0e09309
11 changed files with 193 additions and 117 deletions

View File

@ -3,15 +3,15 @@ import 'package:google_fonts/google_fonts.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
// =====================color schema===================== // =====================color schema=====================
Color whiteColor = const Color(0xffF0F1EA); Color whiteColor = Color(0xffF0F1EA);
Color blackNavyColor = const Color(0xff101010); Color blackNavyColor = Color(0xff101010);
Color primaryColor = const Color(0xff018558); Color primaryColor = Color(0xff018558);
Color secondaryColor = const Color(0xffBDE902); Color secondaryColor = Color(0xffBDE902);
Color tersierColor = const Color(0xffFEF031); Color tersierColor = Color(0xffFEF031);
Color redColor = const Color(0xffFF4438); Color redColor = Color(0xffFF4438);
Color blueColor = const Color(0xff00B5FF); Color blueColor = Color(0xff00B5FF);
Color greyColor = const Color(0xffCBD4E6); Color greyColor = Color(0xffCBD4E6);
Color greyAbsolutColor = const Color(0xff5C707A); Color greyAbsolutColor = Color(0xff5C707A);
// =====================font weight===================== // =====================font weight=====================
FontWeight light = FontWeight.w300; FontWeight light = FontWeight.w300;
@ -24,23 +24,29 @@ FontWeight superBold = FontWeight.w900;
// =====================text behavior===================== // =====================text behavior=====================
class Tulisan { class Tulisan {
static TextStyle heading = GoogleFonts.spaceGrotesk( static TextStyle heading({Color? color}) {
fontSize: 24.sp, return GoogleFonts.spaceGrotesk(
fontWeight: bold, fontSize: 24.sp,
color: blackNavyColor, fontWeight: FontWeight.bold,
); color: color ?? blackNavyColor,
);
}
static TextStyle body = GoogleFonts.spaceMono( static TextStyle body({Color? color}) {
fontSize: 16.sp, return GoogleFonts.spaceMono(
fontWeight: regular, fontSize: 16.sp,
color: blackNavyColor, fontWeight: FontWeight.w400,
); color: color ?? blackNavyColor,
);
}
static TextStyle subheading = GoogleFonts.spaceGrotesk( static TextStyle subheading({Color? color}) {
fontSize: 18.sp, return GoogleFonts.spaceGrotesk(
fontWeight: medium, fontSize: 18.sp,
color: blackNavyColor, fontWeight: FontWeight.w500,
); color: color ?? blackNavyColor,
);
}
} }
// =====================padding custom===================== // =====================padding custom=====================
@ -68,7 +74,11 @@ class PaddingCustom {
double bottom = 0.0, double bottom = 0.0,
}) { }) {
return EdgeInsets.only( 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); return SizedBox(width: value.w);
} }
} }
} }

View File

@ -1,19 +1,23 @@
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:rijig_mobile/core/navigation.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/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/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/auth/otp_screen.dart';
import 'package:rijig_mobile/screen/launch/onboardingpage_screen.dart'; import 'package:rijig_mobile/screen/launch/onboardingpage_screen.dart';
import 'package:rijig_mobile/screen/launch/splash_screen.dart';
final router = GoRouter( final router = GoRouter(
routes: [ routes: [
// GoRoute(path: '/', builder: (context, state) => SplashScreen()), GoRoute(path: '/', builder: (context, state) => SplashScreen()),
GoRoute(path: '/', builder: (context, state) => NavigationPage()),
GoRoute( GoRoute(
path: '/onboarding', path: '/onboarding',
builder: (context, state) => OnboardongPageScreen(), builder: (context, state) => OnboardingPageScreen(),
), ),
GoRoute(path: '/login', builder: (context, state) => LoginScreen()),
GoRoute( GoRoute(
path: '/navigasi', path: '/navigasi',
builder: (context, state) { builder: (context, state) {
@ -29,9 +33,12 @@ final router = GoRouter(
}, },
), ),
GoRoute(path: '/home', builder: (context, state) => HomeScreen()), GoRoute(path: '/home', builder: (context, state) => HomeScreen()),
GoRoute(path: '/activity', builder: (context, state) => ActivityScreen()),
GoRoute( GoRoute(
path: '/requestpickup', path: '/requestpickup',
builder: (context, state) => RequestPickScreen(), builder: (context, state) => RequestPickScreen(),
), ),
GoRoute(path: '/cart', builder: (context, state) => CartScreen()),
GoRoute(path: '/profil', builder: (context, state) => ProfilScreen()),
], ],
); );

View File

@ -2,8 +2,6 @@ import 'package:flutter/material.dart';
import 'package:rijig_mobile/model/product.dart'; import 'package:rijig_mobile/model/product.dart';
import 'package:rijig_mobile/screen/app/home/components/product_card.dart'; import 'package:rijig_mobile/screen/app/home/components/product_card.dart';
import 'section_title.dart';
class PopularProducts extends StatelessWidget { class PopularProducts extends StatelessWidget {
const PopularProducts({super.key}); const PopularProducts({super.key});
@ -13,38 +11,41 @@ class PopularProducts extends StatelessWidget {
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 20), padding: const EdgeInsets.symmetric(horizontal: 20),
child: SectionTitle( child: Row(
title: "Popular Products", mainAxisAlignment: MainAxisAlignment.start,
// press: () { children: [
// Navigator.pushNamed(context, ProductsScreen.routeName); Text(
// }, "Artikel",
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.black,
),
),
],
), ),
), ),
SingleChildScrollView( SingleChildScrollView(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
child: Row( child: Row(
children: [ children: [
...List.generate( ...List.generate(demoProducts.length, (index) {
demoProducts.length, if (demoProducts[index].isPopular) {
(index) { return Padding(
if (demoProducts[index].isPopular) { padding: const EdgeInsets.only(left: 20),
return Padding( child: ProductCard(
padding: const EdgeInsets.only(left: 20), product: demoProducts[index],
child: ProductCard( onPress: () {},
product: demoProducts[index], ),
onPress: (){}, );
), }
);
}
return const SizedBox return const SizedBox.shrink(); // here by default width and height is 0
.shrink(); // here by default width and height is 0 }),
},
),
const SizedBox(width: 20), const SizedBox(width: 20),
], ],
), ),
) ),
], ],
); );
} }

View File

@ -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"),
),
],
);
}
}

View File

@ -1,6 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'section_title.dart';
class SpecialOffers extends StatelessWidget { class SpecialOffers extends StatelessWidget {
const SpecialOffers({super.key}); const SpecialOffers({super.key});
@ -11,11 +10,25 @@ class SpecialOffers extends StatelessWidget {
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 20), padding: const EdgeInsets.symmetric(horizontal: 20),
child: SectionTitle( child: Row(
title: "Special for you", mainAxisAlignment: MainAxisAlignment.start,
// press: () {}, 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( SingleChildScrollView(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
child: Row( child: Row(

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:rijig_mobile/widget/appbar.dart';
class RequestPickScreen extends StatelessWidget { class RequestPickScreen extends StatelessWidget {
const RequestPickScreen({super.key}); const RequestPickScreen({super.key});
@ -6,7 +7,7 @@ class RequestPickScreen extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar(title: Text("Request Pickup")), appBar: CustomAppBar(judul: "Pilih Sampah"),
body: Center( body: Center(
child: ElevatedButton( child: ElevatedButton(
onPressed: () { onPressed: () {

View File

@ -46,7 +46,7 @@ class VerifotpScreen extends StatelessWidget {
if (userVM.authModel?.status == 200) { if (userVM.authModel?.status == 200) {
debugPrint("routing ke halaman home"); debugPrint("routing ke halaman home");
router.go('/home'); router.go('/navigasi');
} }
} }
}, },

View File

@ -1,5 +1,7 @@
import 'package:concentric_transition/concentric_transition.dart'; import 'package:concentric_transition/concentric_transition.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:rijig_mobile/core/guide.dart';
import 'package:rijig_mobile/core/router.dart';
final pages = [ final pages = [
const PageData( const PageData(
@ -22,8 +24,26 @@ final pages = [
), ),
]; ];
class OnboardongPageScreen extends StatelessWidget { class OnboardingPageScreen extends StatefulWidget {
const OnboardongPageScreen({super.key}); const OnboardingPageScreen({super.key});
@override
OnboardingPageScreenState createState() => OnboardingPageScreenState();
}
class OnboardingPageScreenState extends State<OnboardingPageScreen> {
final PageController _controller = PageController();
int _currentIndex = 0;
@override
void initState() {
super.initState();
_controller.addListener(() {
setState(() {
_currentIndex = _controller.page!.round();
});
});
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -31,24 +51,35 @@ class OnboardongPageScreen extends StatelessWidget {
return Scaffold( return Scaffold(
body: ConcentricPageView( body: ConcentricPageView(
colors: pages.map((p) => p.bgColor).toList(), colors: pages.map((p) => p.bgColor).toList(),
pageController: _controller,
radius: screenWidth * 0.1, radius: screenWidth * 0.1,
nextButtonBuilder: nextButtonBuilder: (context) {
(context) => Padding( if (_currentIndex == pages.length - 1) {
padding: const EdgeInsets.only(left: 3), // visual center return InkWell(
child: Icon(Icons.navigate_next, size: screenWidth * 0.08), onTap: () => router.go('/login'),
), child: Center(
// enable itemcount to disable infinite scroll child: Text(
// itemCount: pages.length, "Go to Login",
// opacityFactor: 2.0, 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, scaleFactor: 2,
duration: Duration(milliseconds: 500), duration: Duration(milliseconds: 500),
// verticalPosition: 0.7,
// direction: Axis.vertical,
// itemCount: pages.length,
// physics: NeverScrollableScrollPhysics(),
itemBuilder: (index) { itemBuilder: (index) {
final page = pages[index % pages.length]; final page = pages[index];
return SafeArea(child: _Page(page: page)); return SafeArea(child: _Page(page: page, index: index));
}, },
), ),
); );
@ -71,12 +102,14 @@ class PageData {
class _Page extends StatelessWidget { class _Page extends StatelessWidget {
final PageData page; final PageData page;
final int index;
const _Page({required this.page}); const _Page({required this.page, required this.index});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final screenHeight = MediaQuery.of(context).size.height; final screenHeight = MediaQuery.of(context).size.height;
return Column( return Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [

View File

@ -1,15 +1,14 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:rijig_mobile/core/guide.dart'; import 'package:rijig_mobile/core/guide.dart';
import 'package:rijig_mobile/core/router.dart'; import 'package:rijig_mobile/core/router.dart';
import 'package:shared_preferences/shared_preferences.dart';
class SplashScreen extends StatelessWidget { class SplashScreen extends StatelessWidget {
const SplashScreen({super.key}); const SplashScreen({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Future.delayed(Duration(seconds: 3), () { _checkLoginStatus(context);
router.go('/onboarding');
});
return Scaffold( return Scaffold(
backgroundColor: whiteColor, backgroundColor: whiteColor,
@ -21,7 +20,6 @@ class SplashScreen extends StatelessWidget {
right: 0, right: 0,
child: Image.asset('assets/image/Go_Ride.png', height: 200), child: Image.asset('assets/image/Go_Ride.png', height: 200),
), ),
Align( Align(
alignment: Alignment.center, alignment: Alignment.center,
child: Padding( child: Padding(
@ -31,7 +29,7 @@ class SplashScreen extends StatelessWidget {
style: TextStyle( style: TextStyle(
fontSize: 36, fontSize: 36,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: primaryColor, color: Colors.blue,
fontFamily: 'Roboto', fontFamily: 'Roboto',
), ),
), ),
@ -41,4 +39,17 @@ class SplashScreen extends StatelessWidget {
), ),
); );
} }
Future<void> _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');
}
}
} }

View File

@ -53,6 +53,7 @@ class UserViewModel extends ChangeNotifier {
await prefs.setString('token', response['data']['token']); await prefs.setString('token', response['data']['token']);
await prefs.setString('user_id', response['data']['user_id']); await prefs.setString('user_id', response['data']['user_id']);
await prefs.setString('user_role', response['data']['user_role']); await prefs.setString('user_role', response['data']['user_role']);
await prefs.setBool('isLoggedIn', true);
debugPrint("berhasil login"); debugPrint("berhasil login");
} else { } else {

33
lib/widget/appbar.dart Normal file
View File

@ -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<Widget>? 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);
}