first commit

This commit is contained in:
kicap
2024-02-04 20:57:23 +08:00
commit 27c529dad9
162 changed files with 8557 additions and 0 deletions

52
lib/app/app.dart Normal file
View File

@ -0,0 +1,52 @@
import 'package:stacked_services/stacked_services.dart';
import 'package:stacked/stacked_annotations.dart';
import '../services/global_var.dart';
import '../services/my_easyloading.dart';
import '../services/my_storage.dart';
import '../services/my_tts.dart';
import '../services/other_function.dart';
import '../ui/views/action_dialog/action_dialog_view.dart';
import '../ui/views/nomor_telpon_dialog/nomor_telpon_dialog_view.dart';
import '../ui/views/splash_screen/splash_screen_view.dart';
import '../ui/views/user_tracking_index/list_kamus_kesehatan/list_kamus_kesehatan_view.dart';
import '../ui/views/user_tracking_index/profil_user/profil_user_view.dart';
import '../ui/views/user_tracking_index/user_tracking_index_view.dart';
@StackedApp(
routes: [
MaterialRoute(page: SplashScreenView, initial: true),
MaterialRoute(
page: UserTrackingIndexView,
children: [
MaterialRoute(page: ListKamusKesehatanView),
// MaterialRoute(page: TampilkanListView),
MaterialRoute(page: ProfilUserView),
],
),
],
dialogs: [
StackedDialog(classType: ActionDialogView),
StackedDialog(classType: NomorTelponDialogView)
],
dependencies: [
LazySingleton(classType: NavigationService),
LazySingleton(classType: DialogService),
LazySingleton(classType: SnackbarService),
LazySingleton(classType: BottomSheetService),
// this below is mine
LazySingleton(classType: MyEasyLoading),
// LazySingleton(classType: MyHttpServices),
LazySingleton(classType: GlobalVar),
LazySingleton(classType: MyFunction),
// LazySingleton(classType: MySharedPrefs)
LazySingleton(classType: MyTts),
LazySingleton(classType: MyStorage),
],
logger: StackedLogger(),
// bottomsheets: [
// StackedBottomsheet(classType: DetailSuaraBottomSheetView),
// StackedBottomsheet(classType: DetailSuaraPemilihBottomSheetView)
// ],
)
class App {}

32
lib/app/app.dialogs.dart Normal file
View File

@ -0,0 +1,32 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// **************************************************************************
// StackedDialogGenerator
// **************************************************************************
import 'package:stacked_services/stacked_services.dart';
import '../model/istilah_model.dart';
import 'app.locator.dart';
import '../ui/views/action_dialog/action_dialog_view.dart';
import '../ui/views/nomor_telpon_dialog/nomor_telpon_dialog_view.dart';
enum DialogType {
actionDialogView,
nomorTelponDialogView,
}
void setupDialogUi() {
final dialogService = locator<DialogService>();
final Map<DialogType, DialogBuilder> builders = {
DialogType.actionDialogView: (context, request, completer) =>
ActionDialogView(
request: request as DialogRequest<IstilahModel>,
completer: completer),
DialogType.nomorTelponDialogView: (context, request, completer) =>
NomorTelponDialogView(request: request, completer: completer),
};
dialogService.registerCustomDialogBuilders(builders);
}

41
lib/app/app.locator.dart Normal file
View File

@ -0,0 +1,41 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// **************************************************************************
// StackedLocatorGenerator
// **************************************************************************
// ignore_for_file: public_member_api_docs, implementation_imports, depend_on_referenced_packages
import 'package:stacked_services/src/bottom_sheet/bottom_sheet_service.dart';
import 'package:stacked_services/src/dialog/dialog_service.dart';
import 'package:stacked_services/src/navigation/navigation_service.dart';
import 'package:stacked_services/src/snackbar/snackbar_service.dart';
import 'package:stacked_shared/stacked_shared.dart';
import '../services/global_var.dart';
import '../services/my_easyloading.dart';
import '../services/my_storage.dart';
import '../services/my_tts.dart';
import '../services/other_function.dart';
final locator = StackedLocator.instance;
Future<void> setupLocator({
String? environment,
EnvironmentFilter? environmentFilter,
}) async {
// Register environments
locator.registerEnvironment(
environment: environment, environmentFilter: environmentFilter);
// Register dependencies
locator.registerLazySingleton(() => NavigationService());
locator.registerLazySingleton(() => DialogService());
locator.registerLazySingleton(() => SnackbarService());
locator.registerLazySingleton(() => BottomSheetService());
locator.registerLazySingleton(() => MyEasyLoading());
locator.registerLazySingleton(() => GlobalVar());
locator.registerLazySingleton(() => MyFunction());
locator.registerLazySingleton(() => MyTts());
locator.registerLazySingleton(() => MyStorage());
}

159
lib/app/app.logger.dart Normal file
View File

@ -0,0 +1,159 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// **************************************************************************
// StackedLoggerGenerator
// **************************************************************************
// ignore_for_file: avoid_print, depend_on_referenced_packages
/// Maybe this should be generated for the user as well?
///
/// import 'package:customer_app/services/stackdriver/stackdriver_service.dart';
import 'package:flutter/foundation.dart';
import 'package:logger/logger.dart';
class SimpleLogPrinter extends LogPrinter {
final String className;
final bool printCallingFunctionName;
final bool printCallStack;
final List<String> exludeLogsFromClasses;
final String? showOnlyClass;
SimpleLogPrinter(
this.className, {
this.printCallingFunctionName = true,
this.printCallStack = false,
this.exludeLogsFromClasses = const [],
this.showOnlyClass,
});
@override
List<String> log(LogEvent event) {
var color = PrettyPrinter.levelColors[event.level];
var emoji = PrettyPrinter.levelEmojis[event.level];
var methodName = _getMethodName();
var methodNameSection =
printCallingFunctionName && methodName != null ? ' | $methodName' : '';
var stackLog = event.stackTrace.toString();
var output =
'$emoji $className$methodNameSection - ${event.message}${event.error != null ? '\nERROR: ${event.error}\n' : ''}${printCallStack ? '\nSTACKTRACE:\n$stackLog' : ''}';
if (exludeLogsFromClasses
.any((excludeClass) => className == excludeClass) ||
(showOnlyClass != null && className != showOnlyClass)) return [];
final pattern = RegExp('.{1,800}'); // 800 is the size of each chunk
List<String> result = [];
for (var line in output.split('\n')) {
result.addAll(pattern.allMatches(line).map((match) {
if (kReleaseMode) {
return match.group(0)!;
} else {
return color!(match.group(0)!);
}
}));
}
return result;
}
String? _getMethodName() {
try {
final currentStack = StackTrace.current;
final formattedStacktrace = _formatStackTrace(currentStack, 3);
if (kIsWeb) {
final classNameParts = _splitClassNameWords(className);
return _findMostMatchedTrace(formattedStacktrace!, classNameParts)
.split(' ')
.last;
} else {
final realFirstLine = formattedStacktrace
?.firstWhere((line) => line.contains(className), orElse: () => "");
final methodName = realFirstLine?.replaceAll('$className.', '');
return methodName;
}
} catch (e) {
// There's no deliberate function call from our code so we return null;
return null;
}
}
List<String> _splitClassNameWords(String className) {
return className
.split(RegExp(r'(?=[A-Z])'))
.map((e) => e.toLowerCase())
.toList();
}
/// When the faulty word exists in the begging this method will not be very usefull
String _findMostMatchedTrace(
List<String> stackTraces, List<String> keyWords) {
String match = stackTraces.firstWhere(
(trace) => _doesTraceContainsAllKeywords(trace, keyWords),
orElse: () => '');
if (match.isEmpty) {
match = _findMostMatchedTrace(
stackTraces, keyWords.sublist(0, keyWords.length - 1));
}
return match;
}
bool _doesTraceContainsAllKeywords(String stackTrace, List<String> keywords) {
final formattedKeywordsAsRegex = RegExp(keywords.join('.*'));
return stackTrace.contains(formattedKeywordsAsRegex);
}
}
final stackTraceRegex = RegExp(r'#[0-9]+[\s]+(.+) \(([^\s]+)\)');
List<String>? _formatStackTrace(StackTrace stackTrace, int methodCount) {
var lines = stackTrace.toString().split('\n');
var formatted = <String>[];
var count = 0;
for (var line in lines) {
var match = stackTraceRegex.matchAsPrefix(line);
if (match != null) {
if (match.group(2)!.startsWith('package:logger')) {
continue;
}
var newLine = ("${match.group(1)}");
formatted.add(newLine.replaceAll('<anonymous closure>', '()'));
if (++count == methodCount) {
break;
}
} else {
formatted.add(line);
}
}
if (formatted.isEmpty) {
return null;
} else {
return formatted;
}
}
Logger getLogger(
String className, {
bool printCallingFunctionName = true,
bool printCallstack = false,
List<String> exludeLogsFromClasses = const [],
String? showOnlyClass,
}) {
return Logger(
printer: SimpleLogPrinter(
className,
printCallingFunctionName: printCallingFunctionName,
printCallStack: printCallstack,
showOnlyClass: showOnlyClass,
exludeLogsFromClasses: exludeLogsFromClasses,
),
output: MultiOutput([
if (!kReleaseMode) ConsoleOutput(),
]),
);
}

227
lib/app/app.router.dart Normal file
View File

@ -0,0 +1,227 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// **************************************************************************
// StackedNavigatorGenerator
// **************************************************************************
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:flutter/material.dart' as _i4;
import 'package:flutter/material.dart';
import 'package:kamus_kesehatan/ui/views/splash_screen/splash_screen_view.dart'
as _i2;
import 'package:kamus_kesehatan/ui/views/user_tracking_index/list_kamus_kesehatan/list_kamus_kesehatan_view.dart'
as _i5;
import 'package:kamus_kesehatan/ui/views/user_tracking_index/profil_user/profil_user_view.dart'
as _i6;
import 'package:kamus_kesehatan/ui/views/user_tracking_index/user_tracking_index_view.dart'
as _i3;
import 'package:stacked/stacked.dart' as _i1;
import 'package:stacked_services/stacked_services.dart' as _i7;
class Routes {
static const splashScreenView = '/';
static const userTrackingIndexView = '/user-tracking-index-view';
static const all = <String>{
splashScreenView,
userTrackingIndexView,
};
}
class StackedRouter extends _i1.RouterBase {
final _routes = <_i1.RouteDef>[
_i1.RouteDef(
Routes.splashScreenView,
page: _i2.SplashScreenView,
),
_i1.RouteDef(
Routes.userTrackingIndexView,
page: _i3.UserTrackingIndexView,
),
];
final _pagesMap = <Type, _i1.StackedRouteFactory>{
_i2.SplashScreenView: (data) {
return _i4.MaterialPageRoute<dynamic>(
builder: (context) => const _i2.SplashScreenView(),
settings: data,
);
},
_i3.UserTrackingIndexView: (data) {
return _i4.MaterialPageRoute<dynamic>(
builder: (context) => const _i3.UserTrackingIndexView(),
settings: data,
);
},
};
@override
List<_i1.RouteDef> get routes => _routes;
@override
Map<Type, _i1.StackedRouteFactory> get pagesMap => _pagesMap;
}
class UserTrackingIndexViewRoutes {
static const listKamusKesehatanView = 'list-kamus-kesehatan-view';
static const profilUserView = 'profil-user-view';
static const all = <String>{
listKamusKesehatanView,
profilUserView,
};
}
class UserTrackingIndexViewRouter extends _i1.RouterBase {
final _routes = <_i1.RouteDef>[
_i1.RouteDef(
UserTrackingIndexViewRoutes.listKamusKesehatanView,
page: _i5.ListKamusKesehatanView,
),
_i1.RouteDef(
UserTrackingIndexViewRoutes.profilUserView,
page: _i6.ProfilUserView,
),
];
final _pagesMap = <Type, _i1.StackedRouteFactory>{
_i5.ListKamusKesehatanView: (data) {
return _i4.MaterialPageRoute<dynamic>(
builder: (context) => const _i5.ListKamusKesehatanView(),
settings: data,
);
},
_i6.ProfilUserView: (data) {
return _i4.MaterialPageRoute<dynamic>(
builder: (context) => const _i6.ProfilUserView(),
settings: data,
);
},
};
@override
List<_i1.RouteDef> get routes => _routes;
@override
Map<Type, _i1.StackedRouteFactory> get pagesMap => _pagesMap;
}
extension NavigatorStateExtension on _i7.NavigationService {
Future<dynamic> navigateToSplashScreenView([
int? routerId,
bool preventDuplicates = true,
Map<String, String>? parameters,
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
transition,
]) async {
return navigateTo<dynamic>(Routes.splashScreenView,
id: routerId,
preventDuplicates: preventDuplicates,
parameters: parameters,
transition: transition);
}
Future<dynamic> navigateToUserTrackingIndexView([
int? routerId,
bool preventDuplicates = true,
Map<String, String>? parameters,
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
transition,
]) async {
return navigateTo<dynamic>(Routes.userTrackingIndexView,
id: routerId,
preventDuplicates: preventDuplicates,
parameters: parameters,
transition: transition);
}
Future<dynamic>
navigateToNestedListKamusKesehatanViewInUserTrackingIndexViewRouter([
int? routerId,
bool preventDuplicates = true,
Map<String, String>? parameters,
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
transition,
]) async {
return navigateTo<dynamic>(
UserTrackingIndexViewRoutes.listKamusKesehatanView,
id: routerId,
preventDuplicates: preventDuplicates,
parameters: parameters,
transition: transition);
}
Future<dynamic> navigateToNestedProfilUserViewInUserTrackingIndexViewRouter([
int? routerId,
bool preventDuplicates = true,
Map<String, String>? parameters,
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
transition,
]) async {
return navigateTo<dynamic>(UserTrackingIndexViewRoutes.profilUserView,
id: routerId,
preventDuplicates: preventDuplicates,
parameters: parameters,
transition: transition);
}
Future<dynamic> replaceWithSplashScreenView([
int? routerId,
bool preventDuplicates = true,
Map<String, String>? parameters,
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
transition,
]) async {
return replaceWith<dynamic>(Routes.splashScreenView,
id: routerId,
preventDuplicates: preventDuplicates,
parameters: parameters,
transition: transition);
}
Future<dynamic> replaceWithUserTrackingIndexView([
int? routerId,
bool preventDuplicates = true,
Map<String, String>? parameters,
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
transition,
]) async {
return replaceWith<dynamic>(Routes.userTrackingIndexView,
id: routerId,
preventDuplicates: preventDuplicates,
parameters: parameters,
transition: transition);
}
Future<dynamic>
replaceWithNestedListKamusKesehatanViewInUserTrackingIndexViewRouter([
int? routerId,
bool preventDuplicates = true,
Map<String, String>? parameters,
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
transition,
]) async {
return replaceWith<dynamic>(
UserTrackingIndexViewRoutes.listKamusKesehatanView,
id: routerId,
preventDuplicates: preventDuplicates,
parameters: parameters,
transition: transition);
}
Future<dynamic> replaceWithNestedProfilUserViewInUserTrackingIndexViewRouter([
int? routerId,
bool preventDuplicates = true,
Map<String, String>? parameters,
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
transition,
]) async {
return replaceWith<dynamic>(UserTrackingIndexViewRoutes.profilUserView,
id: routerId,
preventDuplicates: preventDuplicates,
parameters: parameters,
transition: transition);
}
}

View File

@ -0,0 +1,22 @@
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
import '../../services/global_var.dart';
import '../../services/my_easyloading.dart';
import '../../services/other_function.dart';
import '../app.locator.dart';
class CustomBaseViewModel extends BaseViewModel {
final dialogService = locator<DialogService>();
final navigationService = locator<NavigationService>();
final bottomSheetService = locator<BottomSheetService>();
final snackbarService = locator<SnackbarService>();
// below is mine
final easyLoading = locator<MyEasyLoading>();
final globalVar = locator<GlobalVar>();
final myFunction = locator<MyFunction>();
void back() {
navigationService.back();
}
}

30
lib/app/themes/app_colors.dart Executable file
View File

@ -0,0 +1,30 @@
import 'dart:ui';
const Color mainColor = Color(0xFF25C0F1);
const Color secondaryColor = Color(0xFFB72025);
const Color dangerColor = Color(0xFFFF4B68);
const Color warningColor = Color(0xFFFBFFA3);
const Color lightColor = Color(0xFFF4FAFE);
const Color lightGreyColor = Color(0xFFFCFCFC);
const Color stockColor = Color(0xFFEEF3F6);
const Color backgroundColor = Color(0xFFE5E5E5);
const Color backgroundColor3 = Color(0xFFF6F7F8);
const Color orangeColor = Color.fromARGB(255, 250, 145, 84);
const Color blueColor = Color(0xFF026AA2);
const Color greenColor = Color(0xFF2ABB52);
const Color redColor = Color(0xFFED1717);
const Color greyBlueColor = Color(0xFF363F72);
const Color fontColor = Color(0xFF101828);
const Color fontSecondaryColor = Color(0xFF667085);
const Color fontParagraphColor = Color(0xFFB2B2B2);
const Color fontGrey = Color(0xFF1C1C1C);
const Color mainGrey = Color(0xFF8991A4);
const Color secondaryGrey = Color(0xFFD0D5DD);
const Color thirdGrey = Color(0xFFF2F4F7);
const Color fourthGrey = Color(0xFF5C5C5C);
const Color fifthGrey = Color(0xFFEBEBEB);
const Color sixthGrey = Color(0xFF151515);

View File

@ -0,0 +1,44 @@
import 'package:flutter/material.dart';
import 'app_colors.dart';
const regularTextStyle = TextStyle(
fontFamily: 'Arial',
fontSize: 14,
fontWeight: FontWeight.w400,
color: fontColor);
const italicTextStyle = TextStyle(
fontFamily: 'Arial',
fontSize: 14,
color: fontColor,
fontStyle: FontStyle.italic,
);
const mediumTextStyle = TextStyle(
fontFamily: 'Arial',
fontSize: 14,
fontWeight: FontWeight.w500,
color: fontColor,
);
const semiBoldTextStyle = TextStyle(
fontFamily: 'Arial',
fontSize: 14,
fontWeight: FontWeight.w600,
color: fontColor,
);
const boldTextStyle = TextStyle(
fontFamily: 'Arial',
fontSize: 14,
fontWeight: FontWeight.w700,
color: fontColor,
);
const extraBoldTextStyle = TextStyle(
fontFamily: 'Arial',
fontSize: 14,
fontWeight: FontWeight.w800,
color: fontColor,
);

126
lib/app/themes/app_theme.dart Executable file
View File

@ -0,0 +1,126 @@
// ignore_for_file: deprecated_member_use
import 'package:flutter/material.dart';
import 'app_colors.dart';
import 'app_text.dart';
ThemeData appTheme = ThemeData(
useMaterial3: true,
primaryColor: mainColor,
scaffoldBackgroundColor: Colors.white,
canvasColor: Colors.white,
fontFamily: 'Poppins',
appBarTheme: AppBarTheme(
elevation: 0,
titleTextStyle: boldTextStyle.copyWith(fontSize: 16, color: fontGrey),
centerTitle: true,
),
textTheme: TextTheme(
headline1: regularTextStyle.copyWith(fontSize: 32),
headline2: regularTextStyle.copyWith(fontSize: 20),
headline3: regularTextStyle.copyWith(fontSize: 18),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: mainColor,
foregroundColor: Colors.white,
disabledBackgroundColor: mainColor.withOpacity(.3),
minimumSize: const Size(double.maxFinite, 58),
textStyle: boldTextStyle,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
shadowColor: Colors.transparent,
elevation: 0,
),
),
outlinedButtonTheme: OutlinedButtonThemeData(
style: OutlinedButton.styleFrom(
textStyle: boldTextStyle,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
side: const BorderSide(
color: mainColor,
width: 1,
),
foregroundColor: mainColor,
// disabledForegroundColor: mainColor.withOpacity(.3),
minimumSize: const Size(double.maxFinite, 58),
),
),
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
foregroundColor: mainColor,
disabledForegroundColor: mainColor.withOpacity(.3),
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
textStyle: semiBoldTextStyle,
shadowColor: Colors.transparent,
),
),
iconTheme: const IconThemeData(
color: mainColor,
),
listTileTheme: ListTileThemeData(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
checkboxTheme: CheckboxThemeData(
fillColor: MaterialStateProperty.all(mainColor),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4),
),
side: const BorderSide(
color: secondaryGrey,
width: 1,
),
),
radioTheme: RadioThemeData(
fillColor: MaterialStateProperty.all(mainColor),
),
tabBarTheme: TabBarTheme(
labelColor: mainColor,
unselectedLabelColor: secondaryGrey,
labelStyle: boldTextStyle.copyWith(fontSize: 16),
unselectedLabelStyle: mediumTextStyle.copyWith(fontSize: 16),
),
chipTheme: ChipThemeData(
backgroundColor: Colors.white,
disabledColor: Colors.white,
selectedColor: Colors.white,
secondarySelectedColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
side: const BorderSide(color: fifthGrey),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6),
),
labelStyle: regularTextStyle.copyWith(fontSize: 12, color: fontGrey),
secondaryLabelStyle:
regularTextStyle.copyWith(fontSize: 12, color: secondaryColor),
deleteIconColor: fontGrey,
showCheckmark: false,
),
popupMenuTheme: PopupMenuThemeData(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
side: const BorderSide(
color: fifthGrey,
width: 1,
),
),
),
colorScheme: const ColorScheme.light(
primary: mainColor,
secondary: secondaryColor,
onPrimary: Colors.white,
onSecondary: Colors.white,
error: dangerColor,
onError: dangerColor,
background: backgroundColor,
).copyWith(background: Colors.white),
);

40
lib/main.dart Normal file
View File

@ -0,0 +1,40 @@
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:kamus_kesehatan/services/my_storage.dart';
import 'package:stacked_services/stacked_services.dart';
import 'app/app.dialogs.dart';
import 'app/app.locator.dart';
import 'app/app.router.dart';
import 'app/themes/app_theme.dart';
Future main() async {
await setupAllLocator();
await MyStorage().init();
// await MyTts().init();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Cek Suara Caleg',
theme: appTheme,
debugShowCheckedModeBanner: false,
navigatorKey: StackedService.navigatorKey,
onGenerateRoute: StackedRouter().onGenerateRoute,
builder: EasyLoading.init(),
);
}
}
Future<void> setupAllLocator() async {
await setupLocator();
setupDialogUi();
// setupBottomSheetUi();
// setupSnackbarUi();
}

View File

@ -0,0 +1,18 @@
class IstilahModel {
String? istilah;
String? arti;
IstilahModel({this.istilah, this.arti});
IstilahModel.fromJson(Map<String, dynamic> json) {
istilah = json['istilah'];
arti = json['arti'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['istilah'] = istilah;
data['arti'] = arti;
return data;
}
}

View File

@ -0,0 +1,3 @@
class GlobalVar {
String backPressed = 'backNormal';
}

View File

@ -0,0 +1,39 @@
import 'package:flutter_easyloading/flutter_easyloading.dart';
class MyEasyLoading {
showLoading() {
EasyLoading.show(
status: 'loading...',
maskType: EasyLoadingMaskType.black,
dismissOnTap: false,
);
}
dismissLoading() {
EasyLoading.dismiss();
}
customLoading(String message) {
EasyLoading.show(
status: message,
maskType: EasyLoadingMaskType.black,
dismissOnTap: false,
);
}
showSuccess(String message) {
EasyLoading.showSuccess(message);
}
showError(String message) {
EasyLoading.showError(message);
}
showInfo(String message) {
EasyLoading.showInfo(message);
}
showProgress(double progress, String status) {
EasyLoading.showProgress(progress, status: status);
}
}

View File

@ -0,0 +1,25 @@
import 'package:get_storage/get_storage.dart';
class MyStorage {
final box = GetStorage();
init() async {
await GetStorage.init();
}
write(String key, dynamic value) async {
await box.write(key, value);
}
read(String key) {
return box.read(key);
}
remove(String key) async {
await box.remove(key);
}
clear() async {
await box.erase();
}
}

115
lib/services/my_tts.dart Normal file
View File

@ -0,0 +1,115 @@
import 'dart:io' show Platform;
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter_tts/flutter_tts.dart';
import '../app/app.logger.dart';
enum TtsState { playing, stopped, paused, continued }
// create a singleton class of MyTts which have init() method
class MyTts {
final log = getLogger('MyTts');
late FlutterTts flutterTts;
TtsState ttsState = TtsState.stopped;
bool get isAndroid => !kIsWeb && Platform.isAndroid;
Future init() async {
flutterTts = FlutterTts();
_setAwaitOptions();
if (isAndroid) {
await getVoices();
await _getLanguages();
await _getEngines();
await _getDefaultVoice();
await _setLanguage();
// male voice
// await flutterTts.setVoice({"name": "id-id-x-dfz#male_2-local"});
}
flutterTts.setStartHandler(() {
log.i("Playing");
ttsState = TtsState.playing;
});
flutterTts.setCompletionHandler(() {
log.i("Complete");
ttsState = TtsState.stopped;
});
flutterTts.setCancelHandler(() {
log.i("Cancel");
ttsState = TtsState.stopped;
});
flutterTts.setPauseHandler(() {
log.i("Paused");
ttsState = TtsState.paused;
});
flutterTts.setContinueHandler(() {
log.i("Continued");
ttsState = TtsState.continued;
});
flutterTts.setErrorHandler((msg) {
log.e("error: $msg");
ttsState = TtsState.stopped;
});
}
Future<dynamic> _getLanguages() async => await flutterTts.getLanguages;
Future<dynamic> _getEngines() async => await flutterTts.getEngines;
Future<dynamic> getVoices() async => await flutterTts.getVoices;
Future<dynamic> _setLanguage() async => await flutterTts.setLanguage("id-ID");
// Future setVoice() async {
// for (var voice in await flutterTts.getVoices) {
// if (voice.
// }
// }
Future _getDefaultVoice() async {
var voice = await flutterTts.getDefaultVoice;
if (voice != null) {
log.i(voice);
}
}
Future speak(String text) async {
await flutterTts.setVolume(1.0);
await flutterTts.setSpeechRate(0.5);
await flutterTts.setPitch(1.0);
if (text.isNotEmpty) {
var result = await flutterTts.speak(text);
if (result == 1) {
ttsState = TtsState.playing;
}
}
}
Future _setAwaitOptions() async {
await flutterTts.awaitSpeakCompletion(true);
}
Future stop() async {
var result = await flutterTts.stop();
if (result == 1) {
ttsState = TtsState.stopped;
}
}
Future pause() async {
var result = await flutterTts.pause();
if (result == 1) {
ttsState = TtsState.paused;
}
}
}

View File

@ -0,0 +1,16 @@
import 'package:intl/intl.dart';
class MyFunction {
String convertDateTime(String input) {
DateTime dateTime = DateTime.parse(input);
String formattedDateTime =
DateFormat('dd-MM-yyyy | hh.mm.ss a').format(dateTime);
return formattedDateTime;
}
// chnage | to \n in string
String convertDateTime2(String input) {
input = input.replaceAll('| ', '\n');
return input;
}
}

View File

@ -0,0 +1,63 @@
import 'package:flutter/material.dart';
import 'package:kamus_kesehatan/model/istilah_model.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
import './action_dialog_view_model.dart';
class ActionDialogView extends StatelessWidget {
final DialogRequest<IstilahModel> request;
final Function(DialogResponse) completer;
const ActionDialogView({
Key? key,
required this.request,
required this.completer,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return ViewModelBuilder<ActionDialogViewModel>.reactive(
viewModelBuilder: () => ActionDialogViewModel(),
onViewModelReady: (ActionDialogViewModel model) async {
await model.init(request.data);
},
builder: (
BuildContext context,
ActionDialogViewModel model,
Widget? child,
) {
return Dialog(
child: Container(
padding: const EdgeInsets.all(20),
// create a row with 2 circle icon , 1 is whataspp icon, 2 is bookmark, no need text
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
CircleAvatar(
backgroundColor: Colors.green,
child: IconButton(
icon: const Icon(Icons.phone),
onPressed: () {
// completer(DialogResponse(confirmed: true));
model.openWhatsapp();
},
),
),
CircleAvatar(
backgroundColor: Colors.blue,
child: IconButton(
icon: const Icon(Icons.bookmark),
onPressed: () {
model.addBookmark(request.data!);
},
),
),
],
),
),
);
},
);
}
}

View File

@ -0,0 +1,90 @@
import '../../../app/app.dialogs.dart';
import '../../../app/app.locator.dart';
import '../../../app/app.logger.dart';
import '../../../app/core/custom_base_view_model.dart';
import '../../../model/istilah_model.dart';
import '../../../services/my_storage.dart';
class ActionDialogViewModel extends CustomBaseViewModel {
final log = getLogger('ActionDialogViewModel');
final myStorage = locator<MyStorage>();
IstilahModel? data;
Future<void> init(IstilahModel? data) async {
// log.i('init');
// log.i(data!.istilah.toString());
// log.i(data.arti.toString());
this.data = data;
// await myStorage.clear();
}
addBookmark(IstilahModel istilahModel) async {
List<dynamic>? listBookmark;
listBookmark = await myStorage.read('listBookmark');
listBookmark ??= [];
log.i('ini panjang listBookmark ${listBookmark.length}');
log.i('ini listBookmark $listBookmark');
// check if istilahModel is already in listBookmark
bool isExist = false;
for (var item in listBookmark) {
if (item['istilah'] == istilahModel.istilah) {
isExist = true;
break;
}
}
if (isExist) {
snackbarService.showSnackbar(
message: 'Bookmark sudah ada',
duration: const Duration(seconds: 2),
);
return;
}
listBookmark.add({
'istilah': istilahModel.istilah,
'arti': istilahModel.arti,
});
await myStorage.write('listBookmark', listBookmark);
snackbarService.showSnackbar(
message: 'Berhasil menambahkan bookmark',
duration: const Duration(seconds: 2),
);
// // check if istilahModel is already in listBookmark
// bool isExist = false;
// for (var item in listBookmark) {
// if (item.istilah == istilahModel.istilah) {
// isExist = true;
// break;
// }
// }
// if (!isExist) {
// listBookmark.add(istilahModel);
// await myStorage.write('listBookmark', listBookmark);
// snackbarService.showSnackbar(
// message: 'Berhasil menambahkan bookmark',
// duration: const Duration(seconds: 2),
// );
// } else {
// snackbarService.showSnackbar(
// message: 'Bookmark sudah ada',
// duration: const Duration(seconds: 2),
// );
// }
}
openWhatsapp() async {
await dialogService.showCustomDialog(
variant: DialogType.nomorTelponDialogView,
data: data,
);
}
}

View File

@ -0,0 +1,82 @@
import 'package:flutter/material.dart';
import 'package:kamus_kesehatan/ui/widgets/my_textformfield.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
import 'package:validatorless/validatorless.dart';
import './nomor_telpon_dialog_view_model.dart';
class NomorTelponDialogView extends StatelessWidget {
final DialogRequest? request;
final Function(DialogResponse)? completer;
const NomorTelponDialogView({
Key? key,
this.request,
this.completer,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return ViewModelBuilder<NomorTelponDialogViewModel>.reactive(
viewModelBuilder: () => NomorTelponDialogViewModel(),
onViewModelReady: (NomorTelponDialogViewModel model) async {
await model.init(request!.data);
},
builder: (
BuildContext context,
NomorTelponDialogViewModel model,
Widget? child,
) {
return Dialog(
child: Container(
padding: const EdgeInsets.all(20),
child: Form(
key: model.formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
MyTextFormField(
controller: model.nomorTelponController,
hintText: 'Nomor Telpon',
keyboardType: TextInputType.phone,
maxLength: 13,
validator: Validatorless.multiple([
Validatorless.required(
'Nomor Telpon tidak boleh kosong'),
Validatorless.number('Nomor Telpon harus angka'),
Validatorless.min(10, 'Nomor Telpon minimal 10 digit'),
Validatorless.regex(
RegExp(r'^[0-9]*$'), 'Nomor Telpon tidak valid')
])),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () =>
completer!(DialogResponse(confirmed: false)),
child: const Text(
'Batal',
style: TextStyle(color: Colors.red),
),
),
TextButton(
onPressed: () {
if (model.formKey.currentState!.validate()) {
model.openWhatsapp();
}
},
child: const Text('Share'),
),
],
),
],
),
),
),
);
},
);
}
}

View File

@ -0,0 +1,38 @@
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../../app/app.logger.dart';
import '../../../app/core/custom_base_view_model.dart';
import '../../../model/istilah_model.dart';
class NomorTelponDialogViewModel extends CustomBaseViewModel {
final log = getLogger('NomorTelponDialogViewModel');
TextEditingController nomorTelponController = TextEditingController();
final formKey = GlobalKey<FormState>();
IstilahModel? data;
Future<void> init(IstilahModel data) async {
log.i('init');
log.i(data.istilah.toString());
log.i(data.arti.toString());
this.data = data;
// await myStorage.clear();
}
openWhatsapp() async {
// open whatsapp using url
String noTelpon = nomorTelponController.text;
// convert the number to international format
noTelpon = noTelpon.replaceAll(RegExp(r'[^0-9]'), '');
noTelpon = '62${noTelpon.substring(1)}';
log.i('no_telpon: $noTelpon');
// add the data to url with query parameter
final url = Uri.parse('https://wa.me/$noTelpon?text=Kamus Medis : \n'
'Istilah : ${data!.istilah}\n'
'Arti : ${data!.arti}');
if (!await launchUrl(url)) {
throw Exception('Could not launch $url');
}
}
}

View File

@ -0,0 +1,94 @@
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import '../../../app/themes/app_text.dart';
import './splash_screen_view_model.dart';
class SplashScreenView extends StatelessWidget {
const SplashScreenView({super.key});
@override
Widget build(BuildContext context) {
return ViewModelBuilder<SplashScreenViewModel>.nonReactive(
viewModelBuilder: () => SplashScreenViewModel(),
onViewModelReady: (SplashScreenViewModel model) async {
await model.init();
},
builder: (
BuildContext context,
SplashScreenViewModel model,
Widget? child,
) {
return WillPopScope(
onWillPop: () async => false,
child: Scaffold(
// backgroundColor: warningColor,
body: SafeArea(
child: Center(
child: Column(
children: [
const Expanded(
flex: 1,
child: SizedBox(
height: 100,
),
),
Image.asset(
'assets/logo.png',
width: 200,
height: 200,
),
const SizedBox(
height: 10,
),
Text(
'KAMUS MEDIS DAN KESEHATAN',
style: boldTextStyle.copyWith(
// color: backgroundColor,
fontSize: 20,
),
textAlign: TextAlign.center,
),
// Text(
// '(Survei App)',
// style: boldTextStyle.copyWith(
// // color: backgroundColor,
// fontStyle: FontStyle.italic,
// ),
// textAlign: TextAlign.center,
// ),
const Expanded(child: SizedBox()),
Text(
'Created By',
style: regularTextStyle.copyWith(
fontSize: 12,
),
textAlign: TextAlign.center,
),
Text(
'Kicap Karan',
style: boldTextStyle.copyWith(
fontSize: 13,
fontStyle: FontStyle.italic,
),
textAlign: TextAlign.center,
),
Text(
'www.kicap-karan.com',
style: boldTextStyle.copyWith(
fontSize: 13,
),
),
const SizedBox(
height: 20,
)
],
),
),
),
),
);
},
);
}
}

View File

@ -0,0 +1,16 @@
import '../../../app/app.locator.dart';
import '../../../app/app.logger.dart';
import '../../../app/app.router.dart';
import '../../../app/core/custom_base_view_model.dart';
import '../../../services/my_tts.dart';
class SplashScreenViewModel extends CustomBaseViewModel {
final log = getLogger('SplashScreenViewModel');
final myTts = locator<MyTts>();
Future<void> init() async {
await myTts.init();
// await 3 seconds then go to login
// await Future.delayed(const Duration(seconds: 3));
await navigationService.navigateTo(Routes.userTrackingIndexView);
}
}

View File

@ -0,0 +1,86 @@
import 'package:flutter/material.dart';
import 'package:kamus_kesehatan/app/themes/app_text.dart';
import 'package:kamus_kesehatan/ui/widgets/my_textformfield.dart';
import 'package:stacked/stacked.dart';
import './list_kamus_kesehatan_view_model.dart';
class ListKamusKesehatanView extends StatelessWidget {
const ListKamusKesehatanView({super.key});
@override
Widget build(BuildContext context) {
return ViewModelBuilder<ListKamusKesehatanViewModel>.reactive(
viewModelBuilder: () => ListKamusKesehatanViewModel(),
onViewModelReady: (ListKamusKesehatanViewModel model) async {
await model.init();
},
builder: (
BuildContext context,
ListKamusKesehatanViewModel model,
Widget? child,
) {
return Padding(
padding: const EdgeInsets.all(25),
child: Column(
children: [
MyTextFormField(
hintText: 'Cari istilah',
labelText: 'Cari istilah',
controller: model.searchController,
suffixIcon: const Icon(Icons.search),
onChanged: (String value) {
model.searchIstilah();
},
),
const SizedBox(
height: 20,
),
Expanded(
child: model.listIstilah.isEmpty
? const Center(
child: CircularProgressIndicator(),
)
: ListView.builder(
itemCount: model.listIstilah.length,
itemBuilder: (
BuildContext context,
int index,
) {
return Card(
child: ListTile(
title: Text(
model.listIstilah[index].istilah!.toUpperCase(),
style: boldTextStyle,
),
subtitle: Text(model.listIstilah[index].arti!),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
GestureDetector(
onTap: () {
model.cekSuara(model.listIstilah[index]);
},
child: const Icon(Icons.volume_up),
),
IconButton(
onPressed: () {
model.bukaDialogAksi(
model.listIstilah[index]);
},
icon: const Icon(Icons.info),
),
],
),
),
);
},
),
),
],
),
);
},
);
}
}

View File

@ -0,0 +1,72 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
// import '../../../../app/app.dialogs.dart';
import '../../../../app/app.dialogs.dart';
import '../../../../app/app.locator.dart';
import '../../../../app/app.logger.dart';
import '../../../../app/core/custom_base_view_model.dart';
import '../../../../model/istilah_model.dart';
import '../../../../services/my_tts.dart';
class ListKamusKesehatanViewModel extends CustomBaseViewModel {
final log = getLogger('ListKamusKesehatanViewModel');
final myTts = locator<MyTts>();
TextEditingController searchController = TextEditingController();
List<IstilahModel> listIstilah = [];
List<IstilahModel> allListIstilah = [];
Future<void> init() async {
// await myTts.init();
// myTts.getVoices();
listIstilah = await rootBundle.loadString('assets/dataset.json').then(
(String data) {
final List<dynamic> jsonData = json.decode(data);
return jsonData.map((dynamic item) {
return IstilahModel.fromJson(item as Map<String, dynamic>);
}).toList();
},
);
allListIstilah = listIstilah;
notifyListeners();
}
cekSuara(IstilahModel listIstilah) {
myTts.stop();
// speak the listIstilah.istilah and wait 2 seconds then speak the listIstilah.arti
myTts.speak(listIstilah.istilah!);
Future.delayed(const Duration(seconds: 2), () {
myTts.speak(listIstilah.arti!);
});
}
searchIstilah() {
if (searchController.text.isEmpty) {
listIstilah = allListIstilah;
} else {
listIstilah = allListIstilah
.where((IstilahModel istilah) => istilah.istilah!
.toLowerCase()
.contains(searchController.text.toLowerCase()))
.toList();
}
notifyListeners();
}
bukaDialogAksi(IstilahModel listIstilah) async {
var res = await dialogService.showCustomDialog(
variant: DialogType.actionDialogView,
title: 'Form Aksi',
data: listIstilah,
);
if (res!.confirmed) {
log.i('confirmed');
// do something
}
}
}

View File

@ -0,0 +1,171 @@
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import '../../../../app/themes/app_colors.dart';
import '../../../../app/themes/app_text.dart';
import './profil_user_view_model.dart';
class ProfilUserView extends StatelessWidget {
const ProfilUserView({super.key});
@override
Widget build(BuildContext context) {
return ViewModelBuilder<ProfilUserViewModel>.reactive(
viewModelBuilder: () => ProfilUserViewModel(),
onViewModelReady: (ProfilUserViewModel model) async {
await model.init();
},
builder: (
BuildContext context,
ProfilUserViewModel model,
Widget? child,
) {
return Padding(
padding: const EdgeInsets.all(25),
child: Column(
children: [
// MyTextFormField(
// hintText: 'Cari istilah',
// labelText: 'Cari istilah',
// controller: model.searchController,
// suffixIcon: const Icon(Icons.search),
// onChanged: (String value) {
// model.searchIstilah();
// },
// ),
// const SizedBox(
// height: 20,
// ),
Expanded(
child: model.listIstilah == null
? const Center(
child: CircularProgressIndicator(),
)
: model.listIstilah!.isEmpty
? const Center(
child: Text('Belum ada bookmark'),
)
: ListView.builder(
itemCount: model.listIstilah!.length,
itemBuilder: (
BuildContext context,
int index,
) {
return Card(
child: ListTile(
onTap: () =>
model.cekSuara(model.listIstilah![index]),
title: Text(
model.listIstilah![index].istilah!
.toUpperCase(),
style: boldTextStyle,
),
subtitle:
Text(model.listIstilah![index].arti!),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
GestureDetector(
onTap: () {
// model.cekSuara(model.listIstilah[index]);
},
child: const Icon(Icons.phone),
),
IconButton(
onPressed: () {
// model.bukaDialogAksi(
// model.listIstilah[index]);
},
icon: const Icon(Icons.delete_forever),
),
],
),
),
);
},
),
),
],
),
);
// return const Scaffold(
// body: Padding(
// padding: EdgeInsets.all(30),
// child: Column(
// mainAxisAlignment: MainAxisAlignment.center,
// crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisSize: MainAxisSize.min,
// children: [
// Center(
// child: CircleAvatar(
// radius: 65,
// backgroundColor: fontParagraphColor,
// child: Icon(
// Icons.person,
// size: 50,
// color: Colors.white,
// ),
// ),
// ),
// SizedBox(height: 20),
// _ChildWidget(
// icon: Icons.person,
// text: 'ini nama developer',
// ),
// SizedBox(height: 20),
// _ChildWidget(
// icon: Icons.list_alt,
// text: 'Kamus Medis Dan Kesehatan',
// ),
// SizedBox(height: 20),
// _ChildWidget(
// // icon multiple person
// icon: Icons.people,
// text: 'Pembimbing 1',
// ),
// SizedBox(height: 20),
// _ChildWidget(
// // icon multiple person
// icon: Icons.people,
// text: 'Pembimbing 2',
// ),
// ],
// ),
// ),
// );
},
);
}
}
// ignore: unused_element
class _ChildWidget extends StatelessWidget {
const _ChildWidget({
required this.icon,
required this.text,
});
final IconData icon;
final String text;
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(width: 30),
Icon(
icon,
size: 30,
color: mainColor,
),
const SizedBox(width: 40),
Text(
text,
style: regularTextStyle,
),
const Expanded(child: SizedBox(width: 30)),
],
);
}
}

View File

@ -0,0 +1,44 @@
import 'package:kamus_kesehatan/model/istilah_model.dart';
import '../../../../app/app.locator.dart';
import '../../../../app/app.logger.dart';
import '../../../../app/core/custom_base_view_model.dart';
import '../../../../services/my_storage.dart';
import '../../../../services/my_tts.dart';
class ProfilUserViewModel extends CustomBaseViewModel {
final log = getLogger('ProfilUserViewModel');
final myTts = locator<MyTts>();
final myStorage = locator<MyStorage>();
List<IstilahModel>? listIstilah;
List<IstilahModel>? allListIstilah;
Future<void> init() async {
List<dynamic>? listBookmark;
listBookmark = await myStorage.read('listBookmark');
listBookmark ??= [];
log.i('ini panjang listBookmark ${listBookmark.length}');
log.i('ini listBookmark $listBookmark');
listIstilah = listBookmark.map((dynamic item) {
return IstilahModel.fromJson(item as Map<String, dynamic>);
}).toList();
allListIstilah = listIstilah;
notifyListeners();
}
cekSuara(IstilahModel listIstilah) {
myTts.stop();
// speak the listIstilah.istilah and wait 2 seconds then speak the listIstilah.arti
myTts.speak(listIstilah.istilah!);
Future.delayed(const Duration(seconds: 2), () {
myTts.speak(listIstilah.arti!);
});
}
}

View File

@ -0,0 +1,94 @@
import 'package:flutter/material.dart';
import 'package:kamus_kesehatan/app/app.router.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
import 'package:stylish_bottom_bar/model/bar_items.dart';
import 'package:stylish_bottom_bar/stylish_bottom_bar.dart';
import '../../../app/themes/app_colors.dart';
import '../../../app/themes/app_text.dart';
import './user_tracking_index_view_model.dart';
class UserTrackingIndexView extends StatelessWidget {
const UserTrackingIndexView({super.key});
@override
Widget build(BuildContext context) {
return ViewModelBuilder<UserTrackingIndexViewModel>.reactive(
viewModelBuilder: () => UserTrackingIndexViewModel(),
onViewModelReady: (UserTrackingIndexViewModel model) async {
await model.init();
},
builder: (
BuildContext context,
UserTrackingIndexViewModel model,
Widget? child,
) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
backgroundColor: mainColor,
title: Text(
model.header,
style: const TextStyle(
color: fontColor,
fontSize: 20,
),
),
elevation: 0,
automaticallyImplyLeading: false,
actions: [
IconButton(
onPressed: () {
model.exitApp();
},
icon: const Icon(Icons.logout, color: fontColor),
),
],
),
extendBody: false,
body: ExtendedNavigator(
router: UserTrackingIndexViewRouter(),
navigatorKey: StackedService.nestedNavigationKey(2),
initialRoute: UserTrackingIndexViewRoutes.listKamusKesehatanView,
),
bottomNavigationBar: StylishBottomBar(
items: [
for (var item in model.bottomNavBarList)
BottomBarItem(
icon: Icon(item['icon'],
color: model.currentIndex ==
model.bottomNavBarList.indexOf(item)
? mainColor
: fontColor),
title: Text(
item['name'],
style: regularTextStyle.copyWith(
color: model.currentIndex ==
model.bottomNavBarList.indexOf(item)
? mainColor
: fontColor,
),
// textAlign: TextAlign.l,
),
backgroundColor: model.currentIndex ==
model.bottomNavBarList.indexOf(item)
? fontColor
: fontColor,
),
],
currentIndex: model.currentIndex,
option: BubbleBarOptions(),
hasNotch: true,
backgroundColor: mainColor,
onTap: (value) {
model.handleNavigation(value);
},
// fabLocation: StylishBarFabLocation.center,
),
),
);
},
);
}
}

View File

@ -0,0 +1,72 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
import '../../../app/app.locator.dart';
import '../../../app/app.logger.dart';
import '../../../app/app.router.dart';
import '../../../services/global_var.dart';
class UserTrackingIndexViewModel extends IndexTrackingViewModel {
final log = getLogger('UserTrackingIndexViewModel');
final globalVar = locator<GlobalVar>();
final navigationService = locator<NavigationService>();
final dialogService = locator<DialogService>();
final _bottomNavBarList = [
{
'name': 'List Kamus',
'icon': Icons.list_alt_outlined,
'header': 'List Kamus',
},
{
'name': 'Profil Developer',
'icon': Icons.person_outline_outlined,
'header': 'Profil Developer',
},
];
String header = 'List Kamus';
List<Map<String, dynamic>> get bottomNavBarList => _bottomNavBarList;
final List<String> _views = [
// UserTrackingIndexViewRoutes.tampilkanListView,
UserTrackingIndexViewRoutes.listKamusKesehatanView,
UserTrackingIndexViewRoutes.profilUserView,
];
Future<void> init() async {
// setIndex(0);
// handleNavigation(0);
}
void handleNavigation(int index) {
// log.d("handleNavigation: $index");
// log.d("currentIndex: $currentIndex");
if (currentIndex == index) return;
setIndex(index);
header = _bottomNavBarList[index]['header'] as String;
navigationService.navigateTo(
_views[index],
id: 2,
);
}
exitApp() async {
dialogService
.showConfirmationDialog(
title: 'Konfirmasi',
description: 'Apakah anda yakin ingin keluar?',
cancelTitle: 'Batal',
confirmationTitle: 'Keluar',
)
.then((value) async {
if (value!.confirmed) {
SystemNavigator.pop();
}
});
}
}

View File

@ -0,0 +1,38 @@
import 'package:flutter/material.dart';
import '../../app/themes/app_colors.dart';
class MyButton extends StatelessWidget {
const MyButton({
Key? key,
required this.text,
this.theBackgroundColor = mainColor,
this.textColor = backgroundColor,
this.onPressed,
}) : super(key: key);
final String text;
final VoidCallback? onPressed;
final Color theBackgroundColor;
final Color textColor;
@override
Widget build(BuildContext context) {
return ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: theBackgroundColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
),
onPressed: onPressed,
child: Text(
text,
style: TextStyle(
color: textColor,
fontSize: 18,
),
),
);
}
}

View File

@ -0,0 +1,88 @@
import 'package:flutter/material.dart';
import '../../app/themes/app_colors.dart';
class MyTextFormField extends StatelessWidget {
const MyTextFormField({
Key? key,
this.labelText,
this.hintText,
this.obscureText,
this.validator,
this.suffixIcon,
this.prefixIcon,
this.focusNode,
this.controller,
this.maxLines = 1,
this.onEditingComplete,
this.keyboardType = TextInputType.text,
this.initialValue,
this.readOnly = false,
this.maxLength,
this.onChanged,
}) : super(key: key);
final String? labelText;
final String? hintText;
final bool? obscureText;
final FormFieldValidator<String>? validator;
final Widget? suffixIcon;
final Widget? prefixIcon;
final FocusNode? focusNode;
final TextEditingController? controller;
final int maxLines;
final VoidCallback? onEditingComplete;
final TextInputType keyboardType;
final String? initialValue;
final bool readOnly;
final int? maxLength;
final void Function(String)? onChanged;
@override
Widget build(BuildContext context) {
return TextFormField(
onChanged: onChanged,
maxLength: maxLength,
readOnly: readOnly,
initialValue: initialValue,
onEditingComplete: onEditingComplete,
maxLines: maxLines,
controller: controller,
focusNode: focusNode,
obscureText: obscureText ?? false,
keyboardType: keyboardType,
decoration: InputDecoration(
prefixIcon: prefixIcon,
suffixIcon: suffixIcon,
enabledBorder: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(25)),
borderSide: BorderSide(
// color: mainColor,
),
),
focusedBorder: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(25)),
borderSide: BorderSide(
// color: mainColor,
),
),
focusedErrorBorder: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(25)),
borderSide: BorderSide(
color: dangerColor,
),
),
errorBorder: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(25)),
borderSide: BorderSide(
color: dangerColor,
),
),
labelText: labelText,
hintText: hintText,
labelStyle: const TextStyle(color: fontColor),
),
validator: validator,
);
}
}

View File

@ -0,0 +1,73 @@
import 'package:flutter/material.dart';
import '../../app/themes/app_colors.dart';
class TopContainer extends StatelessWidget {
const TopContainer({
super.key,
required this.title,
required this.value,
required this.icon,
required this.background,
});
final String title;
final String value;
final IconData icon;
final Color background;
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
width: double.infinity,
decoration: BoxDecoration(
color: background,
borderRadius: BorderRadius.circular(10),
),
child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
flex: 3,
child: Text(
title,
style: const TextStyle(
color: fontGrey,
fontSize: 20,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.justify,
),
),
const Expanded(
flex: 1,
child: Text(
':',
style: TextStyle(
color: fontGrey,
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
Expanded(
flex: 5,
child: Text(
value,
style: const TextStyle(
color: fontGrey,
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
Icon(
icon,
color: fontGrey,
),
],
),
);
}
}