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';
// =====================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,
);
}
}

View File

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

View File

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

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 '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(

View File

@ -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: () {

View File

@ -46,7 +46,7 @@ class VerifotpScreen extends StatelessWidget {
if (userVM.authModel?.status == 200) {
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: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<OnboardingPageScreen> {
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: [

View File

@ -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<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('user_id', response['data']['user_id']);
await prefs.setString('user_role', response['data']['user_role']);
await prefs.setBool('isLoggedIn', true);
debugPrint("berhasil login");
} 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);
}