first commit
This commit is contained in:
25
lib/app/app.bottomsheets.dart
Normal file
25
lib/app/app.bottomsheets.dart
Normal 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
48
lib/app/app.dart
Normal 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
25
lib/app/app.dialogs.dart
Normal 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
39
lib/app/app.locator.dart
Normal 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
159
lib/app/app.logger.dart
Normal 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
270
lib/app/app.router.dart
Normal 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);
|
||||
}
|
||||
}
|
57
lib/app/core/custom_base_view_model.dart
Executable file
57
lib/app/core/custom_base_view_model.dart
Executable 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
30
lib/app/themes/app_colors.dart
Executable 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);
|
44
lib/app/themes/app_text.dart
Normal file
44
lib/app/themes/app_text.dart
Normal 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
124
lib/app/themes/app_theme.dart
Executable 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
39
lib/main.dart
Normal 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();
|
||||
}
|
660
lib/models/all_currency.dart
Normal file
660
lib/models/all_currency.dart
Normal 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;
|
||||
}
|
||||
}
|
26
lib/models/all_info_model.dart
Normal file
26
lib/models/all_info_model.dart
Normal 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'];
|
||||
}
|
||||
}
|
58
lib/models/conversion_result_model.dart
Normal file
58
lib/models/conversion_result_model.dart
Normal 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;
|
||||
}
|
||||
}
|
48
lib/models/my_response.dart
Normal file
48
lib/models/my_response.dart
Normal 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;
|
||||
}
|
||||
}
|
12
lib/services/global_var.dart
Normal file
12
lib/services/global_var.dart
Normal 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 = [];
|
||||
}
|
34
lib/services/http_services.dart
Normal file
34
lib/services/http_services.dart
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
39
lib/services/my_easyloading.dart
Normal file
39
lib/services/my_easyloading.dart
Normal 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);
|
||||
}
|
||||
}
|
25
lib/services/other_function.dart
Normal file
25
lib/services/other_function.dart
Normal 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;
|
||||
}
|
||||
}
|
91
lib/ui/views/app_index_tracking/app_index_tracking_view.dart
Normal file
91
lib/ui/views/app_index_tracking/app_index_tracking_view.dart
Normal 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),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -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,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -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']);
|
||||
}
|
||||
}
|
@ -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()),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -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 {}
|
||||
}
|
29
lib/ui/views/loading_screen/loading_screen_view.dart
Normal file
29
lib/ui/views/loading_screen/loading_screen_view.dart
Normal 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(),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
import '../../../app/core/custom_base_view_model.dart';
|
||||
|
||||
class LoadingScreenViewModel extends CustomBaseViewModel {
|
||||
Future<void> init() async {}
|
||||
}
|
62
lib/ui/views/splash_screen/splash_screen_view.dart
Normal file
62
lib/ui/views/splash_screen/splash_screen_view.dart
Normal 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),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
52
lib/ui/views/splash_screen/splash_screen_view_model.dart
Normal file
52
lib/ui/views/splash_screen/splash_screen_view_model.dart
Normal 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';
|
||||
}
|
||||
}
|
||||
}
|
34
lib/ui/widgets/my_button.dart
Normal file
34
lib/ui/widgets/my_button.dart
Normal 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,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
91
lib/ui/widgets/my_textformfield.dart
Normal file
91
lib/ui/widgets/my_textformfield.dart
Normal 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,
|
||||
);
|
||||
}
|
||||
}
|
27
lib/ui/widgets/my_white_container.dart
Normal file
27
lib/ui/widgets/my_white_container.dart
Normal 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,
|
||||
);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user