refact: refactoring code and style
This commit is contained in:
parent
4f84abfeee
commit
0d129218de
|
@ -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);
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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"]);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue