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); 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/onboardingpage_screen.dart';
export 'package:rijig_mobile/features/launch/screen/splash_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/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) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
extendBody: true, extendBody: true,
backgroundColor: Colors.white, backgroundColor: whiteColor,
body: IndexedStack( body: IndexedStack(
index: _selectedIndex, index: _selectedIndex,
children: const [ children: const [
@ -151,7 +151,7 @@ class _NavigationPageState extends State<NavigationPage> {
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Icon(Iconsax.archive_2, color: Colors.white, size: 30), Icon(Iconsax.archive_2, color: whiteColor, size: 30),
Text( Text(
"Mulai", "Mulai",
style: Tulisan.customText( 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/router.dart';
import 'package:rijig_mobile/core/utils/guide.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/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 { class AboutComponent extends StatefulWidget {
const AboutComponent({super.key}); const AboutComponent({super.key});
@ -33,6 +33,7 @@ class AboutComponentState extends State<AboutComponent> {
builder: (context, viewModel, child) { builder: (context, viewModel, child) {
if (viewModel.isLoading) { if (viewModel.isLoading) {
return ListView.builder( return ListView.builder(
shrinkWrap: true,
itemCount: 1, itemCount: 1,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return SkeletonCard(); return SkeletonCard();
@ -99,7 +100,6 @@ class AboutComponentState extends State<AboutComponent> {
), ),
onTap: () { onTap: () {
debugPrint("Tapped on ${imageData['route']}"); debugPrint("Tapped on ${imageData['route']}");
// imageData["route"];
router.push("/aboutdetail", extra: 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:flutter_dotenv/flutter_dotenv.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:rijig_mobile/features/home/presentation/viewmodel/about_vmod.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/appbar.dart';
import 'package:rijig_mobile/widget/skeletonize.dart';
class AboutDetailScreenComp extends StatefulWidget { class AboutDetailScreenComp extends StatefulWidget {
final String data; 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:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:flutter_dotenv/flutter_dotenv.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/globaldata/article/article_vmod.dart';
import 'package:rijig_mobile/widget/skeletonize.dart';
class ArticleScreen extends StatefulWidget { class ArticleScreen extends StatefulWidget {
const ArticleScreen({super.key}); const ArticleScreen({super.key});
@ -14,7 +18,6 @@ class _ArticleScreenState extends State<ArticleScreen> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
// Fetch data setelah frame build pertama
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
Provider.of<ArticleViewModel>(context, listen: false).loadArticles(); Provider.of<ArticleViewModel>(context, listen: false).loadArticles();
}); });
@ -27,7 +30,13 @@ class _ArticleScreenState extends State<ArticleScreen> {
return Consumer<ArticleViewModel>( return Consumer<ArticleViewModel>(
builder: (context, viewModel, child) { builder: (context, viewModel, child) {
if (viewModel.isLoading) { if (viewModel.isLoading) {
return const Center(child: CircularProgressIndicator()); return ListView.builder(
shrinkWrap: true,
itemCount: 1,
itemBuilder: (context, index) {
return SkeletonCard();
},
);
} }
if (viewModel.errorMessage != null) { if (viewModel.errorMessage != null) {
@ -38,49 +47,78 @@ class _ArticleScreenState extends State<ArticleScreen> {
return const Center(child: Text("Tidak ada artikel ditemukan.")); return const Center(child: Text("Tidak ada artikel ditemukan."));
} }
return ListView.builder( return SizedBox(
itemCount: viewModel.articles.length, height: 190,
shrinkWrap: true, child: ListView.separated(
physics: const NeverScrollableScrollPhysics(), scrollDirection: Axis.horizontal,
itemBuilder: (context, index) { clipBehavior: Clip.none,
final article = viewModel.articles[index]; itemCount: viewModel.articles.length,
return Card( separatorBuilder: (_, __) => Gap(12),
margin: const EdgeInsets.only(bottom: 12), itemBuilder: (context, index) {
elevation: 3, final article = viewModel.articles[index];
shape: RoundedRectangleBorder( return GestureDetector(
borderRadius: BorderRadius.circular(10), onTap: () {
), router.push("/artikeldetail", extra: article);
child: ListTile( },
contentPadding: const EdgeInsets.all(12), child: Container(
leading: ClipRRect( padding: PaddingCustom().paddingAll(3),
borderRadius: BorderRadius.circular(8), width: 180,
child: Image.network( decoration: BoxDecoration(
"$baseUrl${article.coverImage}", border: Border.all(color: greyColor),
width: 60, color: whiteColor,
height: 60, borderRadius: BorderRadius.circular(16),
fit: BoxFit.cover, ),
errorBuilder: child: Column(
(context, error, stackTrace) => crossAxisAlignment: CrossAxisAlignment.start,
const Icon(Icons.image_not_supported), children: [
ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16),
),
child: Image.network(
"$baseUrl${article.coverImage}",
width: double.infinity,
height: 100,
fit: BoxFit.cover,
errorBuilder:
(context, error, stackTrace) =>
const Icon(Icons.image_not_supported),
),
),
Padding(
padding: PaddingCustom().paddingAll(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
article.heading,
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,
),
),
],
),
),
],
), ),
), ),
title: Text( );
article.heading, },
style: const TextStyle(fontWeight: FontWeight.bold), ),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
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,21 +122,18 @@ class _HomeScreenState extends State<HomeScreen> {
Gap(20), Gap(20),
Column( Column(
children: [ children: [
Padding( Row(
padding: const EdgeInsets.symmetric(horizontal: 20), mainAxisAlignment: MainAxisAlignment.start,
child: Row( children: [
mainAxisAlignment: MainAxisAlignment.start, Text(
children: [ "Artikel",
Text( style: TextStyle(
"Artikel", fontSize: 16,
style: TextStyle( fontWeight: FontWeight.w600,
fontSize: 16, color: Colors.black,
fontWeight: FontWeight.w600,
color: Colors.black,
),
), ),
], ),
), ],
), ),
Gap(15), Gap(15),
ArticleScreen(), ArticleScreen(),

View File

@ -7,7 +7,7 @@ import 'package:provider/provider.dart';
import 'package:rijig_mobile/core/utils/guide.dart'; import 'package:rijig_mobile/core/utils/guide.dart';
import 'package:rijig_mobile/globaldata/trash/trash_viewmodel.dart'; import 'package:rijig_mobile/globaldata/trash/trash_viewmodel.dart';
import 'package:rijig_mobile/widget/appbar.dart'; import 'package:rijig_mobile/widget/appbar.dart';
import 'package:shimmer/shimmer.dart'; import 'package:rijig_mobile/widget/skeletonize.dart';
class RequestPickScreen extends StatefulWidget { class RequestPickScreen extends StatefulWidget {
const RequestPickScreen({super.key}); const RequestPickScreen({super.key});
@ -20,7 +20,9 @@ class RequestPickScreenState extends State<RequestPickScreen> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
Provider.of<TrashViewModel>(context, listen: false).loadCategories(); WidgetsBinding.instance.addPostFrameCallback((_) {
Provider.of<TrashViewModel>(context, listen: false).loadCategories();
});
} }
@override @override
@ -37,10 +39,10 @@ class RequestPickScreenState extends State<RequestPickScreen> {
listen: false, listen: false,
).loadCategories(); ).loadCategories();
}, },
backgroundColor: Colors.white, backgroundColor: whiteColor,
indicatorBuilder: (context, controller) { indicatorBuilder: (context, controller) {
return Padding( return Padding(
padding: const EdgeInsets.all(6.0), padding: PaddingCustom().paddingAll(6),
child: CircularProgressIndicator( child: CircularProgressIndicator(
color: primaryColor, color: primaryColor,
value: value:
@ -54,6 +56,7 @@ class RequestPickScreenState extends State<RequestPickScreen> {
builder: (context, viewModel, child) { builder: (context, viewModel, child) {
if (viewModel.isLoading) { if (viewModel.isLoading) {
return ListView.builder( return ListView.builder(
shrinkWrap: true,
itemCount: 5, itemCount: 5,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return SkeletonCard(); 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),
),
),
);
}
}