refact: refactoring code and style

This commit is contained in:
pahmiudahgede 2025-05-18 00:55:21 +07:00
parent 4f84abfeee
commit 0d129218de
10 changed files with 194 additions and 88 deletions

View File

@ -47,5 +47,13 @@ final router = GoRouter(
return AboutDetailScreenComp(data: data);
},
),
GoRoute(
path: '/artikeldetail',
builder: (context, state) {
dynamic data = state.extra;
return ArticleDetailScreen(data: data);
},
),
],
);

View File

@ -12,3 +12,4 @@ export 'package:rijig_mobile/features/auth/presentation/screen/verifpin_screen.d
export 'package:rijig_mobile/features/launch/screen/onboardingpage_screen.dart';
export 'package:rijig_mobile/features/launch/screen/splash_screen.dart';
export 'package:rijig_mobile/features/home/presentation/components/about_detail_comp.dart';
export 'package:rijig_mobile/features/home/presentation/components/article_content.dart';

View File

@ -52,7 +52,7 @@ class _NavigationPageState extends State<NavigationPage> {
Widget build(BuildContext context) {
return Scaffold(
extendBody: true,
backgroundColor: Colors.white,
backgroundColor: whiteColor,
body: IndexedStack(
index: _selectedIndex,
children: const [
@ -151,7 +151,7 @@ class _NavigationPageState extends State<NavigationPage> {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Iconsax.archive_2, color: Colors.white, size: 30),
Icon(Iconsax.archive_2, color: whiteColor, size: 30),
Text(
"Mulai",
style: Tulisan.customText(

View File

@ -5,7 +5,7 @@ import 'package:provider/provider.dart';
import 'package:rijig_mobile/core/router.dart';
import 'package:rijig_mobile/core/utils/guide.dart';
import 'package:rijig_mobile/features/home/presentation/viewmodel/about_vmod.dart';
import 'package:rijig_mobile/features/requestpick/presentation/screen/requestpickup_screen.dart';
import 'package:rijig_mobile/widget/skeletonize.dart';
class AboutComponent extends StatefulWidget {
const AboutComponent({super.key});
@ -33,6 +33,7 @@ class AboutComponentState extends State<AboutComponent> {
builder: (context, viewModel, child) {
if (viewModel.isLoading) {
return ListView.builder(
shrinkWrap: true,
itemCount: 1,
itemBuilder: (context, index) {
return SkeletonCard();
@ -99,7 +100,6 @@ class AboutComponentState extends State<AboutComponent> {
),
onTap: () {
debugPrint("Tapped on ${imageData['route']}");
// imageData["route"];
router.push("/aboutdetail", extra: imageData["route"]);
},
);

View File

@ -2,8 +2,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:provider/provider.dart';
import 'package:rijig_mobile/features/home/presentation/viewmodel/about_vmod.dart';
import 'package:rijig_mobile/features/requestpick/presentation/screen/requestpickup_screen.dart';
import 'package:rijig_mobile/widget/appbar.dart';
import 'package:rijig_mobile/widget/skeletonize.dart';
class AboutDetailScreenComp extends StatefulWidget {
final String data;

View File

@ -0,0 +1,57 @@
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:gap/gap.dart';
import 'package:rijig_mobile/core/utils/guide.dart';
import 'package:rijig_mobile/globaldata/article/article_model.dart';
import 'package:rijig_mobile/widget/appbar.dart';
class ArticleDetailScreen extends StatelessWidget {
final ArticleModel data;
const ArticleDetailScreen({super.key, required this.data});
@override
Widget build(BuildContext context) {
final String? baseUrl = dotenv.env["BASE_URL"];
return Scaffold(
appBar: CustomAppBar(judul: "detail artikel"),
body: SingleChildScrollView(
padding: PaddingCustom().paddingAll(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(data.heading, style: Tulisan.heading()),
Gap(8),
Text(
"Oleh ${data.author}${data.publishedAt}",
style: TextStyle(
fontSize: 13.sp,
fontStyle: FontStyle.italic,
color: greyAbsolutColor,
),
),
Gap(30),
if (data.coverImage.isNotEmpty)
ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Image.network(
"$baseUrl${data.coverImage}",
width: double.infinity,
fit: BoxFit.cover,
errorBuilder:
(context, error, stackTrace) =>
const Icon(Icons.broken_image),
),
),
Gap(10),
Divider(thickness: 1.3, color: blackNavyColor),
Gap(60),
Text(data.content, style: Tulisan.customText()),
],
),
),
);
}
}

View File

@ -1,7 +1,11 @@
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:provider/provider.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:rijig_mobile/core/router.dart';
import 'package:rijig_mobile/core/utils/guide.dart';
import 'package:rijig_mobile/globaldata/article/article_vmod.dart';
import 'package:rijig_mobile/widget/skeletonize.dart';
class ArticleScreen extends StatefulWidget {
const ArticleScreen({super.key});
@ -14,7 +18,6 @@ class _ArticleScreenState extends State<ArticleScreen> {
@override
void initState() {
super.initState();
// Fetch data setelah frame build pertama
WidgetsBinding.instance.addPostFrameCallback((_) {
Provider.of<ArticleViewModel>(context, listen: false).loadArticles();
});
@ -27,7 +30,13 @@ class _ArticleScreenState extends State<ArticleScreen> {
return Consumer<ArticleViewModel>(
builder: (context, viewModel, child) {
if (viewModel.isLoading) {
return const Center(child: CircularProgressIndicator());
return ListView.builder(
shrinkWrap: true,
itemCount: 1,
itemBuilder: (context, index) {
return SkeletonCard();
},
);
}
if (viewModel.errorMessage != null) {
@ -38,49 +47,78 @@ class _ArticleScreenState extends State<ArticleScreen> {
return const Center(child: Text("Tidak ada artikel ditemukan."));
}
return ListView.builder(
return SizedBox(
height: 190,
child: ListView.separated(
scrollDirection: Axis.horizontal,
clipBehavior: Clip.none,
itemCount: viewModel.articles.length,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
separatorBuilder: (_, __) => Gap(12),
itemBuilder: (context, index) {
final article = viewModel.articles[index];
return Card(
margin: const EdgeInsets.only(bottom: 12),
elevation: 3,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
return GestureDetector(
onTap: () {
router.push("/artikeldetail", extra: article);
},
child: Container(
padding: PaddingCustom().paddingAll(3),
width: 180,
decoration: BoxDecoration(
border: Border.all(color: greyColor),
color: whiteColor,
borderRadius: BorderRadius.circular(16),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16),
),
child: ListTile(
contentPadding: const EdgeInsets.all(12),
leading: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.network(
"$baseUrl${article.coverImage}",
width: 60,
height: 60,
width: double.infinity,
height: 100,
fit: BoxFit.cover,
errorBuilder:
(context, error, stackTrace) =>
const Icon(Icons.image_not_supported),
),
),
title: Text(
Padding(
padding: PaddingCustom().paddingAll(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
article.heading,
style: const TextStyle(fontWeight: FontWeight.bold),
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
),
),
const SizedBox(height: 8),
Text(
"by ${article.author}${article.publishedAt}",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 12,
color: Colors.grey,
),
subtitle: Text(
"By ${article.author}${article.publishedAt}",
style: const TextStyle(fontSize: 12),
),
onTap: () {
// Navigasi ke detail (jika tersedia)
// router.push('/article-detail', extra: article.articleId);
},
],
),
),
],
),
),
);
},
),
);
},
);

View File

@ -122,9 +122,7 @@ class _HomeScreenState extends State<HomeScreen> {
Gap(20),
Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Row(
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
@ -137,7 +135,6 @@ class _HomeScreenState extends State<HomeScreen> {
),
],
),
),
Gap(15),
ArticleScreen(),
],

View File

@ -7,7 +7,7 @@ import 'package:provider/provider.dart';
import 'package:rijig_mobile/core/utils/guide.dart';
import 'package:rijig_mobile/globaldata/trash/trash_viewmodel.dart';
import 'package:rijig_mobile/widget/appbar.dart';
import 'package:shimmer/shimmer.dart';
import 'package:rijig_mobile/widget/skeletonize.dart';
class RequestPickScreen extends StatefulWidget {
const RequestPickScreen({super.key});
@ -20,7 +20,9 @@ class RequestPickScreenState extends State<RequestPickScreen> {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
Provider.of<TrashViewModel>(context, listen: false).loadCategories();
});
}
@override
@ -37,10 +39,10 @@ class RequestPickScreenState extends State<RequestPickScreen> {
listen: false,
).loadCategories();
},
backgroundColor: Colors.white,
backgroundColor: whiteColor,
indicatorBuilder: (context, controller) {
return Padding(
padding: const EdgeInsets.all(6.0),
padding: PaddingCustom().paddingAll(6),
child: CircularProgressIndicator(
color: primaryColor,
value:
@ -54,6 +56,7 @@ class RequestPickScreenState extends State<RequestPickScreen> {
builder: (context, viewModel, child) {
if (viewModel.isLoading) {
return ListView.builder(
shrinkWrap: true,
itemCount: 5,
itemBuilder: (context, index) {
return SkeletonCard();
@ -99,25 +102,3 @@ class RequestPickScreenState extends State<RequestPickScreen> {
);
}
}
class SkeletonCard extends StatelessWidget {
const SkeletonCard({super.key});
@override
Widget build(BuildContext context) {
return Shimmer.fromColors(
baseColor: Colors.grey[300]!,
highlightColor: Colors.grey[100]!,
child: Card(
margin: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
elevation: 4,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: ListTile(
leading: Container(width: 50, height: 50, color: Colors.white),
title: Container(width: 100, height: 15, color: Colors.white),
subtitle: Container(width: 150, height: 10, color: Colors.white),
),
),
);
}
}

View File

@ -0,0 +1,24 @@
import 'package:flutter/material.dart';
import 'package:shimmer/shimmer.dart';
class SkeletonCard extends StatelessWidget {
const SkeletonCard({super.key});
@override
Widget build(BuildContext context) {
return Shimmer.fromColors(
baseColor: Colors.grey[300]!,
highlightColor: Colors.grey[100]!,
child: Card(
margin: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
elevation: 4,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: ListTile(
leading: Container(width: 50, height: 50, color: Colors.white),
title: Container(width: 100, height: 15, color: Colors.white),
subtitle: Container(width: 150, height: 10, color: Colors.white),
),
),
);
}
}