feat: implemenat widget modal and show dialog
This commit is contained in:
parent
c87d820a9d
commit
de01c1acce
|
@ -24,10 +24,10 @@ FontWeight superBold = FontWeight.w900;
|
||||||
|
|
||||||
// =====================text behavior=====================
|
// =====================text behavior=====================
|
||||||
class Tulisan {
|
class Tulisan {
|
||||||
static TextStyle heading({Color? color}) {
|
static TextStyle heading({Color? color, double? fontsize}) {
|
||||||
return GoogleFonts.spaceGrotesk(
|
return GoogleFonts.spaceGrotesk(
|
||||||
fontSize: 24.sp,
|
fontSize: fontsize?.sp ?? 24.sp,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: extraBold,
|
||||||
color: color ?? blackNavyColor,
|
color: color ?? blackNavyColor,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ class Tulisan {
|
||||||
static TextStyle body({Color? color, double? fontsize}) {
|
static TextStyle body({Color? color, double? fontsize}) {
|
||||||
return GoogleFonts.spaceMono(
|
return GoogleFonts.spaceMono(
|
||||||
fontSize: fontsize?.sp ?? 16.sp,
|
fontSize: fontsize?.sp ?? 16.sp,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: regular,
|
||||||
color: color ?? blackNavyColor,
|
color: color ?? blackNavyColor,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ class Tulisan {
|
||||||
static TextStyle subheading({Color? color, double? fontsize}) {
|
static TextStyle subheading({Color? color, double? fontsize}) {
|
||||||
return GoogleFonts.spaceGrotesk(
|
return GoogleFonts.spaceGrotesk(
|
||||||
fontSize: fontsize?.sp ?? 18.sp,
|
fontSize: fontsize?.sp ?? 18.sp,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: medium,
|
||||||
color: color ?? blackNavyColor,
|
color: color ?? blackNavyColor,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ class Tulisan {
|
||||||
}) {
|
}) {
|
||||||
return GoogleFonts.spaceGrotesk(
|
return GoogleFonts.spaceGrotesk(
|
||||||
fontSize: fontsize?.sp ?? 16.sp,
|
fontSize: fontsize?.sp ?? 16.sp,
|
||||||
fontWeight: fontWeight ?? FontWeight.w400,
|
fontWeight: fontWeight ?? regular,
|
||||||
color: color ?? blackNavyColor,
|
color: color ?? blackNavyColor,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:rijig_mobile/core/utils/guide.dart';
|
import 'package:rijig_mobile/core/utils/guide.dart';
|
||||||
import 'package:rijig_mobile/widget/tabbar_custom.dart';
|
import 'package:rijig_mobile/widget/tabbar_custom.dart';
|
||||||
|
import 'package:rijig_mobile/widget/unhope_handler.dart';
|
||||||
|
|
||||||
class ActivityScreen extends StatefulWidget {
|
class ActivityScreen extends StatefulWidget {
|
||||||
const ActivityScreen({super.key});
|
const ActivityScreen({super.key});
|
||||||
|
@ -49,11 +50,11 @@ class _ActivityScreenState extends State<ActivityScreen> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: const TabBarView(
|
body: TabBarView(
|
||||||
children: [
|
children: [
|
||||||
Center(child: Text('Proses Page')),
|
Center(child: InfoStateWidget(type: InfoStateType.emptyData)),
|
||||||
Center(child: Text('Gak Eroh Page')),
|
Center(child: InfoStateWidget(type: InfoStateType.emptyData)),
|
||||||
Center(child: Text('Dibatalkan Page')),
|
Center(child: InfoStateWidget(type: InfoStateType.emptyData)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -13,7 +13,9 @@ import 'package:rijig_mobile/features/home/presentation/components/about_comp.da
|
||||||
import 'package:rijig_mobile/features/home/presentation/components/article_list.dart';
|
import 'package:rijig_mobile/features/home/presentation/components/article_list.dart';
|
||||||
import 'package:rijig_mobile/globaldata/about/about_vmod.dart';
|
import 'package:rijig_mobile/globaldata/about/about_vmod.dart';
|
||||||
import 'package:rijig_mobile/globaldata/article/article_vmod.dart';
|
import 'package:rijig_mobile/globaldata/article/article_vmod.dart';
|
||||||
|
import 'package:rijig_mobile/widget/buttoncard.dart';
|
||||||
import 'package:rijig_mobile/widget/card_withicon.dart';
|
import 'package:rijig_mobile/widget/card_withicon.dart';
|
||||||
|
import 'package:rijig_mobile/widget/showmodal.dart';
|
||||||
|
|
||||||
class HomeScreen extends StatefulWidget {
|
class HomeScreen extends StatefulWidget {
|
||||||
const HomeScreen({super.key});
|
const HomeScreen({super.key});
|
||||||
|
@ -98,7 +100,40 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||||
icon: Iconsax.timer,
|
icon: Iconsax.timer,
|
||||||
text: 'Process',
|
text: 'Process',
|
||||||
number: '1',
|
number: '1',
|
||||||
onTap: () {},
|
onTap: () {
|
||||||
|
CustomModalDialog.show(
|
||||||
|
context: context,
|
||||||
|
variant: ModalVariant.textVersion,
|
||||||
|
title: 'Hapus Akun',
|
||||||
|
content:
|
||||||
|
'Lorem Ipsum is simply dummy text of the printing and typesetting industry.',
|
||||||
|
buttonCount: 2,
|
||||||
|
button1: CardButtonOne(
|
||||||
|
textButton: "Ya, Hapus",
|
||||||
|
onTap: () {},
|
||||||
|
fontSized: 14,
|
||||||
|
colorText: whiteColor,
|
||||||
|
color: primaryColor,
|
||||||
|
borderRadius: 10,
|
||||||
|
horizontal: double.infinity,
|
||||||
|
vertical: 50,
|
||||||
|
loadingTrue: false,
|
||||||
|
usingRow: false,
|
||||||
|
),
|
||||||
|
button2: CardButtonOne(
|
||||||
|
textButton: "Batal",
|
||||||
|
onTap: () => router.pop(),
|
||||||
|
fontSized: 14,
|
||||||
|
colorText: primaryColor,
|
||||||
|
color: Colors.transparent,
|
||||||
|
borderRadius: 10,
|
||||||
|
horizontal: double.infinity,
|
||||||
|
vertical: 50,
|
||||||
|
loadingTrue: false,
|
||||||
|
usingRow: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -6,6 +6,7 @@ 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/auth/presentation/viewmodel/logout_vmod.dart';
|
import 'package:rijig_mobile/features/auth/presentation/viewmodel/logout_vmod.dart';
|
||||||
import 'package:rijig_mobile/widget/buttoncard.dart';
|
import 'package:rijig_mobile/widget/buttoncard.dart';
|
||||||
|
import 'package:rijig_mobile/widget/custom_bottom_sheet.dart';
|
||||||
|
|
||||||
class ButtonLogout extends StatefulWidget {
|
class ButtonLogout extends StatefulWidget {
|
||||||
const ButtonLogout({super.key});
|
const ButtonLogout({super.key});
|
||||||
|
@ -29,17 +30,54 @@ class _ButtonLogoutState extends State<ButtonLogout> {
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
horizontal: double.infinity,
|
horizontal: double.infinity,
|
||||||
vertical: 50,
|
vertical: 50,
|
||||||
onTap: () async {
|
onTap:
|
||||||
await viewModel.logout();
|
() => CustomBottomSheet.show(
|
||||||
|
context: context,
|
||||||
|
title: "Logout Sekarang?",
|
||||||
|
content: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text("Yakin ingin logout dari akun ini?"),
|
||||||
|
// tambahan konten
|
||||||
|
],
|
||||||
|
),
|
||||||
|
button1: CardButtonOne(
|
||||||
|
textButton: "Logout",
|
||||||
|
onTap: () {},
|
||||||
|
fontSized: 14,
|
||||||
|
colorText: Colors.white,
|
||||||
|
color: Colors.red,
|
||||||
|
borderRadius: 10,
|
||||||
|
horizontal: double.infinity,
|
||||||
|
vertical: 50,
|
||||||
|
loadingTrue: false,
|
||||||
|
usingRow: false,
|
||||||
|
),
|
||||||
|
button2: CardButtonOne(
|
||||||
|
textButton: "Batal",
|
||||||
|
onTap: () => router.pop(),
|
||||||
|
fontSized: 14,
|
||||||
|
colorText: Colors.red,
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: 10,
|
||||||
|
horizontal: double.infinity,
|
||||||
|
vertical: 50,
|
||||||
|
loadingTrue: false,
|
||||||
|
usingRow: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
if (viewModel.errorMessage == null) {
|
// onTap: () async {
|
||||||
router.go("/login");
|
// await viewModel.logout();
|
||||||
} else {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
// if (viewModel.errorMessage == null) {
|
||||||
SnackBar(content: Text(viewModel.errorMessage!)),
|
// router.go("/login");
|
||||||
);
|
// } else {
|
||||||
}
|
// ScaffoldMessenger.of(context).showSnackBar(
|
||||||
},
|
// SnackBar(content: Text(viewModel.errorMessage!)),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// },
|
||||||
loadingTrue: viewModel.isLoading,
|
loadingTrue: viewModel.isLoading,
|
||||||
usingRow: false,
|
usingRow: false,
|
||||||
),
|
),
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
|
import 'package:rijig_mobile/core/router.dart';
|
||||||
|
import 'package:rijig_mobile/core/utils/guide.dart';
|
||||||
|
|
||||||
|
class CustomBottomSheet extends StatelessWidget {
|
||||||
|
final String title;
|
||||||
|
final Widget content;
|
||||||
|
final Widget button1;
|
||||||
|
final Widget? button2;
|
||||||
|
|
||||||
|
const CustomBottomSheet({
|
||||||
|
super.key,
|
||||||
|
required this.title,
|
||||||
|
required this.content,
|
||||||
|
required this.button1,
|
||||||
|
this.button2,
|
||||||
|
});
|
||||||
|
|
||||||
|
static void show({
|
||||||
|
required BuildContext context,
|
||||||
|
required String title,
|
||||||
|
required Widget content,
|
||||||
|
required Widget button1,
|
||||||
|
Widget? button2,
|
||||||
|
Duration duration = const Duration(milliseconds: 380),
|
||||||
|
}) {
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
transitionAnimationController: AnimationController(
|
||||||
|
vsync: Navigator.of(context),
|
||||||
|
duration: duration,
|
||||||
|
),
|
||||||
|
builder: (context) {
|
||||||
|
return CustomBottomSheet(
|
||||||
|
title: title,
|
||||||
|
content: content,
|
||||||
|
button1: button1,
|
||||||
|
button2: button2,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Stack(
|
||||||
|
clipBehavior: Clip.none,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: PaddingCustom().paddingAll(15),
|
||||||
|
margin: const EdgeInsets.only(top: 30),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: whiteColor,
|
||||||
|
borderRadius: const BorderRadius.vertical(top: Radius.circular(15)),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(title, style: Tulisan.subheading(fontsize: 20)),
|
||||||
|
const Gap(12),
|
||||||
|
content,
|
||||||
|
const Gap(24),
|
||||||
|
if (button2 != null)
|
||||||
|
Column(children: [button1, const Gap(12), button2!])
|
||||||
|
else
|
||||||
|
button1,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
top: -25,
|
||||||
|
right: 5,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () => router.pop(),
|
||||||
|
child: Container(
|
||||||
|
padding: PaddingCustom().paddingAll(10),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: whiteColor,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: const Icon(Icons.close, size: 25),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,140 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
|
import 'package:rijig_mobile/core/router.dart';
|
||||||
|
import 'package:rijig_mobile/core/utils/guide.dart';
|
||||||
|
|
||||||
|
enum ModalVariant { textVersion, imageVersion }
|
||||||
|
|
||||||
|
class CustomModalDialog extends StatelessWidget {
|
||||||
|
final ModalVariant variant;
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
final String content;
|
||||||
|
final String? imageAsset;
|
||||||
|
|
||||||
|
final int buttonCount;
|
||||||
|
final Widget? button1;
|
||||||
|
final Widget? button2;
|
||||||
|
|
||||||
|
const CustomModalDialog({
|
||||||
|
super.key,
|
||||||
|
required this.variant,
|
||||||
|
required this.title,
|
||||||
|
required this.content,
|
||||||
|
this.imageAsset,
|
||||||
|
this.buttonCount = 0,
|
||||||
|
this.button1,
|
||||||
|
this.button2,
|
||||||
|
});
|
||||||
|
|
||||||
|
static void show({
|
||||||
|
required BuildContext context,
|
||||||
|
required ModalVariant variant,
|
||||||
|
required String title,
|
||||||
|
required String content,
|
||||||
|
String? imageAsset,
|
||||||
|
int buttonCount = 0,
|
||||||
|
Widget? button1,
|
||||||
|
Widget? button2,
|
||||||
|
}) {
|
||||||
|
showGeneralDialog(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: true,
|
||||||
|
barrierLabel: 'Dismiss',
|
||||||
|
barrierColor: Colors.black54,
|
||||||
|
transitionDuration: const Duration(milliseconds: 190),
|
||||||
|
pageBuilder: (_, __, ___) => const SizedBox.shrink(),
|
||||||
|
transitionBuilder: (_, animation, __, child) {
|
||||||
|
return Transform.scale(
|
||||||
|
scale: animation.value,
|
||||||
|
child: Opacity(
|
||||||
|
opacity: animation.value,
|
||||||
|
child: Center(
|
||||||
|
child: Dialog(
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
insetPadding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 24,
|
||||||
|
vertical: 24,
|
||||||
|
),
|
||||||
|
child: CustomModalDialog(
|
||||||
|
variant: variant,
|
||||||
|
title: title,
|
||||||
|
content: content,
|
||||||
|
imageAsset: imageAsset,
|
||||||
|
buttonCount: buttonCount,
|
||||||
|
button1: button1,
|
||||||
|
button2: button2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final modalContent = Container(
|
||||||
|
padding: PaddingCustom().paddingHorizontalVertical(20, 24),
|
||||||
|
margin: const EdgeInsets.only(top: 30),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: whiteColor,
|
||||||
|
borderRadius: BorderRadius.circular(24),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
if (variant == ModalVariant.imageVersion && imageAsset != null)
|
||||||
|
Padding(
|
||||||
|
padding: PaddingCustom().paddingOnly(bottom: 20),
|
||||||
|
child: Image.asset(
|
||||||
|
imageAsset!,
|
||||||
|
width: MediaQuery.of(context).size.width * 0.6,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Text(title, style: Tulisan.subheading(fontsize: 19)),
|
||||||
|
),
|
||||||
|
Gap(8),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Text(content, style: Tulisan.customText(fontsize: 14)),
|
||||||
|
),
|
||||||
|
Gap(24),
|
||||||
|
if (buttonCount == 1 && button1 != null) button1!,
|
||||||
|
if (buttonCount == 2 && button1 != null && button2 != null)
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: [
|
||||||
|
Expanded(child: button2!),
|
||||||
|
Gap(16),
|
||||||
|
Expanded(child: button1!),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return Stack(
|
||||||
|
clipBehavior: Clip.none,
|
||||||
|
children: [
|
||||||
|
modalContent,
|
||||||
|
Positioned(
|
||||||
|
top: -15,
|
||||||
|
right: 5,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () => router.pop(),
|
||||||
|
child: CircleAvatar(
|
||||||
|
radius: 18,
|
||||||
|
backgroundColor: whiteColor,
|
||||||
|
child: Icon(Icons.close, color: blackNavyColor),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
enum InfoStateType { dataNotFound, emptyData, comingSoon }
|
||||||
|
|
||||||
|
class InfoStateWidget extends StatelessWidget {
|
||||||
|
final InfoStateType type;
|
||||||
|
final String? imageAsset;
|
||||||
|
final String? description;
|
||||||
|
|
||||||
|
const InfoStateWidget({
|
||||||
|
super.key,
|
||||||
|
required this.type,
|
||||||
|
this.imageAsset,
|
||||||
|
this.description,
|
||||||
|
});
|
||||||
|
|
||||||
|
String _getDefaultDescription() {
|
||||||
|
switch (type) {
|
||||||
|
case InfoStateType.dataNotFound:
|
||||||
|
return 'Data tidak ditemukan';
|
||||||
|
case InfoStateType.emptyData:
|
||||||
|
return 'Belum ada data yang tersedia';
|
||||||
|
case InfoStateType.comingSoon:
|
||||||
|
return 'Fitur ini segera hadir';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getDefaultImageAsset() {
|
||||||
|
switch (type) {
|
||||||
|
case InfoStateType.dataNotFound:
|
||||||
|
return 'assets/image/empty_data.png';
|
||||||
|
case InfoStateType.emptyData:
|
||||||
|
return 'assets/image/empty_data.png';
|
||||||
|
case InfoStateType.comingSoon:
|
||||||
|
return 'assets/image/empty_data.png';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final screenWidth = MediaQuery.of(context).size.width;
|
||||||
|
final screenHeight = MediaQuery.of(context).size.height;
|
||||||
|
|
||||||
|
return Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Image.asset(
|
||||||
|
imageAsset ?? _getDefaultImageAsset(),
|
||||||
|
width: screenWidth * 0.5,
|
||||||
|
height: screenHeight * 0.3,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
Text(
|
||||||
|
description ?? _getDefaultDescription(),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue