feat: implemenat refresh indicator

This commit is contained in:
pahmiudahgede 2025-05-17 16:37:22 +07:00
parent b951af1eec
commit 0cf104c5d9
2 changed files with 110 additions and 53 deletions

View File

@ -1,8 +1,13 @@
import 'dart:math' as math;
import 'package:custom_refresh_indicator/custom_refresh_indicator.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:iconsax_flutter/iconsax_flutter.dart'; import 'package:iconsax_flutter/iconsax_flutter.dart';
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/features/home/presentation/components/about_comp.dart'; import 'package:rijig_mobile/features/home/presentation/components/about_comp.dart';
import 'package:rijig_mobile/features/home/presentation/viewmodel/about_vmod.dart';
import 'package:rijig_mobile/widget/card_withicon.dart'; import 'package:rijig_mobile/widget/card_withicon.dart';
class HomeScreen extends StatefulWidget { class HomeScreen extends StatefulWidget {
@ -17,10 +22,29 @@ class _HomeScreenState extends State<HomeScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: whiteColor, backgroundColor: whiteColor,
body: SafeArea( body: CustomMaterialIndicator(
child: SingleChildScrollView( onRefresh: () async {
padding: PaddingCustom().paddingHorizontalVertical(16, 20), await Provider.of<AboutViewModel>(
child: Column( context,
listen: false,
).getAboutList();
},
backgroundColor: whiteColor,
indicatorBuilder: (context, controller) {
return Padding(
padding: const EdgeInsets.all(6.0),
child: CircularProgressIndicator(
color: primaryColor,
value:
controller.state.isLoading
? null
: math.min(controller.value, 1.0),
),
);
},
child: SafeArea(
child: ListView(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -72,7 +96,7 @@ class _HomeScreenState extends State<HomeScreen> {
children: [ children: [
Text( Text(
"Important!", "Important!",
style: const TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Colors.black, color: Colors.black,
@ -80,7 +104,7 @@ class _HomeScreenState extends State<HomeScreen> {
), ),
], ],
), ),
const Gap(15), Gap(15),
AboutComponent(), AboutComponent(),
], ],
), ),
@ -94,7 +118,7 @@ class _HomeScreenState extends State<HomeScreen> {
children: [ children: [
Text( Text(
"Artikel", "Artikel",
style: const TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Colors.black, color: Colors.black,
@ -105,6 +129,7 @@ class _HomeScreenState extends State<HomeScreen> {
), ),
], ],
), ),
Gap(20),
], ],
), ),
), ),

View File

@ -1,3 +1,6 @@
import 'dart:math' as math;
import 'package:custom_refresh_indicator/custom_refresh_indicator.dart';
import 'package:flutter/material.dart'; 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';
@ -6,63 +9,92 @@ 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:shimmer/shimmer.dart';
class RequestPickScreen extends StatelessWidget { class RequestPickScreen extends StatefulWidget {
const RequestPickScreen({super.key}); const RequestPickScreen({super.key});
@override
RequestPickScreenState createState() => RequestPickScreenState();
}
class RequestPickScreenState extends State<RequestPickScreen> {
@override
void initState() {
super.initState();
Provider.of<TrashViewModel>(context, listen: false).loadCategories();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Future.microtask(() {
// ignore: use_build_context_synchronously
Provider.of<TrashViewModel>(context, listen: false).loadCategories();
});
final String? baseUrl = dotenv.env["BASE_URL"]; final String? baseUrl = dotenv.env["BASE_URL"];
return Scaffold( return Scaffold(
backgroundColor: whiteColor, backgroundColor: whiteColor,
appBar: CustomAppBar(judul: "Pilih sampah"), appBar: CustomAppBar(judul: "Pilih sampah"),
body: Consumer<TrashViewModel>( body: CustomMaterialIndicator(
builder: (context, viewModel, child) { onRefresh: () async {
if (viewModel.isLoading) { await Provider.of<TrashViewModel>(
return ListView.builder( context,
itemCount: 5, listen: false,
itemBuilder: (context, index) { ).loadCategories();
return SkeletonCard(); },
}, backgroundColor: Colors.white,
); indicatorBuilder: (context, controller) {
} return Padding(
padding: const EdgeInsets.all(6.0),
if (viewModel.errorMessage != null) { child: CircularProgressIndicator(
return Center(child: Text(viewModel.errorMessage!)); color: primaryColor,
} value:
controller.state.isLoading
return ListView.builder( ? null
itemCount: viewModel.trashCategoryResponse?.categories.length ?? 0, : math.min(controller.value, 1.0),
itemBuilder: (context, index) { ),
final category =
viewModel.trashCategoryResponse!.categories[index];
return Card(
margin: const EdgeInsets.symmetric(
vertical: 10,
horizontal: 15,
),
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: ListTile(
leading: Image.network(
"$baseUrl${category.icon}",
width: 50,
height: 50,
fit: BoxFit.cover,
),
title: Text(category.name),
subtitle: Text("${category.price}"),
),
);
},
); );
}, },
child: Consumer<TrashViewModel>(
builder: (context, viewModel, child) {
if (viewModel.isLoading) {
return ListView.builder(
itemCount: 5,
itemBuilder: (context, index) {
return SkeletonCard();
},
);
}
if (viewModel.errorMessage != null) {
return Center(child: Text(viewModel.errorMessage!));
}
return ListView.builder(
itemCount:
viewModel.trashCategoryResponse?.categories.length ?? 0,
itemBuilder: (context, index) {
final category =
viewModel.trashCategoryResponse!.categories[index];
return Card(
margin: const EdgeInsets.symmetric(
vertical: 10,
horizontal: 15,
),
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: ListTile(
leading: Image.network(
"$baseUrl${category.icon}",
width: 50,
height: 50,
fit: BoxFit.cover,
),
title: Text(category.name),
subtitle: Text("${category.price}"),
),
);
},
);
},
),
), ),
); );
} }