first commit

This commit is contained in:
kicap
2023-08-18 01:07:57 +08:00
commit 3b437c4f82
320 changed files with 10503 additions and 0 deletions

View File

@ -0,0 +1,25 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// **************************************************************************
// StackedBottomsheetGenerator
// **************************************************************************
import 'package:stacked_services/stacked_services.dart';
import 'app.locator.dart';
import '../ui/views/app_index_tracking/custom_currency/konversi_dialog/pilih_negara_bottom_sheet/pilih_negara_bottom_sheet_view.dart';
enum BottomSheetType {
pilihNegaraBottomSheetView,
}
void setupBottomSheetUi() {
final bottomsheetService = locator<BottomSheetService>();
final Map<BottomSheetType, SheetBuilder> builders = {
BottomSheetType.pilihNegaraBottomSheetView: (context, request, completer) =>
PilihNegaraBottomSheetView(request: request, completer: completer),
};
bottomsheetService.setCustomSheetBuilders(builders);
}

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

@ -0,0 +1,48 @@
import 'package:curreny_exchange/ui/views/app_index_tracking/custom_currency/konversi_dialog/konversi_dialog_view.dart';
import 'package:curreny_exchange/ui/views/app_index_tracking/custom_currency/konversi_dialog/pilih_negara_bottom_sheet/pilih_negara_bottom_sheet_view.dart';
import 'package:stacked_services/stacked_services.dart';
import 'package:stacked/stacked_annotations.dart';
import '../services/global_var.dart';
import '../services/http_services.dart';
import '../services/my_easyloading.dart';
import '../services/other_function.dart';
import '../ui/views/app_index_tracking/app_index_tracking_view.dart';
import '../ui/views/app_index_tracking/custom_currency/custom_currency_view.dart';
import '../ui/views/app_index_tracking/today_currency/today_currency_view.dart';
import '../ui/views/loading_screen/loading_screen_view.dart';
import '../ui/views/splash_screen/splash_screen_view.dart';
@StackedApp(
routes: [
MaterialRoute(page: SplashScreenView, initial: true),
MaterialRoute(
page: AppIndexTrackingView,
children: [
MaterialRoute(page: LoadingScreenView, initial: true),
MaterialRoute(page: TodayCurrencyView),
MaterialRoute(page: CustomCurrencyView),
],
),
],
dialogs: [
StackedDialog(classType: KonversiDialogView),
],
bottomsheets: [
StackedBottomsheet(classType: PilihNegaraBottomSheetView),
],
dependencies: [
LazySingleton(classType: NavigationService),
LazySingleton(classType: DialogService),
LazySingleton(classType: SnackbarService),
LazySingleton(classType: BottomSheetService),
//
LazySingleton(classType: MyEasyLoading),
LazySingleton(classType: MyHttpServices),
LazySingleton(classType: OtherFunction),
LazySingleton(classType: GlobalVar),
],
logger: StackedLogger(),
)
class App {}

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

@ -0,0 +1,25 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// **************************************************************************
// StackedDialogGenerator
// **************************************************************************
import 'package:stacked_services/stacked_services.dart';
import 'app.locator.dart';
import '../ui/views/app_index_tracking/custom_currency/konversi_dialog/konversi_dialog_view.dart';
enum DialogType {
konversiDialogView,
}
void setupDialogUi() {
final dialogService = locator<DialogService>();
final Map<DialogType, DialogBuilder> builders = {
DialogType.konversiDialogView: (context, request, completer) =>
KonversiDialogView(request: request, completer: completer),
};
dialogService.registerCustomDialogBuilders(builders);
}

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

@ -0,0 +1,39 @@
// 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/http_services.dart';
import '../services/my_easyloading.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(() => MyHttpServices());
locator.registerLazySingleton(() => OtherFunction());
locator.registerLazySingleton(() => GlobalVar());
}

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(),
]),
);
}

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

@ -0,0 +1,270 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// **************************************************************************
// StackedNavigatorGenerator
// **************************************************************************
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:curreny_exchange/ui/views/app_index_tracking/app_index_tracking_view.dart'
as _i3;
import 'package:curreny_exchange/ui/views/app_index_tracking/custom_currency/custom_currency_view.dart'
as _i7;
import 'package:curreny_exchange/ui/views/app_index_tracking/today_currency/today_currency_view.dart'
as _i6;
import 'package:curreny_exchange/ui/views/loading_screen/loading_screen_view.dart'
as _i5;
import 'package:curreny_exchange/ui/views/splash_screen/splash_screen_view.dart'
as _i2;
import 'package:flutter/material.dart' as _i4;
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart' as _i1;
import 'package:stacked_services/stacked_services.dart' as _i8;
class Routes {
static const splashScreenView = '/';
static const appIndexTrackingView = '/app-index-tracking-view';
static const all = <String>{
splashScreenView,
appIndexTrackingView,
};
}
class StackedRouter extends _i1.RouterBase {
final _routes = <_i1.RouteDef>[
_i1.RouteDef(
Routes.splashScreenView,
page: _i2.SplashScreenView,
),
_i1.RouteDef(
Routes.appIndexTrackingView,
page: _i3.AppIndexTrackingView,
),
];
final _pagesMap = <Type, _i1.StackedRouteFactory>{
_i2.SplashScreenView: (data) {
return _i4.MaterialPageRoute<dynamic>(
builder: (context) => const _i2.SplashScreenView(),
settings: data,
);
},
_i3.AppIndexTrackingView: (data) {
return _i4.MaterialPageRoute<dynamic>(
builder: (context) => const _i3.AppIndexTrackingView(),
settings: data,
);
},
};
@override
List<_i1.RouteDef> get routes => _routes;
@override
Map<Type, _i1.StackedRouteFactory> get pagesMap => _pagesMap;
}
class AppIndexTrackingViewRoutes {
static const loadingScreenView = '';
static const todayCurrencyView = 'today-currency-view';
static const customCurrencyView = 'custom-currency-view';
static const all = <String>{
loadingScreenView,
todayCurrencyView,
customCurrencyView,
};
}
class AppIndexTrackingViewRouter extends _i1.RouterBase {
final _routes = <_i1.RouteDef>[
_i1.RouteDef(
AppIndexTrackingViewRoutes.loadingScreenView,
page: _i5.LoadingScreenView,
),
_i1.RouteDef(
AppIndexTrackingViewRoutes.todayCurrencyView,
page: _i6.TodayCurrencyView,
),
_i1.RouteDef(
AppIndexTrackingViewRoutes.customCurrencyView,
page: _i7.CustomCurrencyView,
),
];
final _pagesMap = <Type, _i1.StackedRouteFactory>{
_i5.LoadingScreenView: (data) {
return _i4.MaterialPageRoute<dynamic>(
builder: (context) => const _i5.LoadingScreenView(),
settings: data,
);
},
_i6.TodayCurrencyView: (data) {
return _i4.MaterialPageRoute<dynamic>(
builder: (context) => const _i6.TodayCurrencyView(),
settings: data,
);
},
_i7.CustomCurrencyView: (data) {
return _i4.MaterialPageRoute<dynamic>(
builder: (context) => const _i7.CustomCurrencyView(),
settings: data,
);
},
};
@override
List<_i1.RouteDef> get routes => _routes;
@override
Map<Type, _i1.StackedRouteFactory> get pagesMap => _pagesMap;
}
extension NavigatorStateExtension on _i8.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> navigateToAppIndexTrackingView([
int? routerId,
bool preventDuplicates = true,
Map<String, String>? parameters,
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
transition,
]) async {
return navigateTo<dynamic>(Routes.appIndexTrackingView,
id: routerId,
preventDuplicates: preventDuplicates,
parameters: parameters,
transition: transition);
}
Future<dynamic>
navigateToNestedLoadingScreenViewInAppIndexTrackingViewRouter([
int? routerId,
bool preventDuplicates = true,
Map<String, String>? parameters,
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
transition,
]) async {
return navigateTo<dynamic>(AppIndexTrackingViewRoutes.loadingScreenView,
id: routerId,
preventDuplicates: preventDuplicates,
parameters: parameters,
transition: transition);
}
Future<dynamic>
navigateToNestedTodayCurrencyViewInAppIndexTrackingViewRouter([
int? routerId,
bool preventDuplicates = true,
Map<String, String>? parameters,
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
transition,
]) async {
return navigateTo<dynamic>(AppIndexTrackingViewRoutes.todayCurrencyView,
id: routerId,
preventDuplicates: preventDuplicates,
parameters: parameters,
transition: transition);
}
Future<dynamic>
navigateToNestedCustomCurrencyViewInAppIndexTrackingViewRouter([
int? routerId,
bool preventDuplicates = true,
Map<String, String>? parameters,
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
transition,
]) async {
return navigateTo<dynamic>(AppIndexTrackingViewRoutes.customCurrencyView,
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> replaceWithAppIndexTrackingView([
int? routerId,
bool preventDuplicates = true,
Map<String, String>? parameters,
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
transition,
]) async {
return replaceWith<dynamic>(Routes.appIndexTrackingView,
id: routerId,
preventDuplicates: preventDuplicates,
parameters: parameters,
transition: transition);
}
Future<dynamic>
replaceWithNestedLoadingScreenViewInAppIndexTrackingViewRouter([
int? routerId,
bool preventDuplicates = true,
Map<String, String>? parameters,
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
transition,
]) async {
return replaceWith<dynamic>(AppIndexTrackingViewRoutes.loadingScreenView,
id: routerId,
preventDuplicates: preventDuplicates,
parameters: parameters,
transition: transition);
}
Future<dynamic>
replaceWithNestedTodayCurrencyViewInAppIndexTrackingViewRouter([
int? routerId,
bool preventDuplicates = true,
Map<String, String>? parameters,
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
transition,
]) async {
return replaceWith<dynamic>(AppIndexTrackingViewRoutes.todayCurrencyView,
id: routerId,
preventDuplicates: preventDuplicates,
parameters: parameters,
transition: transition);
}
Future<dynamic>
replaceWithNestedCustomCurrencyViewInAppIndexTrackingViewRouter([
int? routerId,
bool preventDuplicates = true,
Map<String, String>? parameters,
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
transition,
]) async {
return replaceWith<dynamic>(AppIndexTrackingViewRoutes.customCurrencyView,
id: routerId,
preventDuplicates: preventDuplicates,
parameters: parameters,
transition: transition);
}
}

View File

@ -0,0 +1,57 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
import '../../services/global_var.dart';
import '../../services/http_services.dart';
import '../../services/my_easyloading.dart';
import '../../services/other_function.dart';
import '../app.locator.dart';
import '../themes/app_colors.dart';
class CustomBaseViewModel extends BaseViewModel {
final dialogService = locator<DialogService>();
final navigationService = locator<NavigationService>();
final bottomSheetService = locator<BottomSheetService>();
final snackbarService = locator<SnackbarService>();
final easyLoading = locator<MyEasyLoading>();
final httpService = locator<MyHttpServices>();
final otherFunction = locator<OtherFunction>();
final globalVar = locator<GlobalVar>();
// late bool backPressed;
void back() {
navigationService.back();
}
quitApp(BuildContext context) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Keluar'),
content: const Text('Apakah Anda yakin ingin keluar?'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: const Text('Batal'),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: const Text(
'Keluar',
style: TextStyle(color: dangerColor),
),
),
],
);
},
).then((value) {
if (value == true) {
SystemNavigator.pop();
}
});
}
}

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

@ -0,0 +1,30 @@
import 'dart:ui';
const Color mainColor = Color.fromARGB(255, 37, 88, 241);
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: 'Comic Sans MS',
fontSize: 14,
fontWeight: FontWeight.w400,
color: fontColor);
const italicTextStyle = TextStyle(
fontFamily: 'Comic Sans MS',
fontSize: 14,
color: fontColor,
fontStyle: FontStyle.italic,
);
const mediumTextStyle = TextStyle(
fontFamily: 'Comic Sans MS',
fontSize: 14,
fontWeight: FontWeight.w500,
color: fontColor,
);
const semiBoldTextStyle = TextStyle(
fontFamily: 'Comic Sans MS',
fontSize: 14,
fontWeight: FontWeight.w600,
color: fontColor,
);
const boldTextStyle = TextStyle(
fontFamily: 'Comic Sans MS',
fontSize: 14,
fontWeight: FontWeight.w700,
color: fontColor,
);
const extraBoldTextStyle = TextStyle(
fontFamily: 'Comic Sans MS',
fontSize: 14,
fontWeight: FontWeight.w800,
color: fontColor,
);

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

@ -0,0 +1,124 @@
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(
displayLarge: regularTextStyle.copyWith(fontSize: 32),
displayMedium: regularTextStyle.copyWith(fontSize: 20),
displaySmall: 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),
);

39
lib/main.dart Normal file
View File

@ -0,0 +1,39 @@
import 'package:curreny_exchange/app/app.router.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:stacked_services/stacked_services.dart';
import 'app/app.bottomsheets.dart';
import 'app/app.dialogs.dart';
import 'app/app.locator.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
await dotenv.load(fileName: ".env");
await setupAllLocator();
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: 'Currency Exchange',
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,660 @@
class AllCurrencyModel {
int? iDR;
double? aED;
double? aFN;
double? aLL;
double? aMD;
double? aNG;
double? aOA;
double? aRS;
double? aUD;
double? aWG;
double? aZN;
double? bAM;
double? bBD;
double? bDT;
double? bGN;
double? bHD;
double? bIF;
double? bMD;
double? bND;
double? bOB;
double? bRL;
double? bSD;
double? bTN;
double? bWP;
double? bYN;
double? bZD;
double? cAD;
double? cDF;
double? cHF;
double? cLP;
double? cNY;
double? cOP;
double? cRC;
double? cUP;
double? cVE;
double? cZK;
double? dJF;
double? dKK;
double? dOP;
double? dZD;
double? eGP;
double? eRN;
double? eTB;
double? eUR;
double? fJD;
double? fKP;
double? fOK;
double? gBP;
double? gEL;
double? gGP;
double? gHS;
double? gIP;
double? gMD;
double? gNF;
double? gTQ;
double? gYD;
double? hKD;
double? hNL;
double? hRK;
double? hTG;
double? hUF;
double? iLS;
double? iMP;
double? iNR;
double? iQD;
double? iRR;
double? iSK;
double? jEP;
double? jMD;
double? jOD;
double? jPY;
double? kES;
double? kGS;
double? kHR;
double? kID;
double? kMF;
double? kRW;
double? kWD;
double? kYD;
double? kZT;
double? lAK;
double? lBP;
double? lKR;
double? lRD;
double? lSL;
double? lYD;
double? mAD;
double? mDL;
double? mGA;
double? mKD;
double? mMK;
double? mNT;
double? mOP;
double? mRU;
double? mUR;
double? mVR;
double? mWK;
double? mXN;
double? mYR;
double? mZN;
double? nAD;
double? nGN;
double? nIO;
double? nOK;
double? nPR;
double? nZD;
double? oMR;
double? pAB;
double? pEN;
double? pGK;
double? pHP;
double? pKR;
double? pLN;
double? pYG;
double? qAR;
double? rON;
double? rSD;
double? rUB;
double? rWF;
double? sAR;
double? sBD;
double? sCR;
double? sDG;
double? sEK;
double? sGD;
double? sHP;
double? sLE;
double? sLL;
double? sOS;
double? sRD;
double? sSP;
double? sTN;
double? sYP;
double? sZL;
double? tHB;
double? tJS;
double? tMT;
double? tND;
double? tOP;
double? tRY;
double? tTD;
double? tVD;
double? tWD;
double? tZS;
double? uAH;
double? uGX;
double? uSD;
double? uYU;
double? uZS;
double? vES;
double? vND;
double? vUV;
double? wST;
double? xAF;
double? xCD;
double? xDR;
double? xOF;
double? xPF;
double? yER;
double? zAR;
double? zMW;
double? zWL;
AllCurrencyModel(
{this.iDR,
this.aED,
this.aFN,
this.aLL,
this.aMD,
this.aNG,
this.aOA,
this.aRS,
this.aUD,
this.aWG,
this.aZN,
this.bAM,
this.bBD,
this.bDT,
this.bGN,
this.bHD,
this.bIF,
this.bMD,
this.bND,
this.bOB,
this.bRL,
this.bSD,
this.bTN,
this.bWP,
this.bYN,
this.bZD,
this.cAD,
this.cDF,
this.cHF,
this.cLP,
this.cNY,
this.cOP,
this.cRC,
this.cUP,
this.cVE,
this.cZK,
this.dJF,
this.dKK,
this.dOP,
this.dZD,
this.eGP,
this.eRN,
this.eTB,
this.eUR,
this.fJD,
this.fKP,
this.fOK,
this.gBP,
this.gEL,
this.gGP,
this.gHS,
this.gIP,
this.gMD,
this.gNF,
this.gTQ,
this.gYD,
this.hKD,
this.hNL,
this.hRK,
this.hTG,
this.hUF,
this.iLS,
this.iMP,
this.iNR,
this.iQD,
this.iRR,
this.iSK,
this.jEP,
this.jMD,
this.jOD,
this.jPY,
this.kES,
this.kGS,
this.kHR,
this.kID,
this.kMF,
this.kRW,
this.kWD,
this.kYD,
this.kZT,
this.lAK,
this.lBP,
this.lKR,
this.lRD,
this.lSL,
this.lYD,
this.mAD,
this.mDL,
this.mGA,
this.mKD,
this.mMK,
this.mNT,
this.mOP,
this.mRU,
this.mUR,
this.mVR,
this.mWK,
this.mXN,
this.mYR,
this.mZN,
this.nAD,
this.nGN,
this.nIO,
this.nOK,
this.nPR,
this.nZD,
this.oMR,
this.pAB,
this.pEN,
this.pGK,
this.pHP,
this.pKR,
this.pLN,
this.pYG,
this.qAR,
this.rON,
this.rSD,
this.rUB,
this.rWF,
this.sAR,
this.sBD,
this.sCR,
this.sDG,
this.sEK,
this.sGD,
this.sHP,
this.sLE,
this.sLL,
this.sOS,
this.sRD,
this.sSP,
this.sTN,
this.sYP,
this.sZL,
this.tHB,
this.tJS,
this.tMT,
this.tND,
this.tOP,
this.tRY,
this.tTD,
this.tVD,
this.tWD,
this.tZS,
this.uAH,
this.uGX,
this.uSD,
this.uYU,
this.uZS,
this.vES,
this.vND,
this.vUV,
this.wST,
this.xAF,
this.xCD,
this.xDR,
this.xOF,
this.xPF,
this.yER,
this.zAR,
this.zMW,
this.zWL});
AllCurrencyModel.fromJson(Map<String, dynamic> json) {
iDR = json['IDR'];
aED = json['AED'];
aFN = json['AFN'];
aLL = json['ALL'];
aMD = json['AMD'];
aNG = json['ANG'];
aOA = json['AOA'];
aRS = json['ARS'];
aUD = json['AUD'];
aWG = json['AWG'];
aZN = json['AZN'];
bAM = json['BAM'];
bBD = json['BBD'];
bDT = json['BDT'];
bGN = json['BGN'];
bHD = json['BHD'];
bIF = json['BIF'];
bMD = json['BMD'];
bND = json['BND'];
bOB = json['BOB'];
bRL = json['BRL'];
bSD = json['BSD'];
bTN = json['BTN'];
bWP = json['BWP'];
bYN = json['BYN'];
bZD = json['BZD'];
cAD = json['CAD'];
cDF = json['CDF'];
cHF = json['CHF'];
cLP = json['CLP'];
cNY = json['CNY'];
cOP = json['COP'];
cRC = json['CRC'];
cUP = json['CUP'];
cVE = json['CVE'];
cZK = json['CZK'];
dJF = json['DJF'];
dKK = json['DKK'];
dOP = json['DOP'];
dZD = json['DZD'];
eGP = json['EGP'];
eRN = json['ERN'];
eTB = json['ETB'];
eUR = json['EUR'];
fJD = json['FJD'];
fKP = json['FKP'];
fOK = json['FOK'];
gBP = json['GBP'];
gEL = json['GEL'];
gGP = json['GGP'];
gHS = json['GHS'];
gIP = json['GIP'];
gMD = json['GMD'];
gNF = json['GNF'];
gTQ = json['GTQ'];
gYD = json['GYD'];
hKD = json['HKD'];
hNL = json['HNL'];
hRK = json['HRK'];
hTG = json['HTG'];
hUF = json['HUF'];
iLS = json['ILS'];
iMP = json['IMP'];
iNR = json['INR'];
iQD = json['IQD'];
iRR = json['IRR'];
iSK = json['ISK'];
jEP = json['JEP'];
jMD = json['JMD'];
jOD = json['JOD'];
jPY = json['JPY'];
kES = json['KES'];
kGS = json['KGS'];
kHR = json['KHR'];
kID = json['KID'];
kMF = json['KMF'];
kRW = json['KRW'];
kWD = json['KWD'];
kYD = json['KYD'];
kZT = json['KZT'];
lAK = json['LAK'];
lBP = json['LBP'];
lKR = json['LKR'];
lRD = json['LRD'];
lSL = json['LSL'];
lYD = json['LYD'];
mAD = json['MAD'];
mDL = json['MDL'];
mGA = json['MGA'];
mKD = json['MKD'];
mMK = json['MMK'];
mNT = json['MNT'];
mOP = json['MOP'];
mRU = json['MRU'];
mUR = json['MUR'];
mVR = json['MVR'];
mWK = json['MWK'];
mXN = json['MXN'];
mYR = json['MYR'];
mZN = json['MZN'];
nAD = json['NAD'];
nGN = json['NGN'];
nIO = json['NIO'];
nOK = json['NOK'];
nPR = json['NPR'];
nZD = json['NZD'];
oMR = json['OMR'];
pAB = json['PAB'];
pEN = json['PEN'];
pGK = json['PGK'];
pHP = json['PHP'];
pKR = json['PKR'];
pLN = json['PLN'];
pYG = json['PYG'];
qAR = json['QAR'];
rON = json['RON'];
rSD = json['RSD'];
rUB = json['RUB'];
rWF = json['RWF'];
sAR = json['SAR'];
sBD = json['SBD'];
sCR = json['SCR'];
sDG = json['SDG'];
sEK = json['SEK'];
sGD = json['SGD'];
sHP = json['SHP'];
sLE = json['SLE'];
sLL = json['SLL'];
sOS = json['SOS'];
sRD = json['SRD'];
sSP = json['SSP'];
sTN = json['STN'];
sYP = json['SYP'];
sZL = json['SZL'];
tHB = json['THB'];
tJS = json['TJS'];
tMT = json['TMT'];
tND = json['TND'];
tOP = json['TOP'];
tRY = json['TRY'];
tTD = json['TTD'];
tVD = json['TVD'];
tWD = json['TWD'];
tZS = json['TZS'];
uAH = json['UAH'];
uGX = json['UGX'];
uSD = json['USD'];
uYU = json['UYU'];
uZS = json['UZS'];
vES = json['VES'];
vND = json['VND'];
vUV = json['VUV'];
wST = json['WST'];
xAF = json['XAF'];
xCD = json['XCD'];
xDR = json['XDR'];
xOF = json['XOF'];
xPF = json['XPF'];
yER = json['YER'];
zAR = json['ZAR'];
zMW = json['ZMW'];
zWL = json['ZWL'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['IDR'] = iDR;
data['AED'] = aED;
data['AFN'] = aFN;
data['ALL'] = aLL;
data['AMD'] = aMD;
data['ANG'] = aNG;
data['AOA'] = aOA;
data['ARS'] = aRS;
data['AUD'] = aUD;
data['AWG'] = aWG;
data['AZN'] = aZN;
data['BAM'] = bAM;
data['BBD'] = bBD;
data['BDT'] = bDT;
data['BGN'] = bGN;
data['BHD'] = bHD;
data['BIF'] = bIF;
data['BMD'] = bMD;
data['BND'] = bND;
data['BOB'] = bOB;
data['BRL'] = bRL;
data['BSD'] = bSD;
data['BTN'] = bTN;
data['BWP'] = bWP;
data['BYN'] = bYN;
data['BZD'] = bZD;
data['CAD'] = cAD;
data['CDF'] = cDF;
data['CHF'] = cHF;
data['CLP'] = cLP;
data['CNY'] = cNY;
data['COP'] = cOP;
data['CRC'] = cRC;
data['CUP'] = cUP;
data['CVE'] = cVE;
data['CZK'] = cZK;
data['DJF'] = dJF;
data['DKK'] = dKK;
data['DOP'] = dOP;
data['DZD'] = dZD;
data['EGP'] = eGP;
data['ERN'] = eRN;
data['ETB'] = eTB;
data['EUR'] = eUR;
data['FJD'] = fJD;
data['FKP'] = fKP;
data['FOK'] = fOK;
data['GBP'] = gBP;
data['GEL'] = gEL;
data['GGP'] = gGP;
data['GHS'] = gHS;
data['GIP'] = gIP;
data['GMD'] = gMD;
data['GNF'] = gNF;
data['GTQ'] = gTQ;
data['GYD'] = gYD;
data['HKD'] = hKD;
data['HNL'] = hNL;
data['HRK'] = hRK;
data['HTG'] = hTG;
data['HUF'] = hUF;
data['ILS'] = iLS;
data['IMP'] = iMP;
data['INR'] = iNR;
data['IQD'] = iQD;
data['IRR'] = iRR;
data['ISK'] = iSK;
data['JEP'] = jEP;
data['JMD'] = jMD;
data['JOD'] = jOD;
data['JPY'] = jPY;
data['KES'] = kES;
data['KGS'] = kGS;
data['KHR'] = kHR;
data['KID'] = kID;
data['KMF'] = kMF;
data['KRW'] = kRW;
data['KWD'] = kWD;
data['KYD'] = kYD;
data['KZT'] = kZT;
data['LAK'] = lAK;
data['LBP'] = lBP;
data['LKR'] = lKR;
data['LRD'] = lRD;
data['LSL'] = lSL;
data['LYD'] = lYD;
data['MAD'] = mAD;
data['MDL'] = mDL;
data['MGA'] = mGA;
data['MKD'] = mKD;
data['MMK'] = mMK;
data['MNT'] = mNT;
data['MOP'] = mOP;
data['MRU'] = mRU;
data['MUR'] = mUR;
data['MVR'] = mVR;
data['MWK'] = mWK;
data['MXN'] = mXN;
data['MYR'] = mYR;
data['MZN'] = mZN;
data['NAD'] = nAD;
data['NGN'] = nGN;
data['NIO'] = nIO;
data['NOK'] = nOK;
data['NPR'] = nPR;
data['NZD'] = nZD;
data['OMR'] = oMR;
data['PAB'] = pAB;
data['PEN'] = pEN;
data['PGK'] = pGK;
data['PHP'] = pHP;
data['PKR'] = pKR;
data['PLN'] = pLN;
data['PYG'] = pYG;
data['QAR'] = qAR;
data['RON'] = rON;
data['RSD'] = rSD;
data['RUB'] = rUB;
data['RWF'] = rWF;
data['SAR'] = sAR;
data['SBD'] = sBD;
data['SCR'] = sCR;
data['SDG'] = sDG;
data['SEK'] = sEK;
data['SGD'] = sGD;
data['SHP'] = sHP;
data['SLE'] = sLE;
data['SLL'] = sLL;
data['SOS'] = sOS;
data['SRD'] = sRD;
data['SSP'] = sSP;
data['STN'] = sTN;
data['SYP'] = sYP;
data['SZL'] = sZL;
data['THB'] = tHB;
data['TJS'] = tJS;
data['TMT'] = tMT;
data['TND'] = tND;
data['TOP'] = tOP;
data['TRY'] = tRY;
data['TTD'] = tTD;
data['TVD'] = tVD;
data['TWD'] = tWD;
data['TZS'] = tZS;
data['UAH'] = uAH;
data['UGX'] = uGX;
data['USD'] = uSD;
data['UYU'] = uYU;
data['UZS'] = uZS;
data['VES'] = vES;
data['VND'] = vND;
data['VUV'] = vUV;
data['WST'] = wST;
data['XAF'] = xAF;
data['XCD'] = xCD;
data['XDR'] = xDR;
data['XOF'] = xOF;
data['XPF'] = xPF;
data['YER'] = yER;
data['ZAR'] = zAR;
data['ZMW'] = zMW;
data['ZWL'] = zWL;
return data;
}
}

View File

@ -0,0 +1,26 @@
class AllInfoModel {
String? entity;
String? currency;
String? alphabeticCode;
int? numericCode;
int? minorUnit;
String? withdrawalDate;
AllInfoModel(
{this.entity,
this.currency,
this.alphabeticCode,
this.numericCode,
this.minorUnit,
this.withdrawalDate});
AllInfoModel.fromJson(Map<String, dynamic> json) {
entity = json['Entity'];
currency = json['Currency'];
alphabeticCode = json['AlphabeticCode'];
// if jsonNumericCode is string, convert to int
numericCode = num.tryParse(json['NumericCode'].toString())?.toInt();
minorUnit = num.tryParse(json['NumericCode'].toString())?.toInt();
withdrawalDate = json['WithdrawalDate'];
}
}

View File

@ -0,0 +1,58 @@
class ConversionResultModel {
String? result;
String? documentation;
String? termsOfUse;
int? timeLastUpdateUnix;
String? timeLastUpdateUtc;
int? timeNextUpdateUnix;
String? timeNextUpdateUtc;
String? baseCode;
String? targetCode;
double? conversionRate;
double? conversionResult;
ConversionResultModel(
{this.result,
this.documentation,
this.termsOfUse,
this.timeLastUpdateUnix,
this.timeLastUpdateUtc,
this.timeNextUpdateUnix,
this.timeNextUpdateUtc,
this.baseCode,
this.targetCode,
this.conversionRate,
this.conversionResult});
ConversionResultModel.fromJson(Map<String, dynamic> json) {
result = json['result'];
documentation = json['documentation'];
termsOfUse = json['terms_of_use'];
timeLastUpdateUnix = json['time_last_update_unix'];
timeLastUpdateUtc = json['time_last_update_utc'];
timeNextUpdateUnix = json['time_next_update_unix'];
timeNextUpdateUtc = json['time_next_update_utc'];
baseCode = json['base_code'];
targetCode = json['target_code'];
conversionRate = json['conversion_rate'];
conversionResult = json['conversion_result'] is int
? (json['conversion_result'] as int).toDouble()
: json['conversion_result'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['result'] = result;
data['documentation'] = documentation;
data['terms_of_use'] = termsOfUse;
data['time_last_update_unix'] = timeLastUpdateUnix;
data['time_last_update_utc'] = timeLastUpdateUtc;
data['time_next_update_unix'] = timeNextUpdateUnix;
data['time_next_update_utc'] = timeNextUpdateUtc;
data['base_code'] = baseCode;
data['target_code'] = targetCode;
data['conversion_rate'] = conversionRate;
data['conversion_result'] = conversionResult;
return data;
}
}

View File

@ -0,0 +1,48 @@
class MyResponseModel {
String? result;
String? documentation;
String? termsOfUse;
int? timeLastUpdateUnix;
String? timeLastUpdateUtc;
int? timeNextUpdateUnix;
String? timeNextUpdateUtc;
String? baseCode;
dynamic conversionRates;
MyResponseModel(
{this.result,
this.documentation,
this.termsOfUse,
this.timeLastUpdateUnix,
this.timeLastUpdateUtc,
this.timeNextUpdateUnix,
this.timeNextUpdateUtc,
this.baseCode,
this.conversionRates});
MyResponseModel.fromJson(Map<String, dynamic> json) {
result = json['result'];
documentation = json['documentation'];
termsOfUse = json['terms_of_use'];
timeLastUpdateUnix = json['time_last_update_unix'];
timeLastUpdateUtc = json['time_last_update_utc'];
timeNextUpdateUnix = json['time_next_update_unix'];
timeNextUpdateUtc = json['time_next_update_utc'];
baseCode = json['base_code'];
conversionRates = json['conversion_rates'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['result'] = result;
data['documentation'] = documentation;
data['terms_of_use'] = termsOfUse;
data['time_last_update_unix'] = timeLastUpdateUnix;
data['time_last_update_utc'] = timeLastUpdateUtc;
data['time_next_update_unix'] = timeNextUpdateUnix;
data['time_next_update_utc'] = timeNextUpdateUtc;
data['base_code'] = baseCode;
data['conversion_rates'] = conversionRates;
return data;
}
}

View File

@ -0,0 +1,12 @@
import '../models/all_currency.dart';
import '../models/all_info_model.dart';
import '../models/my_response.dart';
class GlobalVar {
String backPressed = 'backNormal';
MyResponseModel? myResponseModel;
AllCurrencyModel? allCurrencyModel;
dynamic jsonAllCurrency;
List<AllInfoModel> allInfoModel = [];
}

View File

@ -0,0 +1,34 @@
import 'package:dio/dio.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
// import 'package:flutter_dotenv/flutter_dotenv.dart';
class MyHttpServices {
// final log = getLogger('MyHttpServices');
final _options = BaseOptions(
baseUrl: dotenv.env['api_url']! + dotenv.env['api_keys']!,
connectTimeout: const Duration(seconds: 120),
receiveTimeout: const Duration(seconds: 120),
);
late Dio _dio;
MyHttpServices() {
_dio = Dio(_options);
}
Future<Response> get(String path) async {
try {
return await _dio.get(path);
} on DioException {
rethrow;
}
}
Future<Response> postWithFormData(String path, FormData formData) async {
try {
return await _dio.post(path, data: formData);
} on DioException {
rethrow;
}
}
}

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:intl/intl.dart';
class OtherFunction {
int umur(String tanggalLahir) {
// change tanggalLahir to DateTime
DateTime date = DateTime.parse(tanggalLahir);
// get current date
DateTime now = DateTime.now();
// get difference in year
int year = now.year - date.year;
return year;
}
String commaFormat(int number) {
final formatter = NumberFormat('#,###');
return formatter.format(number);
}
String timeStampConverter(int timeStamp) {
DateTime date = DateTime.fromMillisecondsSinceEpoch(timeStamp * 1000);
// day name, date and hours with am/pm
String formattedDate = DateFormat('EEEE, d MM-yyyy | HH:mm a').format(date);
return formattedDate;
}
}

View File

@ -0,0 +1,91 @@
import 'package:curreny_exchange/app/app.router.dart';
import 'package:flutter/material.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 './app_index_tracking_view_model.dart';
class AppIndexTrackingView extends StatelessWidget {
const AppIndexTrackingView({super.key});
@override
Widget build(BuildContext context) {
return ViewModelBuilder<AppIndexTrackingViewModel>.reactive(
viewModelBuilder: () => AppIndexTrackingViewModel(),
onViewModelReady: (AppIndexTrackingViewModel model) async {
await model.init();
},
builder: (
BuildContext context,
AppIndexTrackingViewModel model,
Widget? child,
) {
return Scaffold(
appBar: AppBar(
title: Center(
child: Text(
model.header,
style: boldTextStyle.copyWith(
color: Colors.white,
),
),
),
backgroundColor: mainColor,
elevation: 0,
automaticallyImplyLeading: false,
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
child: ExtendedNavigator(
navigatorKey: StackedService.nestedNavigationKey(3),
router: AppIndexTrackingViewRouter(),
// initialRoute: AppIndexTrackingViewRoutes.danaSosialAdminView,
),
),
bottomNavigationBar: StylishBottomBar(
items: [
for (var item in model.bottomNavBarList)
BottomBarItem(
icon: Icon(item['icon'],
color: model.currentIndex ==
model.bottomNavBarList.indexOf(item)
? mainColor
: backgroundColor),
title: Align(
alignment: Alignment.centerLeft,
child: Text(
item['name'],
style: regularTextStyle.copyWith(
color: model.currentIndex ==
model.bottomNavBarList.indexOf(item)
? mainColor
: Colors.grey,
),
),
),
backgroundColor:
model.currentIndex == model.bottomNavBarList.indexOf(item)
? Colors.white
: Colors.grey,
),
],
currentIndex: model.currentIndex,
hasNotch: true,
backgroundColor: mainColor,
onTap: (value) {
model.handleNavigation(value);
},
option: BubbleBarOptions(
barStyle: BubbleBarStyle.horizotnal,
bubbleFillStyle: BubbleFillStyle.fill,
opacity: 0.3),
),
);
},
);
}
}

View File

@ -0,0 +1,57 @@
import 'package:flutter/material.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/my_easyloading.dart';
class AppIndexTrackingViewModel extends IndexTrackingViewModel {
final log = getLogger('AppIndexTrackingViewModel');
final _navigationService = locator<NavigationService>();
final _easyLoading = locator<MyEasyLoading>();
final _bottomNavBarList = [
{
'name': 'Tukaran Global',
// money icon
'icon': Icons.attach_money,
'header': 'TUKARAN UANG GLOBAL'
},
{'name': 'Input Tukaran', 'icon': Icons.money, 'header': 'INPUT TUKARAN'},
];
List<Map<String, dynamic>> get bottomNavBarList => _bottomNavBarList;
final List<String> _views = [
AppIndexTrackingViewRoutes.todayCurrencyView,
AppIndexTrackingViewRoutes.customCurrencyView,
];
String header = 'TUKARAN UANG GLOBAL';
Future<void> init() async {
_easyLoading.showLoading();
await Future.delayed(const Duration(seconds: 1));
_navigationService.navigateTo(
_views[0],
id: 3,
);
_easyLoading.dismissLoading();
}
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: 3,
);
}
}

View File

@ -0,0 +1,233 @@
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import '../../../../app/themes/app_colors.dart';
import '../../../../app/themes/app_text.dart';
import './custom_currency_view_model.dart';
class CustomCurrencyView extends StatelessWidget {
const CustomCurrencyView({super.key});
@override
Widget build(BuildContext context) {
return ViewModelBuilder<CustomCurrencyViewModel>.reactive(
viewModelBuilder: () => CustomCurrencyViewModel(),
onViewModelReady: (CustomCurrencyViewModel model) async {
await model.init();
},
builder: (
BuildContext context,
CustomCurrencyViewModel model,
Widget? child,
) {
return WillPopScope(
onWillPop: () async {
// model.navigationService.back();
if (model.globalVar.backPressed == 'backNormal') {
model.quitApp(context);
}
return false;
},
child: Scaffold(
body: model.conversionResultModel == null
? const Center(
child: Text(
'Sila Klik Icon Untuk\nKonversi Tukaran Mata Uang',
textAlign: TextAlign.center,
),
)
: const TheDataWidget(),
floatingActionButton: FloatingActionButton(
onPressed: () {
// model.navigationService.back();
model.myDialog();
},
// icon exchange currency
child: const Icon(Icons.swap_horiz),
)),
);
},
);
}
}
class TheDataWidget extends ViewModelWidget<CustomCurrencyViewModel> {
const TheDataWidget({
super.key,
});
@override
Widget build(BuildContext context, CustomCurrencyViewModel viewModel) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
RichText(
text: TextSpan(
text: 'Terakhir diperbarui : ',
style: regularTextStyle,
children: [
TextSpan(
text: viewModel.otherFunction.timeStampConverter(
viewModel.conversionResultModel!.timeLastUpdateUnix!),
style: boldTextStyle.copyWith(
color: greenColor,
),
),
],
),
),
const SizedBox(
height: 10,
),
RichText(
text: TextSpan(
text: 'Update berikutnya : ',
style: regularTextStyle,
children: [
TextSpan(
text: viewModel.otherFunction.timeStampConverter(
viewModel.conversionResultModel!.timeNextUpdateUnix!),
style: boldTextStyle.copyWith(
color: mainColor,
),
),
],
),
),
const SizedBox(
height: 10,
),
Row(
children: [
const Text('Mata Uang : ', style: regularTextStyle),
const SizedBox(
width: 3,
),
Image.asset(
'assets/flags/${viewModel.currencyInfoModel!.alphabeticCode!.toLowerCase()}.png',
width: 30,
height: 30,
),
const SizedBox(
width: 5,
),
Text(
viewModel.currencyInfoModel!.entity!,
style: boldTextStyle.copyWith(
color: redColor,
),
),
const SizedBox(
width: 3,
),
Text(
'( ${viewModel.currencyInfoModel!.alphabeticCode} )',
style: italicTextStyle.copyWith(
color: redColor,
),
),
],
),
const SizedBox(
height: 10,
),
RichText(
text: TextSpan(
text: 'Jumlah Tukaran : ',
style: regularTextStyle,
children: [
TextSpan(
text:
'${viewModel.currencyInfoModel!.alphabeticCode} ${viewModel.otherFunction.commaFormat(int.parse(viewModel.nilaiTukaran!))}',
style: boldTextStyle.copyWith(
color: redColor,
),
),
],
),
),
const SizedBox(
height: 10,
),
RichText(
text: TextSpan(
text: 'Konversi Tukaran : ',
style: regularTextStyle,
children: [
TextSpan(
text: '1 ${viewModel.currencyInfoModel!.alphabeticCode}',
style: boldTextStyle.copyWith(
color: redColor,
),
),
const TextSpan(
text: ' = ',
style: regularTextStyle,
),
TextSpan(
text:
'${viewModel.conversionResultModel!.conversionRate} ${viewModel.konversiInfoModel!.alphabeticCode}',
style: boldTextStyle.copyWith(
color: orangeColor,
),
),
],
),
),
const SizedBox(
height: 10,
),
Row(
children: [
const Text('Uang Konversi: ', style: regularTextStyle),
const SizedBox(
width: 3,
),
Image.asset(
'assets/flags/${viewModel.konversiInfoModel!.alphabeticCode!.toLowerCase()}.png',
width: 30,
height: 30,
),
const SizedBox(
width: 5,
),
Text(
viewModel.konversiInfoModel!.entity!,
style: boldTextStyle.copyWith(
color: orangeColor,
),
),
const SizedBox(
width: 3,
),
Text(
'( ${viewModel.konversiInfoModel!.alphabeticCode} )',
style: italicTextStyle.copyWith(
color: orangeColor,
),
),
],
),
const SizedBox(
height: 10,
),
RichText(
text: TextSpan(
text: 'Nilai Konversi : ',
style: regularTextStyle,
children: [
TextSpan(
text:
'${viewModel.konversiInfoModel!.alphabeticCode} ${viewModel.conversionResultModel!.conversionResult}',
style: boldTextStyle.copyWith(
color: orangeColor,
),
),
],
),
),
],
);
}
}

View File

@ -0,0 +1,61 @@
import '../../../../app/app.dialogs.dart';
import '../../../../app/app.logger.dart';
import '../../../../app/core/custom_base_view_model.dart';
import '../../../../models/all_info_model.dart';
import '../../../../models/conversion_result_model.dart';
class CustomCurrencyViewModel extends CustomBaseViewModel {
final log = getLogger('CustomCurrencyViewModel');
ConversionResultModel? conversionResultModel;
AllInfoModel? currencyInfoModel;
AllInfoModel? konversiInfoModel;
String? nilaiTukaran;
Future<void> init() async {}
myDialog() async {
final res = await dialogService.showCustomDialog(
variant: DialogType.konversiDialogView,
);
if (res!.confirmed) {
setBusy(true);
globalVar.backPressed = 'cantBack';
easyLoading.customLoading('Loading Konversi Tukaran ...');
log.i(res.data);
String currencyCountry = res.data['currencyCountry'];
String nilaiTukaran = res.data['nilaiTukaran'];
String konversiCountry = res.data['konversiCountry'];
this.nilaiTukaran = nilaiTukaran;
String api = '/pair/$currencyCountry/$konversiCountry/$nilaiTukaran';
try {
var response = await httpService.get(api);
log.i(response.data);
for (var item in globalVar.allInfoModel) {
if (item.alphabeticCode == currencyCountry) {
currencyInfoModel = item;
}
if (item.alphabeticCode == konversiCountry) {
konversiInfoModel = item;
}
}
log.i(currencyInfoModel!.entity);
log.i(konversiInfoModel!.entity);
conversionResultModel = ConversionResultModel.fromJson(response.data);
} catch (e) {
snackbarService.showSnackbar(message: e.toString());
log.e(e);
} finally {
setBusy(false);
globalVar.backPressed = 'backNormal';
easyLoading.dismissLoading();
notifyListeners();
}
}
}
}

View File

@ -0,0 +1,101 @@
import 'package:curreny_exchange/app/themes/app_colors.dart';
import 'package:curreny_exchange/ui/widgets/my_textformfield.dart';
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
import './konversi_dialog_view_model.dart';
class KonversiDialogView extends StatelessWidget {
// final bool isKonversi;
final DialogRequest? request;
final Function(DialogResponse)? completer;
const KonversiDialogView({
Key? key,
// this.isKonversi = false,
this.request,
this.completer,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return ViewModelBuilder<KonversiDialogViewModel>.reactive(
viewModelBuilder: () => KonversiDialogViewModel(),
onViewModelReady: (KonversiDialogViewModel model) async {
await model.init();
},
builder: (
BuildContext context,
KonversiDialogViewModel model,
Widget? child,
) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
child: Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: model.currencyWidget == null
? const Text('- Pilih Mata Uang -')
: model.currencyWidget!,
),
IconButton(
onPressed: () {
model.pilihNegara(true);
},
icon: const Icon(Icons.edit, color: mainColor),
),
],
),
const SizedBox(height: 10),
MyTextFormField(
hintText: model.hintText ?? 'Pilih Mata Uang Konversi',
keyboardType: TextInputType.number,
controller: model.nilaiTukaranController,
focusNode: model.nilaiTukaranFocusNode,
),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: model.konversiWidget == null
? const Text('- Pilih Mata Uang -')
: model.konversiWidget!),
IconButton(
onPressed: () {
model.pilihNegara(false);
},
icon: const Icon(Icons.edit, color: mainColor),
),
],
),
const SizedBox(height: 10),
Center(
child: ElevatedButton(
onPressed: () {
model.konversi(completer);
},
child: const Text('Konversi'),
),
),
],
),
),
);
},
);
}
}

View File

@ -0,0 +1,153 @@
import 'package:flutter/material.dart';
import 'package:stacked_services/stacked_services.dart';
import '../../../../../app/app.bottomsheets.dart';
import '../../../../../app/app.logger.dart';
import '../../../../../app/core/custom_base_view_model.dart';
import '../../../../../app/themes/app_colors.dart';
import '../../../../../app/themes/app_text.dart';
import '../../../../../models/all_info_model.dart';
class KonversiDialogViewModel extends CustomBaseViewModel {
final log = getLogger('KonversiDialogViewModel');
String? currencyCountry;
Widget? currencyWidget;
String? hintText;
String? konversiCountry;
Widget? konversiWidget;
TextEditingController nilaiTukaranController = TextEditingController();
FocusNode nilaiTukaranFocusNode = FocusNode();
Future<void> init() async {}
pilihNegara(bool bool) async {
// FocusScope.of(StackedService.navigatorKey!.currentContext!).unfocus();
final res = await bottomSheetService.showCustomSheet(
variant: BottomSheetType.pilihNegaraBottomSheetView,
data: {
'bool': bool,
'currencyCountry': bool == true ? currencyCountry : konversiCountry,
},
);
if (res!.confirmed == true) {
log.i('${res.data} ini res.data');
// if (res.data !=
AllInfoModel? allInfoModel = res.data;
if (bool == true) {
currencyCountry = allInfoModel!.alphabeticCode;
log.i(allInfoModel.entity);
hintText = 'Tukaran ${allInfoModel.alphabeticCode}';
currencyWidget = Row(
children: [
Image.asset(
'assets/flags/${allInfoModel.alphabeticCode!.toLowerCase()}.png',
width: 30,
),
const SizedBox(width: 15),
RichText(
text: TextSpan(
text: allInfoModel.entity,
style: regularTextStyle.copyWith(
color: greenColor,
),
children: [
TextSpan(
text: ' (${allInfoModel.alphabeticCode})',
style: italicTextStyle.copyWith(
color: greenColor,
),
),
],
)),
],
);
} else {
// log.i(allInfoModel!.entity);
konversiCountry = (res.data == 'all'
? 'Semua Mata Uang'
: allInfoModel!.alphabeticCode)!;
// hintText = 'Tukaran ${allInfoModel.alphabeticCode}';
konversiWidget = Row(
children: [
res.data == 'all'
? const Icon(
Icons.all_inclusive,
color: redColor,
)
: Image.asset(
'assets/flags/${allInfoModel!.alphabeticCode!.toLowerCase()}.png',
width: 30,
),
const SizedBox(width: 15),
RichText(
text: TextSpan(
text:
res.data == 'all' ? 'Semua Mata Uang' : allInfoModel!.entity,
style: regularTextStyle.copyWith(
color: redColor,
),
children: [
TextSpan(
text: res.data == 'all'
? '*'
: ' (${allInfoModel!.alphabeticCode})',
style: italicTextStyle.copyWith(
color: redColor,
),
),
],
)),
],
);
}
}
notifyListeners();
}
konversi(Function(DialogResponse p1)? completer) async {
if (currencyCountry == null) {
snackbarService.showSnackbar(
message: 'Pilih Mata Uang',
title: 'Pilih Negara',
);
pilihNegara(true);
return;
}
if (nilaiTukaranController.text == '') {
snackbarService.showSnackbar(
message: 'Masukkan Nilai Tukaran',
title: 'Nilai Tukaran',
);
// focus on textfield
FocusScope.of(StackedService.navigatorKey!.currentContext!)
.requestFocus(nilaiTukaranFocusNode);
return;
}
if (konversiCountry == null) {
snackbarService.showSnackbar(
message: 'Pilih Mata Uang Koversi',
title: 'Pilih Negara',
);
pilihNegara(false);
return;
}
// remove keyboard
FocusScope.of(StackedService.navigatorKey!.currentContext!).unfocus();
completer!(
DialogResponse(confirmed: true, data: {
'currencyCountry': currencyCountry,
'nilaiTukaran': nilaiTukaranController.text,
'konversiCountry': konversiCountry,
}),
);
}
}

View File

@ -0,0 +1,121 @@
import 'package:curreny_exchange/app/themes/app_colors.dart';
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
import '../../../../../../app/themes/app_text.dart';
import './pilih_negara_bottom_sheet_view_model.dart';
// class PilihNegaraData {
// final bool? isKonversi;
// final String? alphabeticCode;
// PilihNegaraData({
// this.isKonversi,
// this.alphabeticCode,
// });
// }
class PilihNegaraBottomSheetView extends StatelessWidget {
final SheetRequest? request;
final Function(SheetResponse)? completer;
const PilihNegaraBottomSheetView({
Key? key,
this.request,
this.completer,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return ViewModelBuilder<PilihNegaraBottomSheetViewModel>.reactive(
viewModelBuilder: () => PilihNegaraBottomSheetViewModel(),
onViewModelReady: (PilihNegaraBottomSheetViewModel model) async {
await model.init(request!.data);
},
builder: (
BuildContext context,
PilihNegaraBottomSheetViewModel model,
Widget? child,
) {
return Container(
padding: const EdgeInsets.all(15),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(15),
topRight: Radius.circular(15),
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Pilih Negara',
style: italicTextStyle.copyWith(
fontSize: 15,
fontWeight: FontWeight.bold,
),
),
Expanded(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
for (var item in model.globalVar.allInfoModel)
Card(
// creata a checkbox at the e
color: request!.data['currencyCountry'] ==
item.alphabeticCode
? mainColor
: Colors.white,
child: ListTile(
onTap: () {
// if (isKonversi) {
completer!(
SheetResponse(confirmed: true, data: item));
// }
},
leading: Image.asset(
'assets/flags/${item.alphabeticCode!.toLowerCase()}.png',
width: 30,
errorBuilder: (context, error, stackTrace) {
return const Icon(Icons.error);
},
),
title: Text(
item.entity!,
style: italicTextStyle.copyWith(
fontSize: 15,
fontWeight: FontWeight.bold,
color: request!.data['currencyCountry'] ==
item.alphabeticCode
? Colors.white
: Colors.black,
),
),
subtitle: Text(
item.currency!,
style: italicTextStyle.copyWith(
fontSize: 15,
// fontWeight: FontWeight.bold,
color: request!.data['currencyCountry'] ==
item.alphabeticCode
? Colors.white
: Colors.black,
),
),
),
)
],
),
),
),
],
),
);
},
);
}
}

View File

@ -0,0 +1,10 @@
import '../../../../../../app/app.logger.dart';
import '../../../../../../app/core/custom_base_view_model.dart';
class PilihNegaraBottomSheetViewModel extends CustomBaseViewModel {
final log = getLogger('PilihNegaraBottomSheetViewModel');
Future<void> init(data) async {
// log.i(globalVar.allInfoModel);
log.i(data['bool']);
}
}

View File

@ -0,0 +1,157 @@
import 'package:curreny_exchange/app/themes/app_colors.dart';
import 'package:curreny_exchange/app/themes/app_text.dart';
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import './today_currency_view_model.dart';
class TodayCurrencyView extends StatelessWidget {
const TodayCurrencyView({super.key});
@override
Widget build(BuildContext context) {
return ViewModelBuilder<TodayCurrencyViewModel>.reactive(
viewModelBuilder: () => TodayCurrencyViewModel(),
onViewModelReady: (TodayCurrencyViewModel model) async {
await model.init();
},
builder: (
BuildContext context,
TodayCurrencyViewModel model,
Widget? child,
) {
return WillPopScope(
onWillPop: () async {
// model.navigationService.back();
if (model.globalVar.backPressed == 'backNormal') {
model.quitApp(context);
}
return false;
},
child: Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
RichText(
text: TextSpan(
text: 'Terakhir diperbarui : ',
style: regularTextStyle,
children: [
TextSpan(
text: model.otherFunction.timeStampConverter(model
.globalVar.myResponseModel!.timeLastUpdateUnix!),
style: boldTextStyle.copyWith(
color: greenColor,
),
),
],
),
),
const SizedBox(
height: 10,
),
RichText(
text: TextSpan(
text: 'Update berikutnya : ',
style: regularTextStyle,
children: [
TextSpan(
text: model.otherFunction.timeStampConverter(model
.globalVar.myResponseModel!.timeNextUpdateUnix!),
style: boldTextStyle.copyWith(
color: mainColor,
),
),
],
),
),
const SizedBox(
height: 10,
),
RichText(
text: TextSpan(
text: 'Tukaran : ',
style: regularTextStyle,
children: [
TextSpan(
text: 'Rp . ${model.globalVar.allCurrencyModel!.iDR}',
style: boldTextStyle.copyWith(
color: redColor,
),
),
],
),
),
const SizedBox(
height: 10,
),
Expanded(
child: model.globalVar.jsonAllCurrency != null
? SingleChildScrollView(
child: Column(
children: model.globalVar.allCurrencyModel!
.toJson()
.entries
.map(
(e) {
// make e.key to be uppercase
String key = e.key.toString().toUpperCase();
String lowerKey =
e.key.toString().toLowerCase();
var row = model.globalVar.jsonAllCurrency
.firstWhere(
(currency) =>
currency['AlphabeticCode'] == key,
orElse: () => null);
// Map<String, dynamic> rowMap = row;
if (row != null) {
// model.log.i(row['Currency']);
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 10),
// add icon to card
child: Card(
child: ListTile(
leading: Image.asset(
'assets/flags/$lowerKey.png',
width: 50,
height: 50,
errorBuilder:
(context, error, stackTrace) {
return const Icon(Icons.error);
},
),
title: Text(
row['Entity'],
style: boldTextStyle,
),
subtitle: Text(
row['Currency'],
style: regularTextStyle,
),
trailing: Text(
e.value.toString(),
style: boldTextStyle.copyWith(
color: redColor,
),
),
)),
);
} else {
return const SizedBox();
}
},
).toList(),
),
)
: const Center(child: CircularProgressIndicator()),
),
],
),
),
);
},
);
}
}

View File

@ -0,0 +1,8 @@
import '../../../../app/app.logger.dart';
import '../../../../app/core/custom_base_view_model.dart';
class TodayCurrencyViewModel extends CustomBaseViewModel {
final log = getLogger('TodayCurrencyViewModel');
// dynamic jsonAllCurrency;
Future<void> init() async {}
}

View File

@ -0,0 +1,29 @@
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import './loading_screen_view_model.dart';
class LoadingScreenView extends StatelessWidget {
const LoadingScreenView({super.key});
@override
Widget build(BuildContext context) {
return ViewModelBuilder<LoadingScreenViewModel>.reactive(
viewModelBuilder: () => LoadingScreenViewModel(),
onViewModelReady: (LoadingScreenViewModel model) async {
await model.init();
},
builder: (
BuildContext context,
LoadingScreenViewModel model,
Widget? child,
) {
return const Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
},
);
}
}

View File

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

View File

@ -0,0 +1,62 @@
import 'package:curreny_exchange/app/themes/app_text.dart';
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import './splash_screen_view_model.dart';
class SplashScreenView extends StatelessWidget {
const SplashScreenView({super.key});
@override
Widget build(BuildContext context) {
return ViewModelBuilder<SplashScreenViewModel>.reactive(
viewModelBuilder: () => SplashScreenViewModel(),
onViewModelReady: (SplashScreenViewModel model) async {
await model.init();
},
builder: (
BuildContext context,
SplashScreenViewModel model,
Widget? child,
) {
return WillPopScope(
onWillPop: () async {
return false;
},
child: Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Expanded(child: SizedBox(height: 20)),
Center(
child: Image.asset(
'assets/exchange.png',
width: 200,
height: 200,
),
),
const SizedBox(height: 20),
Text(
"Aplikasi\nTukaran Mata Wang",
style: italicTextStyle.copyWith(
fontSize: 20,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
const Expanded(child: SizedBox(height: 20)),
Text(
"Created by : Marlina & Andi Wafiah",
style: regularTextStyle.copyWith(fontSize: 12),
textAlign: TextAlign.center,
),
const SizedBox(height: 20),
],
),
),
);
},
);
}
}

View File

@ -0,0 +1,52 @@
import 'dart:convert';
import 'package:flutter/services.dart';
import '../../../app/app.logger.dart';
import '../../../app/app.router.dart';
import '../../../app/core/custom_base_view_model.dart';
import '../../../models/all_currency.dart';
import '../../../models/all_info_model.dart';
import '../../../models/my_response.dart';
class SplashScreenViewModel extends CustomBaseViewModel {
final log = getLogger('SplashScreenViewModel');
MyResponseModel? myResponseModel;
Future<void> init() async {
globalVar.backPressed = 'cantBack';
getData();
}
getData() async {
setBusy(true);
try {
var response = await httpService.get('/latest/IDR');
// log.i(response.data);
myResponseModel = MyResponseModel.fromJson(response.data);
globalVar.myResponseModel = myResponseModel;
// log.i(myResponseModel!.conversionRates);
AllCurrencyModel allCurrencyModel =
AllCurrencyModel.fromJson(myResponseModel!.conversionRates!);
globalVar.allCurrencyModel = allCurrencyModel;
final String jsonData =
await rootBundle.loadString('assets/codes-all.json');
globalVar.jsonAllCurrency = jsonDecode(jsonData);
// log.i(globalVar.jsonAllCurrency);
// conver jsonData to Map<String, dynamic>
for (var item in globalVar.jsonAllCurrency) {
// log.i(item);
globalVar.allInfoModel.add(AllInfoModel.fromJson(item));
}
Future.delayed(const Duration(seconds: 1), () {
navigationService.replaceWith(Routes.appIndexTrackingView);
});
} catch (e) {
snackbarService.showSnackbar(message: e.toString());
log.e(e);
} finally {
setBusy(false);
globalVar.backPressed = 'backNormal';
}
}
}

View File

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

View File

@ -0,0 +1,91 @@
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.readOnly = false,
this.onTap,
this.keyboardType = TextInputType.text,
this.initialValue,
this.enabled = true,
this.maxLength,
}) : 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 bool readOnly;
final VoidCallback? onTap;
final TextInputType keyboardType;
final String? initialValue;
final bool enabled;
final int? maxLength;
@override
Widget build(BuildContext context) {
return TextFormField(
maxLength: maxLength,
enabled: enabled,
initialValue: initialValue,
onEditingComplete: onEditingComplete,
maxLines: maxLines,
controller: controller,
focusNode: focusNode,
obscureText: obscureText ?? false,
readOnly: readOnly,
onTap: onTap,
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,27 @@
import 'package:flutter/material.dart';
class MyWhiteContainer extends StatelessWidget {
final Widget? child;
final double? height;
const MyWhiteContainer({
Key? key,
this.child,
this.height,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
height: height,
padding: const EdgeInsets.symmetric(
vertical: 10,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
),
child: child,
);
}
}