commit 053ab5a6ac01018c6267f1916e5d992311c1c088 Author: kicap Date: Sat Aug 3 12:54:13 2024 +0800 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..50e29da --- /dev/null +++ b/.gitignore @@ -0,0 +1,54 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release + +*.env +.env + +android/ +ios/ +linux/ +macos/ +windows/ +tests/ diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..e94891d --- /dev/null +++ b/.metadata @@ -0,0 +1,45 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled. + +version: + revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 + channel: stable + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 + base_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 + - platform: android + create_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 + base_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 + - platform: ios + create_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 + base_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 + - platform: linux + create_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 + base_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 + - platform: macos + create_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 + base_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 + - platform: web + create_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 + base_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 + - platform: windows + create_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 + base_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/README.md b/README.md new file mode 100644 index 0000000..23bc751 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# Traffic Light Disfunctio Monitoring (Flutter) + +### traffic light disfunction monitoring app for displaying the data + +![alt text](image.png) \ No newline at end of file diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..61b6c4d --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 0000000..ced6f6f Binary files /dev/null and b/assets/logo.png differ diff --git a/image.png b/image.png new file mode 100644 index 0000000..c6bafa3 Binary files /dev/null and b/image.png differ diff --git a/lib/app/app.dart b/lib/app/app.dart new file mode 100644 index 0000000..3850cce --- /dev/null +++ b/lib/app/app.dart @@ -0,0 +1,50 @@ +import 'package:stacked_services/stacked_services.dart'; +import 'package:stacked/stacked_annotations.dart'; + +import '../services/http_services.dart'; +import '../services/my_easyloading.dart'; +import '../services/my_socket_io_client.dart'; +import '../services/other_function.dart'; +import '../ui/views/splash_screen/splash_screen_view.dart'; +import '../ui/views/the_index/profil/profil_view.dart'; +import '../ui/views/the_index/status_lampu/status_lampu_view.dart'; +import '../ui/views/the_index/the_index_view.dart'; + +@StackedApp( + routes: [ + MaterialRoute(page: SplashScreenView, initial: true), + MaterialRoute( + page: TheIndexView, + children: [ + MaterialRoute(page: StatusLampuView), + MaterialRoute(page: ProfilView), + ], + ), + ], + // dialogs: [ + // StackedDialog(classType: ScanRfidDialogView), + // StackedDialog(classType: TopUpSaldoView), + // StackedDialog(classType: EditTempatSewaDialogView), + // StackedDialog(classType: EditPenyewaDialogView), + // StackedDialog(classType: DaftarPenyewaView), + // StackedDialog(classType: AddTempatSewaView), + // ], + // bottomsheets: [ + // StackedBottomsheet(classType: DetailLogHistoryView), + // ], + dependencies: [ + LazySingleton(classType: NavigationService), + LazySingleton(classType: DialogService), + LazySingleton(classType: SnackbarService), + LazySingleton(classType: BottomSheetService), + // + + LazySingleton(classType: MyEasyLoading), + LazySingleton(classType: MyHttpServices), + LazySingleton(classType: OtherFunction), + LazySingleton(classType: MySocketIoClient), + // LazySingleton(classType: RetributorIndexView), + ], + logger: StackedLogger(), +) +class App {} diff --git a/lib/app/app.locator.dart b/lib/app/app.locator.dart new file mode 100644 index 0000000..8ce5296 --- /dev/null +++ b/lib/app/app.locator.dart @@ -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/http_services.dart'; +import '../services/my_easyloading.dart'; +import '../services/my_socket_io_client.dart'; +import '../services/other_function.dart'; + +final locator = StackedLocator.instance; + +Future 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(() => MySocketIoClient()); +} diff --git a/lib/app/app.logger.dart b/lib/app/app.logger.dart new file mode 100644 index 0000000..26ab3f9 --- /dev/null +++ b/lib/app/app.logger.dart @@ -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 exludeLogsFromClasses; + final String? showOnlyClass; + + SimpleLogPrinter( + this.className, { + this.printCallingFunctionName = true, + this.printCallStack = false, + this.exludeLogsFromClasses = const [], + this.showOnlyClass, + }); + + @override + List 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 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 _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 stackTraces, List 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 keywords) { + final formattedKeywordsAsRegex = RegExp(keywords.join('.*')); + return stackTrace.contains(formattedKeywordsAsRegex); + } +} + +final stackTraceRegex = RegExp(r'#[0-9]+[\s]+(.+) \(([^\s]+)\)'); + +List? _formatStackTrace(StackTrace stackTrace, int methodCount) { + var lines = stackTrace.toString().split('\n'); + + var formatted = []; + 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('', '()')); + 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 exludeLogsFromClasses = const [], + String? showOnlyClass, +}) { + return Logger( + printer: SimpleLogPrinter( + className, + printCallingFunctionName: printCallingFunctionName, + printCallStack: printCallstack, + showOnlyClass: showOnlyClass, + exludeLogsFromClasses: exludeLogsFromClasses, + ), + output: MultiOutput([ + if (!kReleaseMode) ConsoleOutput(), + ]), + ); +} diff --git a/lib/app/app.router.dart b/lib/app/app.router.dart new file mode 100644 index 0000000..304e4a8 --- /dev/null +++ b/lib/app/app.router.dart @@ -0,0 +1,222 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ************************************************************************** +// StackedNavigatorGenerator +// ************************************************************************** + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'package:flutter/material.dart' as _i4; +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart' as _i1; +import 'package:stacked_services/stacked_services.dart' as _i7; +import 'package:traffic_light/ui/views/splash_screen/splash_screen_view.dart' + as _i2; +import 'package:traffic_light/ui/views/the_index/profil/profil_view.dart' + as _i6; +import 'package:traffic_light/ui/views/the_index/status_lampu/status_lampu_view.dart' + as _i5; +import 'package:traffic_light/ui/views/the_index/the_index_view.dart' as _i3; + +class Routes { + static const splashScreenView = '/'; + + static const theIndexView = '/the-index-view'; + + static const all = { + splashScreenView, + theIndexView, + }; +} + +class StackedRouter extends _i1.RouterBase { + final _routes = <_i1.RouteDef>[ + _i1.RouteDef( + Routes.splashScreenView, + page: _i2.SplashScreenView, + ), + _i1.RouteDef( + Routes.theIndexView, + page: _i3.TheIndexView, + ), + ]; + + final _pagesMap = { + _i2.SplashScreenView: (data) { + return _i4.MaterialPageRoute( + builder: (context) => const _i2.SplashScreenView(), + settings: data, + ); + }, + _i3.TheIndexView: (data) { + return _i4.MaterialPageRoute( + builder: (context) => const _i3.TheIndexView(), + settings: data, + ); + }, + }; + + @override + List<_i1.RouteDef> get routes => _routes; + + @override + Map get pagesMap => _pagesMap; +} + +class TheIndexViewRoutes { + static const statusLampuView = 'status-lampu-view'; + + static const profilView = 'profil-view'; + + static const all = { + statusLampuView, + profilView, + }; +} + +class TheIndexViewRouter extends _i1.RouterBase { + final _routes = <_i1.RouteDef>[ + _i1.RouteDef( + TheIndexViewRoutes.statusLampuView, + page: _i5.StatusLampuView, + ), + _i1.RouteDef( + TheIndexViewRoutes.profilView, + page: _i6.ProfilView, + ), + ]; + + final _pagesMap = { + _i5.StatusLampuView: (data) { + return _i4.MaterialPageRoute( + builder: (context) => const _i5.StatusLampuView(), + settings: data, + ); + }, + _i6.ProfilView: (data) { + return _i4.MaterialPageRoute( + builder: (context) => const _i6.ProfilView(), + settings: data, + ); + }, + }; + + @override + List<_i1.RouteDef> get routes => _routes; + + @override + Map get pagesMap => _pagesMap; +} + +extension NavigatorStateExtension on _i7.NavigationService { + Future navigateToSplashScreenView([ + int? routerId, + bool preventDuplicates = true, + Map? parameters, + Widget Function(BuildContext, Animation, Animation, Widget)? + transition, + ]) async { + return navigateTo(Routes.splashScreenView, + id: routerId, + preventDuplicates: preventDuplicates, + parameters: parameters, + transition: transition); + } + + Future navigateToTheIndexView([ + int? routerId, + bool preventDuplicates = true, + Map? parameters, + Widget Function(BuildContext, Animation, Animation, Widget)? + transition, + ]) async { + return navigateTo(Routes.theIndexView, + id: routerId, + preventDuplicates: preventDuplicates, + parameters: parameters, + transition: transition); + } + + Future navigateToNestedStatusLampuViewInTheIndexViewRouter([ + int? routerId, + bool preventDuplicates = true, + Map? parameters, + Widget Function(BuildContext, Animation, Animation, Widget)? + transition, + ]) async { + return navigateTo(TheIndexViewRoutes.statusLampuView, + id: routerId, + preventDuplicates: preventDuplicates, + parameters: parameters, + transition: transition); + } + + Future navigateToNestedProfilViewInTheIndexViewRouter([ + int? routerId, + bool preventDuplicates = true, + Map? parameters, + Widget Function(BuildContext, Animation, Animation, Widget)? + transition, + ]) async { + return navigateTo(TheIndexViewRoutes.profilView, + id: routerId, + preventDuplicates: preventDuplicates, + parameters: parameters, + transition: transition); + } + + Future replaceWithSplashScreenView([ + int? routerId, + bool preventDuplicates = true, + Map? parameters, + Widget Function(BuildContext, Animation, Animation, Widget)? + transition, + ]) async { + return replaceWith(Routes.splashScreenView, + id: routerId, + preventDuplicates: preventDuplicates, + parameters: parameters, + transition: transition); + } + + Future replaceWithTheIndexView([ + int? routerId, + bool preventDuplicates = true, + Map? parameters, + Widget Function(BuildContext, Animation, Animation, Widget)? + transition, + ]) async { + return replaceWith(Routes.theIndexView, + id: routerId, + preventDuplicates: preventDuplicates, + parameters: parameters, + transition: transition); + } + + Future replaceWithNestedStatusLampuViewInTheIndexViewRouter([ + int? routerId, + bool preventDuplicates = true, + Map? parameters, + Widget Function(BuildContext, Animation, Animation, Widget)? + transition, + ]) async { + return replaceWith(TheIndexViewRoutes.statusLampuView, + id: routerId, + preventDuplicates: preventDuplicates, + parameters: parameters, + transition: transition); + } + + Future replaceWithNestedProfilViewInTheIndexViewRouter([ + int? routerId, + bool preventDuplicates = true, + Map? parameters, + Widget Function(BuildContext, Animation, Animation, Widget)? + transition, + ]) async { + return replaceWith(TheIndexViewRoutes.profilView, + id: routerId, + preventDuplicates: preventDuplicates, + parameters: parameters, + transition: transition); + } +} diff --git a/lib/app/core/custom_base_view_model.dart b/lib/app/core/custom_base_view_model.dart new file mode 100755 index 0000000..ad91ed0 --- /dev/null +++ b/lib/app/core/custom_base_view_model.dart @@ -0,0 +1,29 @@ +// import 'package:shared_preferences/shared_preferences.dart'; +import 'package:stacked/stacked.dart'; +import 'package:stacked_services/stacked_services.dart'; + +import '../../services/http_services.dart'; +import '../../services/my_easyloading.dart'; +import '../../services/my_socket_io_client.dart'; +import '../../services/other_function.dart'; +// import '../../services/shared_prefs.dart'; +import '../app.locator.dart'; + +class CustomBaseViewModel extends BaseViewModel { + final dialogService = locator(); + final navigationService = locator(); + final bottomSheetService = locator(); + final snackbarService = locator(); + // final mySharedPrefs = locator(); + final otherFunction = locator(); + final socketIoClient = locator(); + final httpService = locator(); + final easyLoading = locator(); + + String? no, light, pln; + double? rms; + + void back() { + navigationService.back(); + } +} diff --git a/lib/app/themes/app_colors.dart b/lib/app/themes/app_colors.dart new file mode 100755 index 0000000..a0554fe --- /dev/null +++ b/lib/app/themes/app_colors.dart @@ -0,0 +1,30 @@ +import 'dart:ui'; + +const Color mainColor = Color.fromARGB(255, 6, 164, 238); +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); diff --git a/lib/app/themes/app_text.dart b/lib/app/themes/app_text.dart new file mode 100644 index 0000000..092d154 --- /dev/null +++ b/lib/app/themes/app_text.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; + +import 'app_colors.dart'; + +const regularTextStyle = TextStyle( + fontFamily: 'Arial', + fontSize: 14, + fontWeight: FontWeight.w400, + color: fontColor); + +const italicTextStyle = TextStyle( + fontFamily: 'Arial', + fontSize: 14, + color: fontColor, + fontStyle: FontStyle.italic, +); + +const mediumTextStyle = TextStyle( + fontFamily: 'Arial', + fontSize: 14, + fontWeight: FontWeight.w500, + color: fontColor, +); + +const semiBoldTextStyle = TextStyle( + fontFamily: 'Arial', + fontSize: 14, + fontWeight: FontWeight.w600, + color: fontColor, +); + +const boldTextStyle = TextStyle( + fontFamily: 'Arial', + fontSize: 14, + fontWeight: FontWeight.w700, + color: fontColor, +); + +const extraBoldTextStyle = TextStyle( + fontFamily: 'Arial', + fontSize: 14, + fontWeight: FontWeight.w800, + color: fontColor, +); diff --git a/lib/app/themes/app_theme.dart b/lib/app/themes/app_theme.dart new file mode 100755 index 0000000..eefa661 --- /dev/null +++ b/lib/app/themes/app_theme.dart @@ -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), +); diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..44ee3de --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,53 @@ +import 'dart:io'; + +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 'package:traffic_light/app/app.router.dart'; + +import 'app/app.locator.dart'; +import 'app/themes/app_theme.dart'; + +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + HttpOverrides.global = MyHttpOverrides(); + // await initializeDateFormatting('id_ID'); + await dotenv.load(fileName: ".env"); + await setupAllLocator(); + runApp(const MyApp()); + 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: 'Sistem Retribusi Pasar Enerekang', + theme: appTheme, + debugShowCheckedModeBanner: false, + navigatorKey: StackedService.navigatorKey, + onGenerateRoute: StackedRouter().onGenerateRoute, + builder: EasyLoading.init(), + ); + } +} + +Future setupAllLocator() async { + await setupLocator(); + // setupDialogUi(); + // setupBottomSheetUi(); + // setupSnackbarUi(); +} + +class MyHttpOverrides extends HttpOverrides { + @override + HttpClient createHttpClient(SecurityContext? context) { + return super.createHttpClient(context) + ..badCertificateCallback = + (X509Certificate cert, String host, int port) => true; + } +} diff --git a/lib/model/my_response_model.dart b/lib/model/my_response_model.dart new file mode 100644 index 0000000..5d4f78f --- /dev/null +++ b/lib/model/my_response_model.dart @@ -0,0 +1,21 @@ +class MyResponseModel { + String? message; + dynamic data; + bool? status; + + MyResponseModel({this.message, this.data, this.status}); + + MyResponseModel.fromJson(Map json) { + message = json['message']; + data = json['data']; + status = json['status']; + } + + Map toJson() { + final Map data = {}; + data['message'] = message; + data['data'] = this.data; + data['status'] = status; + return data; + } +} diff --git a/lib/services/http_services.dart b/lib/services/http_services.dart new file mode 100644 index 0000000..a0cf10d --- /dev/null +++ b/lib/services/http_services.dart @@ -0,0 +1,113 @@ +import 'package:dio/dio.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:stacked_services/stacked_services.dart'; + +import '../app/app.locator.dart'; +import '../app/app.logger.dart'; + +class MyHttpServices { + final _log = getLogger('MyHttpServices'); + final _snackbarService = locator(); + final _options = BaseOptions( + baseUrl: dotenv.env['api_url']!, + connectTimeout: const Duration(seconds: 120), + receiveTimeout: const Duration(seconds: 120), + ); + + late Dio _dio; + + MyHttpServices() { + _dio = Dio(_options); + } + + Future get(String path) async { + try { + return await _dio.get(path); + } on DioException catch (e) { + String response = e.response != null + ? e.response!.data['message'].toString() + : e.toString(); + _log.e('ini errornya: $response'); + _snackbarService.showSnackbar( + message: response, + title: 'Error', + duration: const Duration(milliseconds: 1000), + ); + rethrow; + } + } + + Future postWithFormData(String path, FormData formData) async { + try { + return await _dio.post(path, data: formData); + } on DioException catch (e) { + _log.d(e.response); + String response = e.response != null + ? e.response!.data['message'].toString() + : e.toString(); + // _log.e('ini errornya: $response'); + _snackbarService.showSnackbar( + message: response, + title: 'Error', + duration: const Duration(milliseconds: 1000), + ); + rethrow; + } + } + + // putWithFormData + Future putWithFormData(String path, FormData formData) async { + try { + return await _dio.put(path, data: formData); + } on DioException catch (e) { + String response = e.response != null + ? e.response!.data['message'].toString() + : e.toString(); + _log.e('ini errornya: $response'); + _snackbarService.showSnackbar( + message: response, + title: 'Error', + duration: const Duration(milliseconds: 1000), + ); + rethrow; + } + } + + delete(String path) async { + try { + await _dio.delete(path); + } on DioException catch (e) { + String response = e.response != null + ? e.response!.data['message'].toString() + : e.toString(); + _log.e('ini errornya: $response'); + _snackbarService.showSnackbar( + message: response, + title: 'Error', + duration: const Duration(milliseconds: 1000), + ); + rethrow; + } + } + + // // delete + // Future delete(String path, FormData data) async { + // try { + // // log.i('path: $path'); + // return await _dio.delete( + // path, + // data: data, + // // encoding: Encoding.getByName('utf-8'), + // options: Options( + // headers: { + // 'Content-Type': 'application/x-www-form-urlencoded', + // }, + // ), + // ); + // } on DioError catch (e) { + // log.e(e.message); + // log.e(e.response); + // rethrow; + // } + // } +} diff --git a/lib/services/my_easyloading.dart b/lib/services/my_easyloading.dart new file mode 100644 index 0000000..98198c9 --- /dev/null +++ b/lib/services/my_easyloading.dart @@ -0,0 +1,39 @@ +import 'package:flutter_easyloading/flutter_easyloading.dart'; + +class MyEasyLoading { + showLoading() { + EasyLoading.show( + status: 'loading...', + maskType: EasyLoadingMaskType.black, + dismissOnTap: false, + ); + } + + dismiss() { + 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); + } +} diff --git a/lib/services/my_socket_io_client.dart b/lib/services/my_socket_io_client.dart new file mode 100644 index 0000000..8689244 --- /dev/null +++ b/lib/services/my_socket_io_client.dart @@ -0,0 +1,48 @@ +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:socket_io_client/socket_io_client.dart'; + +import '../app/app.logger.dart'; + +class MySocketIoClient { + final log = getLogger('MySocketIoClient'); + final String _url = dotenv.env['url']!; + static final MySocketIoClient _instance = MySocketIoClient._internal(); + factory MySocketIoClient() => _instance; + MySocketIoClient._internal(); + + late Socket _socket; + Socket get socket => _socket; + + Future init() async { + try { + _socket = io(_url, { + 'transports': ['websocket'], + 'autoConnect': false, + }); + _socket.connect(); + log.i('socket connected'); + } catch (e) { + log.e('error : $e'); + } + } + + Future emit(String event, dynamic data) async { + _socket.emit(event, data); + } + + Future on(String event, Function(dynamic) callback) async { + _socket.on(event, callback); + } + + Future off(String event) async { + _socket.off(event); + } + + Future disconnect() async { + _socket.disconnect(); + } + + Future connect() async { + _socket.connect(); + } +} diff --git a/lib/services/other_function.dart b/lib/services/other_function.dart new file mode 100644 index 0000000..0822750 --- /dev/null +++ b/lib/services/other_function.dart @@ -0,0 +1,137 @@ +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 changeMonth(String month) { + switch (month) { + case 'Januari': + return '01'; + case 'Februari': + return '02'; + case 'Maret': + return '03'; + case 'April': + return '04'; + case 'Mei': + return '05'; + case 'Juni': + return '06'; + case 'Juli': + return '07'; + case 'Agustus': + return '08'; + case 'September': + return '09'; + case 'Oktober': + return '10'; + case 'November': + return '11'; + case 'Desember': + return '12'; + default: + return ''; + } + } + + String changeMonthYear(String s) { + // get the last 2 digits + String month = s.substring(s.length - 2); + // get the first 4 digits + String year = s.substring(0, 4); + // return the month and year + switch (month) { + case '01': + return 'Januari $year'; + case '02': + return 'Februari $year'; + case '03': + return 'Maret $year'; + case '04': + return 'April $year'; + case '05': + return 'Mei $year'; + case '06': + return 'Juni $year'; + case '07': + return 'Juli $year'; + case '08': + return 'Agustus $year'; + case '09': + return 'September $year'; + case '10': + return 'Oktober $year'; + case '11': + return 'November $year'; + case '12': + return 'Desember $year'; + default: + return ''; + } + } + + String getDayOfWeek(String date) { + DateTime dateTime = DateTime.parse(date); + List daysOfWeek = [ + 'Senin', + 'Selasa', + 'Rabu', + 'Kamis', + 'Jumat', + 'Sabtu', + 'Minggu' + ]; + return daysOfWeek[dateTime.weekday - 1]; + } + + String formatDateString(String dateString) { + // Remove the "T" and replace it with " | " + String formattedString = dateString.replaceAll('T', '\n'); + + // Remove the ".000Z" + formattedString = formattedString.replaceAll('.000Z', ''); + + // Parse the input string to DateTime object + DateTime dateTime = DateTime.parse(dateString); + + // Get the day of the week in Indonesian + String dayOfWeek = DateFormat.EEEE('id_ID').format(dateTime); + + // Add the day of the week to the formatted string + formattedString = '$formattedString\n$dayOfWeek'; + + return formattedString; + } + + String formatDateString2(String dateString) { + // Remove the "T" and replace it with " | " + String formattedString = dateString.replaceAll('T', ' | '); + + // Remove the ".000Z" + formattedString = formattedString.replaceAll('.000Z', ' | '); + + // Parse the input string to DateTime object + DateTime dateTime = DateTime.parse(dateString); + + // Get the day of the week in Indonesian + String dayOfWeek = DateFormat.EEEE('id_ID').format(dateTime); + + // Add the day of the week to the formatted string + formattedString = '$formattedString | $dayOfWeek'; + + return formattedString; + } +} diff --git a/lib/ui/views/splash_screen/splash_screen_view.dart b/lib/ui/views/splash_screen/splash_screen_view.dart new file mode 100644 index 0000000..0fbc48b --- /dev/null +++ b/lib/ui/views/splash_screen/splash_screen_view.dart @@ -0,0 +1,69 @@ +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; + +import '../../../app/themes/app_text.dart'; +import './splash_screen_view_model.dart'; + +class SplashScreenView extends StatelessWidget { + const SplashScreenView({super.key}); + + @override + Widget build(BuildContext context) { + return ViewModelBuilder.nonReactive( + viewModelBuilder: () => SplashScreenViewModel(), + onViewModelReady: (SplashScreenViewModel model) async { + await model.init(); + }, + builder: ( + BuildContext context, + SplashScreenViewModel model, + Widget? child, + ) { + return Scaffold( + // backgroundColor: mainColor, + body: Column( + children: [ + const SizedBox(), + Expanded( + child: Center( + // show the logo.png + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Image( + image: AssetImage("assets/logo.png"), + width: 200, + height: 200, + ), + const SizedBox(height: 10), + Text( + "Disfungsi Traffic Light", + style: boldTextStyle.copyWith( + fontSize: 25, + ), + ) + ], + ), + ), + ), + Text( + "LUCKY PRASETIO AY", + textAlign: TextAlign.center, + style: boldTextStyle.copyWith( + fontSize: 15, + ), + ), + const SizedBox(height: 15), + const Text( + "217 280 111", + textAlign: TextAlign.center, + style: italicTextStyle, + ), + const SizedBox(height: 15), + ], + ), + ); + }, + ); + } +} diff --git a/lib/ui/views/splash_screen/splash_screen_view_model.dart b/lib/ui/views/splash_screen/splash_screen_view_model.dart new file mode 100644 index 0000000..af191ca --- /dev/null +++ b/lib/ui/views/splash_screen/splash_screen_view_model.dart @@ -0,0 +1,15 @@ +import '../../../app/app.logger.dart'; +import '../../../app/app.router.dart'; +import '../../../app/core/custom_base_view_model.dart'; + +class SplashScreenViewModel extends CustomBaseViewModel { + final log = getLogger('SplashScreenViewModel'); + Future init() async { + await Future.delayed(const Duration(seconds: 2)); + // navigate to login page + // ignore: use_build_context_synchronously + socketIoClient.init(); + + navigationService.replaceWith(Routes.theIndexView); + } +} diff --git a/lib/ui/views/the_index/profil/profil_view.dart b/lib/ui/views/the_index/profil/profil_view.dart new file mode 100644 index 0000000..e94543a --- /dev/null +++ b/lib/ui/views/the_index/profil/profil_view.dart @@ -0,0 +1,277 @@ +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; + +import './profil_view_model.dart'; + +class ProfilView extends StatelessWidget { + const ProfilView({super.key}); + + @override + Widget build(BuildContext context) { + return ViewModelBuilder.reactive( + viewModelBuilder: () => ProfilViewModel(), + onViewModelReady: (ProfilViewModel model) async { + await model.init(); + }, + builder: ( + BuildContext context, + ProfilViewModel model, + Widget? child, + ) { + return Column( + children: [ + SizedBox( + height: MediaQuery.of(context).size.height * 0.2, + ), + const Image( + image: AssetImage("assets/logo.png"), + width: 100, + height: 100, + ), + SizedBox( + height: MediaQuery.of(context).size.height * 0.02, + ), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + width: MediaQuery.of(context).size.width * 0.02, + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.26, + child: const Text( + 'Nama', + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + ), + textAlign: TextAlign.left, + ), + ), + const SizedBox( + width: 10, + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.05, + child: const Text(" : "), + ), + const SizedBox( + width: 10, + ), + Flexible( + child: SizedBox( + width: MediaQuery.of(context).size.width * 0.5, + child: const Text( + 'LUCKY PRASETIO AY', + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.02, + ), + ], + ), + SizedBox( + height: MediaQuery.of(context).size.height * 0.02, + ), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + width: MediaQuery.of(context).size.width * 0.02, + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.26, + child: const Text( + 'NIM', + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + ), + textAlign: TextAlign.left, + ), + ), + const SizedBox( + width: 10, + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.05, + child: const Text(" : "), + ), + const SizedBox( + width: 10, + ), + Flexible( + child: SizedBox( + width: MediaQuery.of(context).size.width * 0.5, + child: const Text( + '217 280 111', + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.02, + ), + ], + ), + SizedBox( + height: MediaQuery.of(context).size.height * 0.02, + ), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + width: MediaQuery.of(context).size.width * 0.02, + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.26, + child: const Text( + 'Pembimbing 1', + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + ), + textAlign: TextAlign.left, + ), + ), + const SizedBox( + width: 10, + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.05, + child: const Text(" : "), + ), + const SizedBox( + width: 10, + ), + Flexible( + child: SizedBox( + width: MediaQuery.of(context).size.width * 0.5, + child: const Text( + 'Muhammad Basri, ST., MT.', + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.02, + ), + ], + ), + SizedBox( + height: MediaQuery.of(context).size.height * 0.02, + ), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + width: MediaQuery.of(context).size.width * 0.02, + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.26, + child: const Text( + 'Pembimbing 2', + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + ), + textAlign: TextAlign.left, + ), + ), + const SizedBox( + width: 10, + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.05, + child: const Text(" : "), + ), + const SizedBox( + width: 10, + ), + Flexible( + child: SizedBox( + width: MediaQuery.of(context).size.width * 0.5, + child: const Text( + 'Ir. Untung Suwardoyo, S.Kom.,MT.IPP', + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.02, + ), + ], + ), + SizedBox( + height: MediaQuery.of(context).size.height * 0.02, + ), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + width: MediaQuery.of(context).size.width * 0.02, + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.26, + child: const Text( + 'Judul', + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + ), + textAlign: TextAlign.left, + ), + ), + const SizedBox( + width: 10, + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.05, + child: const Text(" : "), + ), + const SizedBox( + width: 10, + ), + Flexible( + child: SizedBox( + width: MediaQuery.of(context).size.width * 0.5, + child: const Text( + 'PROTOTYPE SISTEM MONITORING DISFUNGSI TRAFFIC LIGHT', + textAlign: TextAlign.justify, + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.02, + ), + ], + ) + ], + ); + }, + ); + } +} diff --git a/lib/ui/views/the_index/profil/profil_view_model.dart b/lib/ui/views/the_index/profil/profil_view_model.dart new file mode 100644 index 0000000..edb68ec --- /dev/null +++ b/lib/ui/views/the_index/profil/profil_view_model.dart @@ -0,0 +1,5 @@ +import 'package:traffic_light/app/core/custom_base_view_model.dart'; + +class ProfilViewModel extends CustomBaseViewModel { + Future init() async {} +} diff --git a/lib/ui/views/the_index/status_lampu/status_lampu_view.dart b/lib/ui/views/the_index/status_lampu/status_lampu_view.dart new file mode 100644 index 0000000..0ab87cd --- /dev/null +++ b/lib/ui/views/the_index/status_lampu/status_lampu_view.dart @@ -0,0 +1,105 @@ +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; +import 'package:traffic_light/app/themes/app_text.dart'; + +import './status_lampu_view_model.dart'; + +class StatusLampuView extends StatelessWidget { + const StatusLampuView({super.key}); + + @override + Widget build(BuildContext context) { + return ViewModelBuilder.reactive( + viewModelBuilder: () => StatusLampuViewModel(), + onViewModelReady: (StatusLampuViewModel model) async { + await model.init(); + }, + builder: ( + BuildContext context, + StatusLampuViewModel model, + Widget? child, + ) { + // create traffic light using column + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + padding: const EdgeInsets.all(20), + width: 150, + height: 400, + decoration: BoxDecoration( + color: Colors.black, + borderRadius: BorderRadius.circular(20), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 100, + height: 100, + decoration: BoxDecoration( + color: model.light == 'Red' + ? const Color.fromARGB(255, 249, 8, 8) + : Colors.red[100], + shape: BoxShape.circle, + ), + ), + const SizedBox(height: 20), + Container( + width: 100, + height: 100, + decoration: BoxDecoration( + color: model.light == 'Yellow' + ? const Color.fromARGB(255, 245, 220, 0) + : Colors.yellow[100], + shape: BoxShape.circle, + ), + ), + const SizedBox(height: 20), + Container( + width: 100, + height: 100, + decoration: BoxDecoration( + color: model.light == 'Green' + ? const Color.fromARGB(255, 0, 255, 13) + : Colors.green[100], + shape: BoxShape.circle, + ), + ), + ], + ), + ), + const SizedBox(height: 20), + Text( + model.no == null + ? '...' + : model.strLength <= 2 + ? model.no! + : "...", + style: italicTextStyle.copyWith( + fontWeight: FontWeight.bold, + fontSize: 30, + ), + ), + const SizedBox(height: 20), + Text( + 'Current RMS (A) : ${model.rms ?? '...'}', + style: boldTextStyle.copyWith( + fontSize: 20, + ), + ), + const SizedBox(height: 20), + Text( + 'Status Traffic Light : ${model.plnStatus}', + style: boldTextStyle.copyWith( + fontSize: 20, + ), + ), + ], + ), + ); + }, + ); + } +} diff --git a/lib/ui/views/the_index/status_lampu/status_lampu_view_model.dart b/lib/ui/views/the_index/status_lampu/status_lampu_view_model.dart new file mode 100644 index 0000000..2b0a11a --- /dev/null +++ b/lib/ui/views/the_index/status_lampu/status_lampu_view_model.dart @@ -0,0 +1,35 @@ +import 'package:traffic_light/app/core/custom_base_view_model.dart'; + +import '../../../../app/app.logger.dart'; + +class StatusLampuViewModel extends CustomBaseViewModel { + final log = getLogger('StatusLampuViewModel'); + String plnStatus = "...."; + int strLength = 0; + Future init() async { + socketIoClient.on('datanya', (data) { + // log.d(data); + String rmsnya = data['rms']; + no = data['no']; + strLength = no!.length; + light = data['light']; + pln = data['pln']; + rms = double.parse(rmsnya); + + log.d(rms); + + if (pln == "") { + plnStatus = "...."; + } else if (pln == "PLN OFF") { + plnStatus = "Listrik mati"; + } else if (pln == "PLN ON" && rms! < 0.262) { + // 0.262 , 0.37 + plnStatus = "Lampu terputus & rusak"; + } else if (pln == "PLN ON" && rms! >= 0.262) { + plnStatus = "Lampu normal"; + } + + notifyListeners(); + }); + } +} diff --git a/lib/ui/views/the_index/the_index_view.dart b/lib/ui/views/the_index/the_index_view.dart new file mode 100644 index 0000000..c62866b --- /dev/null +++ b/lib/ui/views/the_index/the_index_view.dart @@ -0,0 +1,96 @@ +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; +import 'package:stacked_services/stacked_services.dart'; +import 'package:stylish_bottom_bar/stylish_bottom_bar.dart'; + +import '../../../app/app.router.dart'; +import '../../../app/themes/app_colors.dart'; +import '../../../app/themes/app_text.dart'; +import './the_index_view_model.dart'; + +class TheIndexView extends StatelessWidget { + const TheIndexView({super.key}); + + @override + Widget build(BuildContext context) { + return ViewModelBuilder.reactive( + viewModelBuilder: () => TheIndexViewModel(), + onViewModelReady: (TheIndexViewModel model) async { + await model.init(); + }, + builder: ( + BuildContext context, + TheIndexViewModel model, + Widget? child, + ) { + return WillPopScope( + onWillPop: () async { + return false; + }, + child: Scaffold( + appBar: AppBar( + title: Text( + model.header, + style: const TextStyle( + color: Colors.white, + fontSize: 20, + ), + ), + backgroundColor: mainColor, + elevation: 0, + automaticallyImplyLeading: false, + actions: [ + IconButton( + onPressed: () { + // model.logout(); + }, + icon: const Icon(Icons.logout, color: Colors.white), + ), + ], + ), + body: ExtendedNavigator( + navigatorKey: StackedService.nestedNavigationKey(3), + router: TheIndexViewRouter(), + initialRoute: TheIndexViewRoutes.statusLampuView, + ), + bottomNavigationBar: StylishBottomBar( + items: [ + for (var item in model.bottomNavBarList) + BottomBarItem( + icon: Icon(item['icon'], + color: model.currentIndex == + model.bottomNavBarList.indexOf(item) + ? sixthGrey + : backgroundColor), + title: Text( + item['name'], + style: regularTextStyle.copyWith( + color: model.currentIndex == + model.bottomNavBarList.indexOf(item) + ? sixthGrey + : mainGrey, + ), + ), + backgroundColor: model.currentIndex == + model.bottomNavBarList.indexOf(item) + ? fontColor + : mainGrey, + ), + ], + currentIndex: model.currentIndex, + hasNotch: true, + backgroundColor: mainColor, + onTap: (value) { + model.handleNavigation(value); + }, + option: BubbleBarOptions( + barStyle: BubbleBarStyle.horizontal, + bubbleFillStyle: BubbleFillStyle.fill, + opacity: 0.3), + ), + ), + ); + }, + ); + } +} diff --git a/lib/ui/views/the_index/the_index_view_model.dart b/lib/ui/views/the_index/the_index_view_model.dart new file mode 100644 index 0000000..86b4696 --- /dev/null +++ b/lib/ui/views/the_index/the_index_view_model.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; +import 'package:stacked_services/stacked_services.dart'; +import 'package:traffic_light/app/app.router.dart'; + +import '../../../app/app.locator.dart'; +import '../../../app/app.logger.dart'; + +class TheIndexViewModel extends IndexTrackingViewModel { + final log = getLogger('TheIndexViewModel'); + final _navigationService = locator(); + + final _bottomNavBarList = [ + { + 'name': 'Status Traffic Light', + 'icon': Icons.list_alt_outlined, + 'header': 'Status Traffic Light', + }, + { + 'name': 'Profil Peneliti', + 'icon': Icons.person_outline, + 'header': 'Profil Peneliti', + }, + ]; + + List> get bottomNavBarList => _bottomNavBarList; + + final List _views = [ + TheIndexViewRoutes.statusLampuView, + TheIndexViewRoutes.profilView, + ]; + + String header = 'Status Lampu'; + + Future init() async {} + + 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, + ); + } +} diff --git a/lib/ui/widgets/my_button.dart b/lib/ui/widgets/my_button.dart new file mode 100644 index 0000000..97596be --- /dev/null +++ b/lib/ui/widgets/my_button.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; + +import '../../app/themes/app_colors.dart'; + +class MyButton extends StatelessWidget { + const MyButton({ + Key? key, + required this.text, + this.onPressed, + this.color = mainColor, + }) : super(key: key); + + final String text; + final VoidCallback? onPressed; + final Color color; + + @override + Widget build(BuildContext context) { + return ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: color, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30), + ), + ), + onPressed: onPressed, + child: Text( + text, + style: const TextStyle( + color: backgroundColor, + fontSize: 18, + ), + ), + ); + } +} diff --git a/lib/ui/widgets/my_textformfield.dart b/lib/ui/widgets/my_textformfield.dart new file mode 100644 index 0000000..43544e0 --- /dev/null +++ b/lib/ui/widgets/my_textformfield.dart @@ -0,0 +1,96 @@ +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, + this.width = double.infinity, + }) : super(key: key); + + final String? labelText; + final String? hintText; + final bool? obscureText; + final FormFieldValidator? 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; + final double? width; + + @override + Widget build(BuildContext context) { + return SizedBox( + width: width, + child: 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, + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 0000000..effbc44 --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,685 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a + url: "https://pub.dev" + source: hosted + version: "61.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 + url: "https://pub.dev" + source: hosted + version: "5.13.0" + args: + dependency: transitive + description: + name: args + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" + url: "https://pub.dev" + source: hosted + version: "2.5.0" + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + build: + dependency: transitive + description: + name: build + sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + build_config: + dependency: transitive + description: + name: build_config + sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 + url: "https://pub.dev" + source: hosted + version: "1.1.1" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" + url: "https://pub.dev" + source: hosted + version: "4.0.1" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" + url: "https://pub.dev" + source: hosted + version: "2.4.2" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22" + url: "https://pub.dev" + source: hosted + version: "2.4.9" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799" + url: "https://pub.dev" + source: hosted + version: "7.3.0" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb + url: "https://pub.dev" + source: hosted + version: "8.9.2" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff + url: "https://pub.dev" + source: hosted + version: "2.0.3" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 + url: "https://pub.dev" + source: hosted + version: "4.10.0" + collection: + dependency: transitive + description: + name: collection + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + url: "https://pub.dev" + source: hosted + version: "1.17.1" + convert: + dependency: transitive + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + crypto: + dependency: transitive + description: + name: crypto + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" + source: hosted + version: "3.0.3" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d + url: "https://pub.dev" + source: hosted + version: "1.0.6" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + dio: + dependency: "direct main" + description: + name: dio + sha256: e17f6b3097b8c51b72c74c9f071a605c47bcc8893839bd66732457a5ebe73714 + url: "https://pub.dev" + source: hosted + version: "5.5.0+1" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: "36c5b2d79eb17cdae41e974b7a8284fec631651d2a6f39a8a2ff22327e90aeac" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + file: + dependency: transitive + description: + name: file + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_dotenv: + dependency: "direct main" + description: + name: flutter_dotenv + sha256: "9357883bdd153ab78cbf9ffa07656e336b8bbb2b5a3ca596b0b27e119f7c7d77" + url: "https://pub.dev" + source: hosted + version: "5.1.0" + flutter_easyloading: + dependency: "direct main" + description: + name: flutter_easyloading + sha256: ba21a3c883544e582f9cc455a4a0907556714e1e9cf0eababfcb600da191d17c + url: "https://pub.dev" + source: hosted + version: "3.0.5" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + url: "https://pub.dev" + source: hosted + version: "2.0.3" + flutter_spinkit: + dependency: transitive + description: + name: flutter_spinkit + sha256: d2696eed13732831414595b98863260e33e8882fc069ee80ec35d4ac9ddb0472 + url: "https://pub.dev" + source: hosted + version: "5.2.1" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + freezed_annotation: + dependency: transitive + description: + name: freezed_annotation + sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 + url: "https://pub.dev" + source: hosted + version: "2.4.4" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" + get: + dependency: transitive + description: + name: get + sha256: "2ba20a47c8f1f233bed775ba2dd0d3ac97b4cf32fc17731b3dfc672b06b0e92a" + url: "https://pub.dev" + source: hosted + version: "4.6.5" + get_it: + dependency: transitive + description: + name: get_it + sha256: e6017ce7fdeaf218dc51a100344d8cb70134b80e28b760f8bb23c242437bafd7 + url: "https://pub.dev" + source: hosted + version: "7.6.7" + glob: + dependency: transitive + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + graphs: + dependency: transitive + description: + name: graphs + sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 + url: "https://pub.dev" + source: hosted + version: "2.3.1" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + intl: + dependency: "direct main" + description: + name: intl + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + url: "https://pub.dev" + source: hosted + version: "0.19.0" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + lints: + dependency: transitive + description: + name: lints + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + logger: + dependency: transitive + description: + name: logger + sha256: "7ad7215c15420a102ec687bb320a7312afd449bac63bfb1c60d9787c27b9767f" + url: "https://pub.dev" + source: hosted + version: "1.4.0" + logging: + dependency: transitive + description: + name: logging + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + url: "https://pub.dev" + source: hosted + version: "0.12.15" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + meta: + dependency: transitive + description: + name: meta + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + mime: + dependency: transitive + description: + name: mime + sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + url: "https://pub.dev" + source: hosted + version: "1.0.4" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + path: + dependency: transitive + description: + name: path + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + url: "https://pub.dev" + source: hosted + version: "1.8.3" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" + provider: + dependency: transitive + description: + name: provider + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c + url: "https://pub.dev" + source: hosted + version: "6.1.2" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + recase: + dependency: transitive + description: + name: recase + sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213 + url: "https://pub.dev" + source: hosted + version: "4.1.0" + shelf: + dependency: transitive + description: + name: shelf + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + url: "https://pub.dev" + source: hosted + version: "1.4.1" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + socket_io_client: + dependency: "direct main" + description: + name: socket_io_client + sha256: ede469f3e4c55e8528b4e023bdedbc20832e8811ab9b61679d1ba3ed5f01f23b + url: "https://pub.dev" + source: hosted + version: "2.0.3+1" + socket_io_common: + dependency: transitive + description: + name: socket_io_common + sha256: "2ab92f8ff3ebbd4b353bf4a98bee45cc157e3255464b2f90f66e09c4472047eb" + url: "https://pub.dev" + source: hosted + version: "2.0.3" + source_gen: + dependency: transitive + description: + name: source_gen + sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" + url: "https://pub.dev" + source: hosted + version: "1.5.0" + source_span: + dependency: transitive + description: + name: source_span + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + url: "https://pub.dev" + source: hosted + version: "1.9.1" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" + source: hosted + version: "1.11.0" + stacked: + dependency: "direct main" + description: + name: stacked + sha256: ed19ecdc2dcc682b9be9c7e34646e603c0f770437a914b15c7d2d13391c92a09 + url: "https://pub.dev" + source: hosted + version: "3.4.3" + stacked_generator: + dependency: "direct dev" + description: + name: stacked_generator + sha256: c141baf56cb7168dfde142d3578118c7bf914be9fb62bc8593344e97894ac8f9 + url: "https://pub.dev" + source: hosted + version: "1.5.1" + stacked_services: + dependency: "direct main" + description: + name: stacked_services + sha256: df2780a026fd1d3671aceaa9d7cae9e8d15d577c71ef29d7dd6f79a0a289e77e + url: "https://pub.dev" + source: hosted + version: "0.9.12" + stacked_shared: + dependency: transitive + description: + name: stacked_shared + sha256: "26e11dcfe23df81d565d0180eb5bcf4742efed066ba3328623b458f21a82b346" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + stylish_bottom_bar: + dependency: "direct main" + description: + name: stylish_bottom_bar + sha256: ca72557a5bd8f44caae9017eb3a73002e9189d7a9d2fac598fa55be13724f32b + url: "https://pub.dev" + source: hosted + version: "1.1.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + url: "https://pub.dev" + source: hosted + version: "0.5.1" + timing: + dependency: transitive + description: + name: timing + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" + universal_io: + dependency: transitive + description: + name: universal_io + sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad" + url: "https://pub.dev" + source: hosted + version: "2.2.2" + validatorless: + dependency: "direct main" + description: + name: validatorless + sha256: ddb46df636114b3322d289489164cac309767b157191ba43c7ad49b28c2b57c7 + url: "https://pub.dev" + source: hosted + version: "1.2.3" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + watcher: + dependency: transitive + description: + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + url: "https://pub.dev" + source: hosted + version: "2.4.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" + source: hosted + version: "3.1.2" +sdks: + dart: ">=3.0.0 <4.0.0" + flutter: ">=3.10.0" diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..24ead13 --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,102 @@ +name: traffic_light +description: A new Flutter project. +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +# In Windows, build-name is used as the major, minor, and patch parts +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.0+1 + +environment: + sdk: '>=3.0.0 <4.0.0' + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + stacked: ^3.0.1 + stacked_services: ^0.9.8 + flutter_dotenv: + dio: + intl: + flutter_easyloading: + stylish_bottom_bar: ^1.0.0 + validatorless: ^1.2.3 + socket_io_client: + + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + build_runner: + stacked_generator: + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + assets: + - .env + - assets/logo.png + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/test/widget_test.dart b/test/widget_test.dart new file mode 100644 index 0000000..c2ed0f1 --- /dev/null +++ b/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:traffic_light/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/web/favicon.png b/web/favicon.png new file mode 100644 index 0000000..8aaa46a Binary files /dev/null and b/web/favicon.png differ diff --git a/web/icons/Icon-192.png b/web/icons/Icon-192.png new file mode 100644 index 0000000..b749bfe Binary files /dev/null and b/web/icons/Icon-192.png differ diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png new file mode 100644 index 0000000..88cfd48 Binary files /dev/null and b/web/icons/Icon-512.png differ diff --git a/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000..eb9b4d7 Binary files /dev/null and b/web/icons/Icon-maskable-192.png differ diff --git a/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000..d69c566 Binary files /dev/null and b/web/icons/Icon-maskable-512.png differ diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..4527eaf --- /dev/null +++ b/web/index.html @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + traffic_light + + + + + + + + + + diff --git a/web/manifest.json b/web/manifest.json new file mode 100644 index 0000000..6ea33cf --- /dev/null +++ b/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "traffic_light", + "short_name": "traffic_light", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +}