complete admin, mandor and user page

This commit is contained in:
kicap
2023-11-03 23:24:23 +08:00
parent 4cc9967ab8
commit 8eacfa6dee
41 changed files with 1335 additions and 492 deletions

4
.env
View File

@ -1,2 +1,2 @@
url = 'http://20.20.20.25/perumahan_backend/'
api_url = 'http://20.20.20.25/perumahan_backend/api/'
url = 'https://my_localhost.kicap-karan.com/perumahan_backend/'
api_url = 'https://my_localhost.kicap-karan.com/perumahan_backend/index.php/api/'

View File

@ -1,25 +1,11 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:label="perumahan_bew"
android:name="${applicationName}"
android:usesCleartextTraffic="true"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<application android:label="perumahan_bew" android:name="${applicationName}" android:usesCleartextTraffic="true" android:icon="@mipmap/ic_launcher">
<activity android:name=".MainActivity" android:exported="true" android:launchMode="singleTop" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<meta-data android:name="io.flutter.embedding.android.NormalTheme" android:resource="@style/NormalTheme" />
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
@ -27,8 +13,22 @@
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
<meta-data android:name="flutterEmbedding" android:value="2" />
</application>
<queries>
<!-- If your app checks for SMS support -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="sms" />
</intent>
<!-- If your app checks for call support -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="tel" />
</intent>
<!-- If your application checks for inAppBrowserView launch mode support -->
<intent>
<action android:name="android.support.customtabs.action.CustomTabsService" />
</intent>
</queries>
</manifest>

View File

@ -1,4 +1,5 @@
import 'package:perumahan_bew/ui/views/play_video_dialog/play_video_dialog_view.dart';
import 'package:perumahan_bew/ui/views/user_index/user_list_pembangunan/user_list_pembangunan_page/user_list_pembangunan_page_view.dart';
import 'package:stacked_services/stacked_services.dart';
import 'package:stacked/stacked_annotations.dart';
@ -18,7 +19,6 @@ import '../ui/views/pengembang_index/pengembang_profil/pengembang_profil_view.da
import '../ui/views/pengembang_index/perumahan_detail/perumahan_detail_view.dart';
import '../ui/views/splash_screen/splash_screen_view.dart';
import '../ui/views/tambah_lihat_progress_bottom_sheet/tambah_lihat_progress_bottom_sheet_view.dart';
import '../ui/views/user_index/user_home/user_home_view.dart';
import '../ui/views/user_index/user_index_view.dart';
import '../ui/views/user_index/user_list_pembangunan/user_list_pembangunan_view.dart';
import '../ui/views/user_index/user_profile/user_profile_view.dart';
@ -30,9 +30,9 @@ import '../ui/views/user_index/user_profile/user_profile_view.dart';
MaterialRoute(
page: UserIndexView,
children: [
MaterialRoute(page: UserHomeView, initial: true),
MaterialRoute(page: UserListPembangunanView, initial: true),
MaterialRoute(page: UserProfileView),
MaterialRoute(page: UserListPembangunanView),
MaterialRoute(page: UserListPembangunanPageView),
],
),
// dibawah untuk admin
@ -60,7 +60,6 @@ import '../ui/views/user_index/user_profile/user_profile_view.dart';
LazySingleton(classType: SnackbarService),
LazySingleton(classType: BottomSheetService),
//
LazySingleton(classType: UserHomeView),
LazySingleton(classType: PengembangHomeView),
LazySingleton(classType: MyEasyLoading),
LazySingleton(classType: MyHttpServices),

View File

@ -18,7 +18,6 @@ import '../services/my_easyloading.dart';
import '../services/other_function.dart';
import '../services/shared_prefs.dart';
import '../ui/views/pengembang_index/pengembang_home/pengembang_home_view.dart';
import '../ui/views/user_index/user_home/user_home_view.dart';
final locator = StackedLocator.instance;
@ -35,7 +34,6 @@ Future<void> setupLocator({
locator.registerLazySingleton(() => DialogService());
locator.registerLazySingleton(() => SnackbarService());
locator.registerLazySingleton(() => BottomSheetService());
locator.registerLazySingleton(() => const UserHomeView());
locator.registerLazySingleton(() => const PengembangHomeView());
locator.registerLazySingleton(() => MyEasyLoading());
locator.registerLazySingleton(() => MyHttpServices());

View File

@ -25,11 +25,11 @@ import 'package:perumahan_bew/ui/views/pengembang_index/perumahan_detail/perumah
as _i6;
import 'package:perumahan_bew/ui/views/splash_screen/splash_screen_view.dart'
as _i2;
import 'package:perumahan_bew/ui/views/user_index/user_home/user_home_view.dart'
as _i9;
import 'package:perumahan_bew/ui/views/user_index/user_index_view.dart' as _i4;
import 'package:perumahan_bew/ui/views/user_index/user_list_pembangunan/user_list_pembangunan_view.dart'
import 'package:perumahan_bew/ui/views/user_index/user_list_pembangunan/user_list_pembangunan_page/user_list_pembangunan_page_view.dart'
as _i11;
import 'package:perumahan_bew/ui/views/user_index/user_list_pembangunan/user_list_pembangunan_view.dart'
as _i9;
import 'package:perumahan_bew/ui/views/user_index/user_profile/user_profile_view.dart'
as _i10;
import 'package:stacked/stacked.dart' as _i1;
@ -162,39 +162,39 @@ class PerumahanDetailViewArguments {
}
class UserIndexViewRoutes {
static const userHomeView = '';
static const userListPembangunanView = '';
static const userProfileView = 'user-profile-view';
static const userListPembangunanView = 'user-list-pembangunan-view';
static const userListPembangunanPageView = 'user-list-pembangunan-page-view';
static const all = <String>{
userHomeView,
userProfileView,
userListPembangunanView,
userProfileView,
userListPembangunanPageView,
};
}
class UserIndexViewRouter extends _i1.RouterBase {
final _routes = <_i1.RouteDef>[
_i1.RouteDef(
UserIndexViewRoutes.userHomeView,
page: _i9.UserHomeView,
UserIndexViewRoutes.userListPembangunanView,
page: _i9.UserListPembangunanView,
),
_i1.RouteDef(
UserIndexViewRoutes.userProfileView,
page: _i10.UserProfileView,
),
_i1.RouteDef(
UserIndexViewRoutes.userListPembangunanView,
page: _i11.UserListPembangunanView,
UserIndexViewRoutes.userListPembangunanPageView,
page: _i11.UserListPembangunanPageView,
),
];
final _pagesMap = <Type, _i1.StackedRouteFactory>{
_i9.UserHomeView: (data) {
_i9.UserListPembangunanView: (data) {
return _i8.MaterialPageRoute<dynamic>(
builder: (context) => const _i9.UserHomeView(),
builder: (context) => const _i9.UserListPembangunanView(),
settings: data,
);
},
@ -204,9 +204,9 @@ class UserIndexViewRouter extends _i1.RouterBase {
settings: data,
);
},
_i11.UserListPembangunanView: (data) {
_i11.UserListPembangunanPageView: (data) {
return _i8.MaterialPageRoute<dynamic>(
builder: (context) => const _i11.UserListPembangunanView(),
builder: (context) => const _i11.UserListPembangunanPageView(),
settings: data,
);
},
@ -450,14 +450,14 @@ extension NavigatorStateExtension on _i16.NavigationService {
transition: transition);
}
Future<dynamic> navigateToNestedUserHomeViewInUserIndexViewRouter([
Future<dynamic> navigateToNestedUserListPembangunanViewInUserIndexViewRouter([
int? routerId,
bool preventDuplicates = true,
Map<String, String>? parameters,
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
transition,
]) async {
return navigateTo<dynamic>(UserIndexViewRoutes.userHomeView,
return navigateTo<dynamic>(UserIndexViewRoutes.userListPembangunanView,
id: routerId,
preventDuplicates: preventDuplicates,
parameters: parameters,
@ -478,14 +478,15 @@ extension NavigatorStateExtension on _i16.NavigationService {
transition: transition);
}
Future<dynamic> navigateToNestedUserListPembangunanViewInUserIndexViewRouter([
Future<dynamic>
navigateToNestedUserListPembangunanPageViewInUserIndexViewRouter([
int? routerId,
bool preventDuplicates = true,
Map<String, String>? parameters,
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
transition,
]) async {
return navigateTo<dynamic>(UserIndexViewRoutes.userListPembangunanView,
return navigateTo<dynamic>(UserIndexViewRoutes.userListPembangunanPageView,
id: routerId,
preventDuplicates: preventDuplicates,
parameters: parameters,
@ -701,14 +702,15 @@ extension NavigatorStateExtension on _i16.NavigationService {
transition: transition);
}
Future<dynamic> replaceWithNestedUserHomeViewInUserIndexViewRouter([
Future<dynamic>
replaceWithNestedUserListPembangunanViewInUserIndexViewRouter([
int? routerId,
bool preventDuplicates = true,
Map<String, String>? parameters,
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
transition,
]) async {
return replaceWith<dynamic>(UserIndexViewRoutes.userHomeView,
return replaceWith<dynamic>(UserIndexViewRoutes.userListPembangunanView,
id: routerId,
preventDuplicates: preventDuplicates,
parameters: parameters,
@ -730,14 +732,14 @@ extension NavigatorStateExtension on _i16.NavigationService {
}
Future<dynamic>
replaceWithNestedUserListPembangunanViewInUserIndexViewRouter([
replaceWithNestedUserListPembangunanPageViewInUserIndexViewRouter([
int? routerId,
bool preventDuplicates = true,
Map<String, String>? parameters,
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
transition,
]) async {
return replaceWith<dynamic>(UserIndexViewRoutes.userListPembangunanView,
return replaceWith<dynamic>(UserIndexViewRoutes.userListPembangunanPageView,
id: routerId,
preventDuplicates: preventDuplicates,
parameters: parameters,

View File

@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
@ -13,6 +14,7 @@ import 'app/themes/app_theme.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
HttpOverrides.global = MyHttpOverrides();
await dotenv.load(fileName: ".env");
await setupAllLocator();
runApp(const MyApp());
@ -41,3 +43,12 @@ Future<void> setupAllLocator() async {
setupBottomSheetUi();
// setupSnackbarUi();
}
class MyHttpOverrides extends HttpOverrides {
@override
HttpClient createHttpClient(SecurityContext? context) {
return super.createHttpClient(context)
..badCertificateCallback =
(X509Certificate cert, String host, int port) => true;
}
}

View File

@ -84,6 +84,7 @@ class ProgressModel {
String? nama;
String? noTelpon;
int? no;
String? type;
ProgressModel(
{this.idProgress,
@ -94,7 +95,8 @@ class ProgressModel {
this.idMandor,
this.nama,
this.noTelpon,
this.no});
this.no,
this.type});
ProgressModel.fromJson(Map<String, dynamic> json) {
idProgress = json['id_progress'];
@ -106,6 +108,7 @@ class ProgressModel {
nama = json['nama'];
noTelpon = json['no_telpon'];
no = json['no'];
type = json['type'];
}
Map<String, dynamic> toJson() {
@ -119,6 +122,29 @@ class ProgressModel {
data['nama'] = nama;
data['no_telpon'] = noTelpon;
data['no'] = no;
data['type'] = type;
return data;
}
}
class MandorModel {
String? idMandor;
String? nama;
String? noTelpon;
MandorModel({this.idMandor, this.nama, this.noTelpon});
MandorModel.fromJson(Map<String, dynamic> json) {
idMandor = json['id_mandor'];
nama = json['nama'];
noTelpon = json['no_telpon'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id_mandor'] = idMandor;
data['nama'] = nama;
data['no_telpon'] = noTelpon;
return data;
}
}

View File

@ -36,6 +36,18 @@ class MandorTrackingIndexView extends StatelessWidget {
backgroundColor: mainColor,
elevation: 0,
automaticallyImplyLeading: false,
actions: [
// create logout button
IconButton(
onPressed: () {
model.logout();
},
icon: const Icon(
Icons.logout,
color: Colors.white,
),
),
],
),
body: ExtendedNavigator(
navigatorKey: StackedService.nestedNavigationKey(4),

View File

@ -5,10 +5,14 @@ import 'package:stacked_services/stacked_services.dart';
import '../../../app/app.locator.dart';
import '../../../app/app.logger.dart';
import '../../../app/app.router.dart';
import '../../../services/shared_prefs.dart';
class MandorTrackingIndexViewModel extends IndexTrackingViewModel {
final log = getLogger('MandorTrackingIndexViewModel');
final _navigationService = locator<NavigationService>();
final _mySharedPrefs = locator<MySharedPrefs>();
final _dialogService = locator<DialogService>();
final _snackbarService = locator<SnackbarService>();
final _bottomNavBarList = [
{'name': 'List', 'icon': Icons.list_alt_rounded, 'header': 'List Pemilik'},
@ -43,4 +47,26 @@ class MandorTrackingIndexViewModel extends IndexTrackingViewModel {
id: 4,
);
}
logout() {
_dialogService
.showConfirmationDialog(
title: 'Logout',
description: 'Apakah Anda yakin ingin logout?',
cancelTitle: 'Batal',
confirmationTitle: 'Logout',
)
.then((value) async {
if (value!.confirmed) {
await _mySharedPrefs.clear();
_navigationService.clearStackAndShow(Routes.loginScreenView);
_snackbarService.showSnackbar(
message: 'Logout berhasil',
duration: const Duration(seconds: 2),
);
}
});
// await _mySharedPrefs.clear();
// _navigationService.clearStackAndShow(Routes.loginScreenView);
}
}

View File

@ -92,7 +92,8 @@ class ListPemilikView extends StatelessWidget {
return Card(
child: GestureDetector(
onTap: () {
model.log.i('Card $index tapped');
model.checkProgress(
model.listProgress[index]);
},
child: ListTile(
title: Text(

View File

@ -1,3 +1,4 @@
import '../../../../app/app.bottomsheets.dart';
import '../../../../app/app.logger.dart';
import '../../../../app/core/custom_base_view_model.dart';
import '../../../../model/my_response_model.dart';
@ -49,4 +50,15 @@ class ListPemilikViewModel extends CustomBaseViewModel {
setBusy(false);
}
}
checkProgress(ProgressModel progressModel) async {
await bottomSheetService.showCustomSheet(
variant: BottomSheetType.tambahLihatProgressBottomSheetView,
title: 'Lihat Progress',
data: {
'idPerumahan': progressModel.idRumah,
'progressModel': progressModel,
},
);
}
}

View File

@ -31,21 +31,44 @@ class PengembangHomeView extends StatelessWidget {
}
return false;
},
child: SafeArea(
child: SizedBox(
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (model.progress < 1)
Center(
child: LinearProgressIndicator(
value: model.progress,
),
),
Expanded(
child: Visibility(
visible: model.status,
child: WebView(
// initialUrl: 'http://192.168.43.125/rekam-medis',
initialUrl: model.url,
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController) {
onWebViewCreated:
(WebViewController webViewController) {
// _controller.complete(webViewController);
model.webViewControllerCompleter.future
.then((value) => model.webVIewcontroller = value);
model.webViewControllerCompleter.complete(webViewController);
model.webViewControllerCompleter.future.then(
(value) => model.webVIewcontroller = value);
model.webViewControllerCompleter
.complete(webViewController);
},
onProgress: (int progress) {
double progressDouble = progress / 100;
LinearProgressIndicator(
value: progressDouble,
);
model.progress = progressDouble;
model.log.d('progress: $progressDouble');
if (progressDouble == 1) {
model.status = true;
}
model.notifyListeners();
// LinearProgressIndicator(
// value: progressDouble,
// );
// model.myEasyLoading.showProgress(progressDouble, "Loading Denah");
},
// javascriptChannels: <JavascriptChannel>{
@ -79,6 +102,12 @@ class PengembangHomeView extends StatelessWidget {
backgroundColor: const Color(0x00000000),
),
),
),
],
),
),
),
),
);
},
);

View File

@ -15,12 +15,18 @@ class PengembangHomeViewModel extends CustomBaseViewModel {
late String url;
late String level;
bool status = true;
double progress = 0;
late WebViewController webVIewcontroller;
final Completer<WebViewController> webViewControllerCompleter =
Completer<WebViewController>();
Future<void> init() async {
// Future.delayed(const Duration(milliseconds: 1), () {
// status = false;
// notifyListeners();
// });
globalVar.backPressed = "exitApp";
url = dotenv.env['url']!;
log.d(url);

View File

@ -38,6 +38,18 @@ class PengembangIndexView extends StatelessWidget {
backgroundColor: mainColor,
elevation: 0,
automaticallyImplyLeading: false,
actions: [
// create logout button
IconButton(
onPressed: () {
model.logout();
},
icon: const Icon(
Icons.logout,
color: Colors.white,
),
),
],
),
body: ExtendedNavigator(
navigatorKey: StackedService.nestedNavigationKey(3),

View File

@ -5,10 +5,14 @@ import 'package:stacked_services/stacked_services.dart';
import '../../../app/app.locator.dart';
import '../../../app/app.logger.dart';
import '../../../app/app.router.dart';
import '../../../services/shared_prefs.dart';
class PengembangIndexViewModel extends IndexTrackingViewModel {
final log = getLogger('PengembangIndexViewModel');
final _navigationService = locator<NavigationService>();
final _dialogService = locator<DialogService>();
final _mySharedPrefs = locator<MySharedPrefs>();
final _snackbarService = locator<SnackbarService>();
final _bottomNavBarList = [
{'name': 'List', 'icon': Icons.list_alt_rounded, 'header': 'List Pemilik'},
@ -43,4 +47,26 @@ class PengembangIndexViewModel extends IndexTrackingViewModel {
id: 3,
);
}
logout() {
_dialogService
.showConfirmationDialog(
title: 'Logout',
description: 'Apakah Anda yakin ingin logout?',
cancelTitle: 'Batal',
confirmationTitle: 'Logout',
)
.then((value) async {
if (value!.confirmed) {
await _mySharedPrefs.clear();
_navigationService.clearStackAndShow(Routes.loginScreenView);
_snackbarService.showSnackbar(
message: 'Logout berhasil',
duration: const Duration(seconds: 2),
);
}
});
// await _mySharedPrefs.clear();
// _navigationService.clearStackAndShow(Routes.loginScreenView);
}
}

View File

@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import '../../../../app/themes/app_colors.dart';
import '../../../../app/themes/app_text.dart';
import './pengembang_profil_view_model.dart';
class PengembangProfilView extends StatelessWidget {
@ -18,10 +20,126 @@ class PengembangProfilView extends StatelessWidget {
PengembangProfilViewModel model,
Widget? child,
) {
return const Scaffold(
body: Center(
child: Text(
'PengembangProfilView',
return Scaffold(
body: SafeArea(
child: Stack(
children: [
Padding(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(
child: Stack(
children: [
const CircleAvatar(
radius: 50,
backgroundColor: fontParagraphColor,
// child: model.imageBytes == null
// ? const Icon(
// Icons.person,
// size: 50,
// color: Colors.white,
// )
// : ClipRRect(
// borderRadius: BorderRadius.circular(50),
// child: Image.memory(
// model.imageBytes!,
// width: 100,
// height: 100,
// fit: BoxFit.cover,
// ),
// ),
child: Icon(
Icons.person,
size: 50,
color: Colors.white,
),
),
Positioned(
bottom: 0,
right: 0,
child: CircleAvatar(
radius: 15,
backgroundColor: mainColor,
child: IconButton(
onPressed: () {
// model.addImage();
},
icon: const Icon(
Icons.add,
color: backgroundColor,
size: 15,
)),
),
),
],
),
),
if (model.level == 'Mandor' &&
model.mandorModel != null &&
!model.isBusy)
Column(
children: [
const SizedBox(height: 20),
_FirstDetail(
text: model.mandorModel!.nama!,
icon: Icons.person,
),
const SizedBox(height: 20),
_FirstDetail(
text: model.mandorModel!.noTelpon!,
icon: Icons.phone,
),
],
),
if (model.level != null && model.level == 'Admin')
const Column(
children: [
SizedBox(height: 20),
_FirstDetail(
text: 'Admin',
icon: Icons.person,
),
SizedBox(height: 20),
_FirstDetail(
text:
'Perumahan Mutiara Alga, Kecamatan Suppa, Kabupaten Pinrang, Sulawesi Selatan, 91131',
icon: Icons.phone,
),
],
),
if (model.isBusy)
const Column(
children: [
SizedBox(height: 20),
Center(child: CircularProgressIndicator()),
],
),
],
),
),
// create rounded edit button at top right
Positioned(
top: 20,
right: 20,
child: CircleAvatar(
radius: 20,
backgroundColor: mainColor,
child: IconButton(
onPressed: () {},
icon: const Icon(
Icons.edit,
color: backgroundColor,
size: 15,
),
),
),
),
],
),
),
);
@ -29,3 +147,56 @@ class PengembangProfilView extends StatelessWidget {
);
}
}
class _FirstDetail extends StatelessWidget {
const _FirstDetail({
Key? key,
required this.text,
required this.icon,
}) : super(key: key);
final String text;
final IconData icon;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Icon(
icon,
color: mainColor,
size: 40,
),
const SizedBox(
width: 20,
),
Expanded(
child: Text(
text,
style: regularTextStyle.copyWith(
fontSize: 15,
color: mainGrey,
),
),
),
// created edit button
// Expanded(
// child: Align(
// alignment: Alignment.centerRight,
// child: IconButton(
// onPressed: () {},
// icon: const Icon(
// Icons.edit,
// color: mainColor,
// ),
// ),
// ),
// ),
],
),
);
}
}

View File

@ -1,5 +1,37 @@
import 'package:perumahan_bew/app/core/custom_base_view_model.dart';
import 'package:perumahan_bew/model/my_response_model.dart';
import 'package:perumahan_bew/model/rumah_model.dart';
import '../../../../app/app.logger.dart';
import '../../../../app/core/custom_base_view_model.dart';
class PengembangProfilViewModel extends CustomBaseViewModel {
Future<void> init() async {}
final log = getLogger('PengembangProfilViewModel');
String? level;
String? id;
MandorModel? mandorModel;
Future<void> init() async {
level = await mySharedPrefs.getString('level');
id = await mySharedPrefs.getString('id');
log.i('level: $level');
if (level == 'Mandor') {
await getData();
}
notifyListeners();
}
getData() async {
setBusy(true);
try {
var response = await httpService.get('mandor?id=$id');
MyResponseModel myResponseModel = MyResponseModel.fromJson(response.data);
mandorModel = MandorModel.fromJson(myResponseModel.data);
log.i('mandorModel: ${mandorModel!.nama}');
} catch (e) {
log.e('Error: $e');
} finally {
setBusy(false);
}
}
}

View File

@ -24,6 +24,9 @@ class PerumahanDetailView extends StatelessWidget {
) {
return Scaffold(
appBar: AppBar(
iconTheme: const IconThemeData(
color: backgroundColor,
),
title: Text(
'Perumahan Blok ${model.blok} , No. ${model.idRumah}',
style: const TextStyle(
@ -34,15 +37,7 @@ class PerumahanDetailView extends StatelessWidget {
backgroundColor: mainColor,
elevation: 0,
),
body: WillPopScope(
onWillPop: () async {
if (model.globalVar.backPressed == 'exitApp') {
// model.back();
return true;
// model.quitApp(context);
}
return false;
},
body: SafeArea(
child: Stack(
children: [
Padding(
@ -190,7 +185,8 @@ class PerumahanDetailView extends StatelessWidget {
return Card(
child: GestureDetector(
onTap: () {
model.log.i('Card $index tapped');
model.checkProgress(
model.progressModel![index]);
},
child: ListTile(
title: Text(
@ -245,6 +241,70 @@ class PerumahanDetailView extends StatelessWidget {
),
),
),
// create whatsapp button
Positioned(
top: model.level == 'Mandor' ? 80 : 15,
right: 15,
child: Container(
alignment: Alignment.center,
width: 50,
height: 50,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: greenColor,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.5),
spreadRadius: 2,
blurRadius: 7,
offset: const Offset(0, 3),
),
],
),
child: IconButton(
onPressed: () {
model.openWhatsapp();
},
icon: const Icon(
Icons.chat_outlined,
color: Colors.white,
size: 30,
),
),
),
),
// create call button
Positioned(
top: model.level == 'Mandor' ? 145 : 80,
right: 15,
child: Container(
alignment: Alignment.center,
width: 50,
height: 50,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: mainColor,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.5),
spreadRadius: 2,
blurRadius: 7,
offset: const Offset(0, 3),
),
],
),
child: IconButton(
onPressed: () {
model.call();
},
icon: const Icon(
Icons.call_outlined,
color: Colors.white,
size: 30,
),
),
),
),
],
),
),

View File

@ -1,3 +1,5 @@
import 'package:url_launcher/url_launcher.dart';
import '../../../../app/app.bottomsheets.dart';
import '../../../../app/app.logger.dart';
import '../../../../app/app.router.dart';
@ -60,7 +62,9 @@ class PerumahanDetailViewModel extends CustomBaseViewModel {
var res = await bottomSheetService.showCustomSheet(
variant: BottomSheetType.tambahLihatProgressBottomSheetView,
title: 'Form Tambah Progress',
data: idPerumahan,
data: {
'idPerumahan': idPerumahan,
},
);
if (res!.confirmed) {
@ -72,4 +76,37 @@ class PerumahanDetailViewModel extends CustomBaseViewModel {
await getData();
}
}
checkProgress(ProgressModel progressModel) async {
await bottomSheetService.showCustomSheet(
variant: BottomSheetType.tambahLihatProgressBottomSheetView,
title: 'Lihat Progress',
data: {
'idPerumahan': idPerumahan,
'progressModel': progressModel,
},
);
}
openWhatsapp() async {
// open whatsapp using url
String noTelpon = rumahModel!.noTelpon!;
// convert the number to international format
noTelpon = noTelpon.replaceAll(RegExp(r'[^0-9]'), '');
noTelpon = '62${noTelpon.substring(1)}';
log.i('no_telpon: $noTelpon');
final url = Uri.parse('https://wa.me/$noTelpon');
if (!await launchUrl(url)) {
throw Exception('Could not launch $url');
}
}
call() async {
final Uri callUri = Uri(scheme: 'tel', path: rumahModel!.noTelpon!);
if (!await launchUrl(callUri)) {
throw 'Could not launch ${callUri.toString()}';
}
}
}

View File

@ -21,7 +21,7 @@ class PlayVideoDialogView extends StatelessWidget {
return ViewModelBuilder<PlayVideoDialogViewModel>.reactive(
viewModelBuilder: () => PlayVideoDialogViewModel(),
onViewModelReady: (PlayVideoDialogViewModel model) async {
await model.init();
await model.init(request!.data);
},
builder: (
BuildContext context,
@ -34,7 +34,8 @@ class PlayVideoDialogView extends StatelessWidget {
// height: 450,
padding: const EdgeInsets.all(15),
child: SingleChildScrollView(
child: Column(
child: model.status != null
? Column(
mainAxisSize: MainAxisSize.min,
children: [
AspectRatio(
@ -42,15 +43,20 @@ class PlayVideoDialogView extends StatelessWidget {
child: NativeVideoPlayerView(
onViewReady: (controller) async {
final videoSource = await VideoSource.init(
path: request!.data.toString(),
type: VideoSourceType.file,
path: model.status == 'file'
? model.path!
: model.url!,
type: model.status == 'file'
? VideoSourceType.file
: VideoSourceType.network,
);
await controller.loadVideoSource(videoSource);
model.nativeVideoPlayerController = controller;
model.notifyListeners();
model.nativeVideoPlayerController!.play();
// loop video
model.nativeVideoPlayerController!.onPlaybackEnded
model.nativeVideoPlayerController!
.onPlaybackEnded
.addListener(() {
model.nativeVideoPlayerController!.seekTo(0);
model.nativeVideoPlayerController!.play();
@ -76,8 +82,10 @@ class PlayVideoDialogView extends StatelessWidget {
),
),
],
),
),
)
: const Center(
child: CircularProgressIndicator(),
)),
),
);
},

View File

@ -4,5 +4,14 @@ import 'package:perumahan_bew/app/core/custom_base_view_model.dart';
class PlayVideoDialogViewModel extends CustomBaseViewModel {
NativeVideoPlayerController? nativeVideoPlayerController;
bool playVideo = true;
Future<void> init() async {}
String? status;
String? url;
String? path;
Future<void> init(data) async {
status = data['status'];
url = data['url'];
path = data['path'];
}
}

View File

@ -47,7 +47,7 @@ class SplashScreenView extends StatelessWidget {
),
const Expanded(child: SizedBox()),
Text(
'Jl. Raya Mutiara Alga No. 1, \nKec. Ciputat, Kota Tangerang Selatan, \nBanten 15412',
'Kecamatan Suppa, \nKabupaten Pinrang,\nSulawesi Selatan,91131',
style: regularTextStyle.copyWith(
color: backgroundColor,
fontSize: 12,

View File

@ -1,10 +1,12 @@
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:native_video_player/native_video_player.dart';
import 'package:perumahan_bew/app/themes/app_text.dart';
import 'package:perumahan_bew/ui/widgets/my_button.dart';
import 'package:perumahan_bew/ui/widgets/my_textformfield.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:validatorless/validatorless.dart';
import '../../../app/themes/app_colors.dart';
@ -70,7 +72,7 @@ class TambahLihatProgressBottomSheetView extends StatelessWidget {
children: [
Center(
child: Text(
'${request!.title!} ${request!.data.toString().toUpperCase()}',
'${request!.title!} ${request!.data['idPerumahan'].toString().toUpperCase()}',
style: boldTextStyle,
),
),
@ -94,8 +96,8 @@ class TambahLihatProgressBottomSheetView extends StatelessWidget {
borderRadius:
BorderRadius.circular(10),
child: GestureDetector(
onTap: () =>
model.showImage(context),
onTap: () => model.showImage(
context, 'file', null),
child: Image.memory(
model.imageVideoBytes!,
fit: BoxFit.fill,
@ -139,12 +141,73 @@ class TambahLihatProgressBottomSheetView extends StatelessWidget {
),
),
))
: (model.progressModel != null
? (model.imageVideoType == 'image'
? ClipRRect(
borderRadius:
BorderRadius.circular(10),
child: GestureDetector(
onTap: () => model.showImage(
context,
'network',
dotenv.env['url']! +
model.progressModel!
.img!),
child: Image.network(
dotenv.env['url']! +
model.progressModel!
.img!,
fit: BoxFit.fill,
)),
)
: ClipRRect(
borderRadius:
BorderRadius.circular(10),
child: AspectRatio(
aspectRatio: 16 / 11,
child: NativeVideoPlayerView(
onViewReady:
(controller) async {
final videoSource =
await VideoSource.init(
path: dotenv.env['url']! +
model.progressModel!
.img!,
type: VideoSourceType
.network,
);
await controller
.loadVideoSource(
videoSource);
model.nativeVideoPlayerController =
controller;
model.notifyListeners();
model
.nativeVideoPlayerController!
.play();
// loop video
model
.nativeVideoPlayerController!
.onPlaybackEnded
.addListener(() {
model
.nativeVideoPlayerController!
.seekTo(0);
model
.nativeVideoPlayerController!
.play();
});
},
),
),
))
: const Icon(
Icons.credit_card_rounded,
color: Colors.white,
size: 50,
)),
),
),
if (model.progressModel == null)
Positioned(
bottom: 0,
right: 0,
@ -163,6 +226,38 @@ class TambahLihatProgressBottomSheetView extends StatelessWidget {
),
),
),
if (model.imageVideoType == 'video')
Positioned(
bottom: 0,
left: 0,
top: 0,
right: 0,
child: Container(
alignment: Alignment.center,
child: CircleAvatar(
radius: 15,
backgroundColor: sixthGrey,
child: IconButton(
onPressed: () {
if (model.progressModel != null) {
model.playVideo(
'network',
dotenv.env['url']! +
model.progressModel!.img!);
} else {
model.playVideo(
'file', model.imageVideoPath);
}
},
icon: const Icon(
Icons.play_arrow,
color: backgroundColor3,
size: 15,
),
),
),
),
),
],
),
),
@ -174,8 +269,16 @@ class TambahLihatProgressBottomSheetView extends StatelessWidget {
controller: model.ketController,
validator:
Validatorless.required('Keterangan harus diisi'),
readOnly: model.progressModel != null,
),
const SizedBox(height: 20),
if (model.progressModel != null)
MyTextFormField(
labelText: 'Waktu',
controller: model.waktuController,
readOnly: true,
),
if (model.progressModel == null)
Center(
child: SizedBox(
width: 250,
@ -201,11 +304,13 @@ class TambahLihatProgressBottomSheetView extends StatelessWidget {
.then((value) async {
if (value!.confirmed) {
// stop playing video
model.nativeVideoPlayerController?.pause();
model.nativeVideoPlayerController
?.pause();
model.nativeVideoPlayerController
?.removeListener(() {
// model.nativeVideoPlayerController?.dispose();
model.nativeVideoPlayerController = null;
model.nativeVideoPlayerController =
null;
});
model.nativeVideoPlayerController = null;
@ -222,6 +327,82 @@ class TambahLihatProgressBottomSheetView extends StatelessWidget {
),
),
),
if (model.level == 'Pemilik Rumah' &&
model.mandorModel != null)
Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 20),
MyTextFormField(
labelText: 'Mandor',
controller: model.mandorController,
readOnly: true,
),
const SizedBox(height: 20),
MyTextFormField(
labelText: 'No. Telp Mandor',
controller: model.noHpController,
readOnly: true,
),
const SizedBox(height: 20),
// create row with 2 rounded icon , one is chat whatsapp , one is call
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircleAvatar(
radius: 15,
backgroundColor: greenColor,
child: IconButton(
onPressed: () async {
// model.openWhatsapp();
String noTelpon =
model.noHpController!.text;
// convert the number to international format
noTelpon = noTelpon.replaceAll(
RegExp(r'[^0-9]'), '');
noTelpon = '62${noTelpon.substring(1)}';
// log.i('no_telpon: $noTelpon');
final url =
Uri.parse('https://wa.me/$noTelpon');
if (!await launchUrl(url)) {
throw Exception(
'Could not launch $url');
}
},
icon: const Icon(
Icons.chat,
color: backgroundColor3,
size: 15,
),
),
),
const SizedBox(width: 20),
CircleAvatar(
radius: 15,
backgroundColor: mainColor,
child: IconButton(
onPressed: () async {
// model.callPhone();
final Uri callUri = Uri(
scheme: 'tel',
path: model.noHpController!.text);
if (!await launchUrl(callUri)) {
throw 'Could not launch ${callUri.toString()}';
}
},
icon: const Icon(
Icons.call,
color: backgroundColor3,
size: 15,
),
),
),
],
),
],
),
],
),
),

View File

@ -5,22 +5,31 @@ import 'package:easy_image_viewer/easy_image_viewer.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:native_video_player/native_video_player.dart';
import 'package:perumahan_bew/model/rumah_model.dart';
import '../../../app/app.dialogs.dart';
import '../../../app/app.logger.dart';
import '../../../app/core/custom_base_view_model.dart';
import '../../../app/themes/app_colors.dart';
import '../../../model/my_response_model.dart';
class TambahLihatProgressBottomSheetViewModel extends CustomBaseViewModel {
final log = getLogger('TambahLihatProgressBottomSheetViewModel');
NativeVideoPlayerController? nativeVideoPlayerController;
String? level;
MandorModel? mandorModel;
TextEditingController? mandorController = TextEditingController();
TextEditingController? noHpController = TextEditingController();
String? idPerumahan;
ProgressModel? progressModel;
// form variable
final globalKey = GlobalKey<FormState>();
TextEditingController? ketController = TextEditingController();
TextEditingController? waktuController = TextEditingController();
// image video variable
// image picker
@ -29,9 +38,40 @@ class TambahLihatProgressBottomSheetViewModel extends CustomBaseViewModel {
XFile? imageVideoFile;
Uint8List? imageVideoBytes;
String? imageVideoType;
Future<void> init(String data) async {
Future<void> init(data) async {
globalVar.backPressed = "exitApp";
idPerumahan = data;
idPerumahan = data['idPerumahan'];
// log.i(data['progressModel']);
if (data['progressModel'] != null) {
progressModel = data['progressModel'];
ketController!.text = progressModel!.ket!;
waktuController!.text = progressModel!.createdAt!;
log.i('type: ${progressModel!.type}');
imageVideoType = progressModel!.type;
}
level = await mySharedPrefs.getString('level');
if (level == 'Pemilik Rumah') {
await getData();
}
}
getData() async {
setBusy(true);
try {
var response =
await httpService.get('mandor?id=${progressModel!.idMandor}');
MyResponseModel myResponseModel = MyResponseModel.fromJson(response.data);
mandorModel = MandorModel.fromJson(myResponseModel.data);
log.i('mandorModel: ${mandorModel!.nama}');
mandorController!.text = mandorModel!.nama!;
noHpController!.text = mandorModel!.noTelpon!;
} catch (e) {
log.e('Error: $e');
} finally {
setBusy(false);
}
}
addImage(String type) async {
@ -79,13 +119,20 @@ class TambahLihatProgressBottomSheetViewModel extends CustomBaseViewModel {
});
}
playVideo() async {
playVideo(String status, String? url) async {
// play video by imageVideoPath
log.i('play video');
log.i(status);
log.i(url);
await dialogService.showCustomDialog(
variant: DialogType.playVideoDialogView,
title: 'Video',
data: imageVideoPath,
data: {
'status': status,
'url': url,
'path': imageVideoPath,
},
);
}
@ -117,12 +164,19 @@ class TambahLihatProgressBottomSheetViewModel extends CustomBaseViewModel {
}
}
showImage(BuildContext context) async {
showImage(BuildContext context, String status, String? url) async {
log.i(status);
log.i(url);
showImageViewer(
context,
Image.memory(
status == 'file'
? Image.memory(
imageVideoBytes!,
fit: BoxFit.fill,
).image
: Image.network(
url!,
fit: BoxFit.fill,
).image,
swipeDismissible: true,
doubleTapZoomable: true,

View File

@ -1,69 +0,0 @@
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import 'package:webview_flutter/webview_flutter.dart';
import './user_home_view_model.dart';
class UserHomeView extends StatelessWidget {
const UserHomeView({super.key});
@override
Widget build(BuildContext context) {
return ViewModelBuilder<UserHomeViewModel>.nonReactive(
viewModelBuilder: () => UserHomeViewModel(),
onViewModelReady: (UserHomeViewModel model) async {
await model.init();
},
disposeViewModel: false,
fireOnViewModelReadyOnce: true,
builder: (
BuildContext context,
UserHomeViewModel model,
Widget? child,
) {
return Scaffold(
body: WebView(
// initialUrl: 'http://192.168.43.125/rekam-medis',
initialUrl: 'http://20.20.20.25/perumahan',
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController) {
// _controller.complete(webViewController);
model.webViewControllerCompleter.future
.then((value) => model.webVIewcontroller = value);
model.webViewControllerCompleter.complete(webViewController);
},
onProgress: (int progress) {
double progressDouble = progress / 100;
model.myEasyLoading.showProgress(progressDouble, "Loading Denah");
},
// javascriptChannels: <JavascriptChannel>{
// _toasterJavascriptChannel(context),
// },
javascriptChannels: <JavascriptChannel>{
JavascriptChannel(
name: 'messageHandler',
onMessageReceived: (JavascriptMessage message) {
model.log.d(message.message);
// dev.i("message from the web view=\"${message.message}\"");
// if (message.message == "coba") {
// dev.i("sini untuk coba");
// controller.runJavascript("coba22('heheheh')");
// }
},
),
},
// navigationDelegate: (NavigationRequest request) async {},
onPageStarted: (String url) {},
onPageFinished: (String url) {
// dev.i('Page finished loading: $url');
model.myEasyLoading.dismissLoading();
},
gestureNavigationEnabled: true,
backgroundColor: const Color(0x00000000),
),
);
},
);
}
}

View File

@ -1,25 +0,0 @@
import 'dart:async';
import 'dart:io';
import 'package:webview_flutter/webview_flutter.dart';
import '../../../../app/app.locator.dart';
import '../../../../app/app.logger.dart';
import '../../../../app/core/custom_base_view_model.dart';
import '../../../../services/my_easyloading.dart';
class UserHomeViewModel extends CustomBaseViewModel {
final log = getLogger('UserHomeViewModel');
final _myEasyLoading = locator<MyEasyLoading>();
get myEasyLoading => _myEasyLoading;
late WebViewController webVIewcontroller;
final Completer<WebViewController> webViewControllerCompleter =
Completer<WebViewController>();
Future<void> init() async {
if (Platform.isAndroid) {
WebView.platform = SurfaceAndroidWebView();
}
}
}

View File

@ -38,10 +38,22 @@ class UserIndexView extends StatelessWidget {
backgroundColor: mainColor,
elevation: 0,
automaticallyImplyLeading: false,
actions: [
// create logout button
IconButton(
onPressed: () {
model.logout();
},
icon: const Icon(
Icons.logout,
color: Colors.white,
),
),
],
),
// extendBody: true,
body: ExtendedNavigator(
navigatorKey: StackedService.nestedNavigationKey(2),
navigatorKey: StackedService.nestedNavigationKey(7),
router: UserIndexViewRouter(),
),
bottomNavigationBar: StylishBottomBar(

View File

@ -5,10 +5,14 @@ import 'package:stacked_services/stacked_services.dart';
import '../../../app/app.locator.dart';
import '../../../app/app.logger.dart';
import '../../../app/app.router.dart';
import '../../../services/shared_prefs.dart';
class UserIndexViewModel extends IndexTrackingViewModel {
final log = getLogger('UserIndexViewModel');
final _navigationService = locator<NavigationService>();
final _dialogService = locator<DialogService>();
final _mySharedPrefs = locator<MySharedPrefs>();
final _snackbarService = locator<SnackbarService>();
final _bottomNavBarList = [
{
@ -16,22 +20,18 @@ class UserIndexViewModel extends IndexTrackingViewModel {
'icon': Icons.list_alt_rounded,
'header': 'List Perumahan'
},
{'name': 'Denah', 'icon': Icons.home_outlined, 'header': 'Denah Perumahan'},
{'name': 'Profil', 'icon': Icons.person_outline, 'header': 'Profil'},
];
List<Map<String, dynamic>> get bottomNavBarList => _bottomNavBarList;
final List<String> _views = [
UserIndexViewRoutes.userListPembangunanView,
UserIndexViewRoutes.userHomeView,
UserIndexViewRoutes.userListPembangunanPageView,
UserIndexViewRoutes.userProfileView,
];
String header = 'Denah Perumahan';
Future<void> init() async {
setIndex(1);
}
Future<void> init() async {}
void handleNavigation(int index) {
log.d("handleNavigation: $index");
@ -43,7 +43,29 @@ class UserIndexViewModel extends IndexTrackingViewModel {
header = _bottomNavBarList[index]['header'] as String;
_navigationService.navigateTo(
_views[index],
id: 2,
id: 7,
);
}
logout() {
_dialogService
.showConfirmationDialog(
title: 'Logout',
description: 'Apakah Anda yakin ingin logout?',
cancelTitle: 'Batal',
confirmationTitle: 'Logout',
)
.then((value) async {
if (value!.confirmed) {
await _mySharedPrefs.clear();
_navigationService.clearStackAndShow(Routes.loginScreenView);
_snackbarService.showSnackbar(
message: 'Logout berhasil',
duration: const Duration(seconds: 2),
);
}
});
// await _mySharedPrefs.clear();
// _navigationService.clearStackAndShow(Routes.loginScreenView);
}
}

View File

@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
import 'package:perumahan_bew/ui/views/user_index/user_list_pembangunan/user_list_pembangunan_view.dart';
import 'package:stacked/stacked.dart';
import './user_list_pembangunan_page_view_model.dart';
class UserListPembangunanPageView extends StatelessWidget {
const UserListPembangunanPageView({super.key});
@override
Widget build(BuildContext context) {
return ViewModelBuilder<UserListPembangunanPageViewModel>.nonReactive(
viewModelBuilder: () => UserListPembangunanPageViewModel(),
onViewModelReady: (UserListPembangunanPageViewModel model) async {
await model.init();
},
builder: (
BuildContext context,
UserListPembangunanPageViewModel model,
Widget? child,
) {
return const UserListPembangunanView();
},
);
}
}

View File

@ -0,0 +1,5 @@
import 'package:perumahan_bew/app/core/custom_base_view_model.dart';
class UserListPembangunanPageViewModel extends CustomBaseViewModel {
Future<void> init() async {}
}

View File

@ -25,25 +25,15 @@ class UserListPembangunanView extends StatelessWidget {
body: Column(
children: [
const SizedBox(height: 20),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
MyTopWidget(
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: MyTopWidget(
icon: Icons.list_alt_outlined,
title: 'Pembangunan',
subtitle: '15 kali',
subtitle:
'${model.listProgress != null ? model.listProgress!.length : 0} Progress',
// lastUpdate: '31/02/15 - 10.00 am',
),
MyTopWidget(
icon: Icons.home,
title: 'Progress',
subtitle: '76 %',
// lastUpdate: '31/02/15 - 10.00 am',
),
],
),
),
const SizedBox(height: 25),
Expanded(
@ -63,22 +53,42 @@ class UserListPembangunanView extends StatelessWidget {
],
),
// create a listview with 20 dummy data on card and scrollable
child: ListView.builder(
child: model.isBusy
? const Center(child: CircularProgressIndicator())
: model.status == false
? const Center(child: Text('Error Loading Data'))
: model.listProgress!.isEmpty
? const Center(child: Text('Data Kosong'))
: ListView.builder(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 10),
itemCount: 20,
itemCount: model.listProgress!.length,
itemBuilder: (context, index) {
return Card(
child: GestureDetector(
onTap: () {
model.log.i('Card $index tapped');
},
child: ListTile(
title: Text('1/02/15 - 10.00 am',
title: Text(
model.listProgress![index]
.createdAt!,
style: boldTextStyle.copyWith(
fontSize: 13, color: mainColor)),
subtitle: Text('Progress $index'),
trailing: Text('Pembangunan $index'),
fontSize: 13,
color: mainColor,
),
),
subtitle: Text(model
.listProgress![index].ket!),
// create icon show
trailing: CircleAvatar(
backgroundColor: mainColor,
child: IconButton(
icon: const Icon(
Icons.list_alt_outlined,
color: backgroundColor,
),
onPressed: () {
model.checkProgress(model
.listProgress![index]);
},
),
),
),
);

View File

@ -1,8 +1,52 @@
import 'package:perumahan_bew/app/core/custom_base_view_model.dart';
import 'package:perumahan_bew/model/rumah_model.dart';
import '../../../../app/app.bottomsheets.dart';
import '../../../../app/app.logger.dart';
import '../../../../app/core/custom_base_view_model.dart';
import '../../../../model/my_response_model.dart';
class UserListPembangunanViewModel extends CustomBaseViewModel {
final log = getLogger('UserListPembangunanViewModel');
Future<void> init() async {}
bool status = false;
RumahDetailModel? rumahDetailModel;
RumahModel? rumahModel;
List<ProgressModel>? listProgress;
Future<void> init() async {
String idRumah = await mySharedPrefs.getString('id') ?? '';
if (idRumah.isNotEmpty) {
await getData(idRumah);
}
}
getData(String idRumah) async {
setBusy(true);
try {
var response = await httpService.get('rumah?id=$idRumah');
MyResponseModel myResponse = MyResponseModel.fromJson(response.data);
// log.i(myResponse.toJson());
RumahDetailModel rumahDetailModel =
RumahDetailModel.fromJson(myResponse.data);
listProgress = rumahDetailModel.progressModel;
status = true;
} catch (e) {
status = false;
log.e(e);
} finally {
setBusy(false);
}
}
checkProgress(ProgressModel progressModel) async {
await bottomSheetService.showCustomSheet(
variant: BottomSheetType.tambahLihatProgressBottomSheetView,
title: 'Lihat Progress',
data: {
'idPerumahan': progressModel.idRumah,
'progressModel': progressModel,
},
);
}
}

View File

@ -21,100 +21,119 @@ class UserProfileView extends StatelessWidget {
Widget? child,
) {
return Scaffold(
body: Stack(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(20),
child: Stack(
children: [
Column(
children: [
const SizedBox(
height: 25,
),
// create a rounded container
Center(
child: Container(
width: 150,
height: 150,
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
image: const NetworkImage(
'http://kicap-karan.com/assets/img/me.jpg',
),
fit: BoxFit.cover,
onError: (exception, stackTrace) {
model.log.e('Error: $exception');
},
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.5),
spreadRadius: 2,
blurRadius: 7,
offset: const Offset(0, 3),
),
],
),
),
),
const SizedBox(
height: 15,
),
Center(
child: Text(
'Kicap Karan',
style: boldTextStyle.copyWith(
fontSize: 18,
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 30, vertical: 10),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
child: Stack(
children: [
for (var i = 0; i < 10; i++) const _DetailChild(),
],
const CircleAvatar(
radius: 50,
backgroundColor: fontParagraphColor,
// child: model.imageBytes == null
// ? const Icon(
// Icons.person,
// size: 50,
// color: Colors.white,
// )
// : ClipRRect(
// borderRadius: BorderRadius.circular(50),
// child: Image.memory(
// model.imageBytes!,
// width: 100,
// height: 100,
// fit: BoxFit.cover,
// ),
// ),
child: Icon(
Icons.person,
size: 50,
color: Colors.white,
),
),
),
),
const SizedBox(
height: 15,
),
],
),
Positioned(
top: 15,
right: 65,
bottom: 0,
right: 0,
child: CircleAvatar(
radius: 15,
backgroundColor: mainColor,
child: IconButton(
onPressed: () {
model.log.i('Edit Profile');
// model.addImage();
},
icon: const Icon(
Icons.add,
color: fontColor,
size: 15,
)),
),
),
],
),
),
if (!model.isBusy &&
model.status == true &&
model.rumahModel != null)
Expanded(
child: Column(
children: [
const SizedBox(
height: 15,
),
_DetailChild(
text: model.rumahModel!.pemilik!,
icon: Icons.person,
),
_DetailChild(
text: model.rumahModel!.tanggalPembelian!,
icon: Icons.calendar_today_outlined,
),
_DetailChild(
text: "Rp. ${model.rumahModel!.harga!}",
icon: Icons.attach_money_outlined,
),
_DetailChild(
text: 'Rp. ${model.rumahModel!.cicilan!}',
icon: Icons.money_outlined,
),
],
),
),
if (model.isBusy)
const Expanded(
child: Center(
child: CircularProgressIndicator(),
),
),
],
),
// create rounded button with edit on top right
Positioned(
top: 0,
right: 0,
child: CircleAvatar(
radius: 15,
backgroundColor: mainColor,
child: IconButton(
onPressed: () {
// model.addImage();
},
icon: const Icon(
Icons.edit,
color: mainColor,
size: 30,
),
),
),
Positioned(
top: 15,
right: 15,
child: IconButton(
onPressed: () {
model.log.i('Logout');
model.logout();
},
icon: const Icon(
Icons.logout,
color: mainColor,
size: 30,
),
color: fontColor,
size: 15,
)),
),
),
],
),
),
),
);
},
);
@ -124,8 +143,13 @@ class UserProfileView extends StatelessWidget {
class _DetailChild extends StatelessWidget {
const _DetailChild({
Key? key,
required this.text,
required this.icon,
}) : super(key: key);
final String text;
final IconData icon;
@override
Widget build(BuildContext context) {
return Padding(
@ -133,8 +157,8 @@ class _DetailChild extends StatelessWidget {
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const Icon(
Icons.maps_home_work_outlined,
Icon(
icon,
color: mainColor,
size: 40,
),
@ -142,7 +166,7 @@ class _DetailChild extends StatelessWidget {
width: 20,
),
Text(
'Jln 2, Blok C, No 2',
text,
style: regularTextStyle.copyWith(
fontSize: 15,
color: mainGrey,

View File

@ -1,11 +1,43 @@
import 'package:perumahan_bew/app/core/custom_base_view_model.dart';
import '../../../../app/app.logger.dart';
import '../../../../model/my_response_model.dart';
import '../../../../model/rumah_model.dart';
class UserProfileViewModel extends CustomBaseViewModel {
final log = getLogger('UserProfileViewModel');
RumahDetailModel? rumahDetailModel;
RumahModel? rumahModel;
bool status = false;
String? idRumah;
Future<void> init() async {}
Future<void> init() async {
idRumah = await mySharedPrefs.getString('id') ?? '';
if (idRumah != '') {
await getData();
}
// await getData();
}
getData() async {
setBusy(true);
try {
var response = await httpService.get('rumah?id=$idRumah');
MyResponseModel myResponseModel = MyResponseModel.fromJson(response.data);
// log.i('myResponseModel: ${myResponseModel.data}');
rumahDetailModel = RumahDetailModel.fromJson(myResponseModel.data);
rumahModel = rumahDetailModel!.rumahModel;
// rumahModel = RumahModel.fromJson(myResponseModel.data);
log.i('rumahModel: ${rumahModel!.toJson()}');
status = true;
} catch (e) {
log.e('Error: $e');
status = false;
} finally {
setBusy(false);
}
}
logout() {
log.i('logout');

View File

@ -7,9 +7,13 @@
#include "generated_plugin_registrant.h"
#include <file_selector_linux/file_selector_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
}

View File

@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
file_selector_linux
url_launcher_linux
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST

View File

@ -6,13 +6,13 @@ import FlutterMacOS
import Foundation
import file_selector_macos
import location
import path_provider_foundation
import shared_preferences_foundation
import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
LocationPlugin.register(with: registry.registrar(forPlugin: "LocationPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
}

View File

@ -512,30 +512,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.1"
location:
dependency: "direct main"
description:
name: location
sha256: "9051959f6f2ccadd887b28b66e9cbbcc25b6838e37cf9e894c421ccc0ebf80b5"
url: "https://pub.dev"
source: hosted
version: "4.4.0"
location_platform_interface:
dependency: transitive
description:
name: location_platform_interface
sha256: "62eeaf1658e92e4459b727f55a3c328eccbac8ba043fa6d262ac5286ad48384c"
url: "https://pub.dev"
source: hosted
version: "2.3.0"
location_web:
dependency: transitive
description:
name: location_web
sha256: "6c08c408a040534c0269c4ff9fe17eebb5a36dea16512fbaf116b9c8bc21545b"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
logger:
dependency: transitive
description:
@ -949,6 +925,70 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.2.2"
url_launcher:
dependency: "direct main"
description:
name: url_launcher
sha256: "47e208a6711459d813ba18af120d9663c20bdf6985d6ad39fe165d2538378d27"
url: "https://pub.dev"
source: hosted
version: "6.1.14"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
sha256: "31222ffb0063171b526d3e569079cf1f8b294075ba323443fdc690842bfd4def"
url: "https://pub.dev"
source: hosted
version: "6.2.0"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
sha256: "4ac97281cf60e2e8c5cc703b2b28528f9b50c8f7cebc71df6bdf0845f647268a"
url: "https://pub.dev"
source: hosted
version: "6.2.0"
url_launcher_linux:
dependency: transitive
description:
name: url_launcher_linux
sha256: "9f2d390e096fdbe1e6e6256f97851e51afc2d9c423d3432f1d6a02a8a9a8b9fd"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
url_launcher_macos:
dependency: transitive
description:
name: url_launcher_macos
sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234
url: "https://pub.dev"
source: hosted
version: "3.1.0"
url_launcher_platform_interface:
dependency: transitive
description:
name: url_launcher_platform_interface
sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
url_launcher_web:
dependency: transitive
description:
name: url_launcher_web
sha256: ba140138558fcc3eead51a1c42e92a9fb074a1b1149ed3c73e66035b2ccd94f2
url: "https://pub.dev"
source: hosted
version: "2.0.19"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
sha256: "7754a1ad30ee896b265f8d14078b0513a4dba28d358eabb9d5f339886f4a1adc"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
validatorless:
dependency: "direct main"
description:

View File

@ -42,7 +42,7 @@ dependencies:
path_provider: ^2.0.9
dio:
flutter_easyloading:
location: ^4.4.0
# location: ^4.4.0
# flutter_inappwebview:
webview_flutter: ^3.0.4
google_fonts:
@ -54,6 +54,7 @@ dependencies:
omni_datetime_picker:
easy_image_viewer:
native_video_player:
url_launcher:
dev_dependencies:
flutter_test:

View File

@ -7,8 +7,11 @@
#include "generated_plugin_registrant.h"
#include <file_selector_windows/file_selector_windows.h>
#include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
FileSelectorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorWindows"));
UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
}

View File

@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
file_selector_windows
url_launcher_windows
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST