├── .github └── workflows │ └── release.yaml ├── .gitignore ├── .metadata ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── analog_clock │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-v21 │ │ │ └── launch_background.xml │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets ├── fonts │ └── Lato-Regular.ttf └── icons │ ├── Liberty.svg │ ├── Moon.svg │ ├── Settings.svg │ ├── Sun.svg │ ├── Sydney.svg │ ├── clock.svg │ ├── stop_watch.svg │ ├── watch_2.svg │ └── world.svg ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings ├── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h └── RunnerTests │ └── RunnerTests.swift ├── lib ├── main.dart └── src │ ├── app.dart │ ├── models │ └── my_theme_provider.dart │ ├── pages │ ├── alarm │ │ ├── alarm_page.dart │ │ ├── controllers │ │ │ └── alarm_controller.dart │ │ └── widgets │ │ │ └── list_alarm.dart │ ├── bedtime │ │ ├── bed_time_page.dart │ │ └── widgets │ │ │ └── pick_day.dart │ ├── countdown │ │ ├── controllers │ │ │ └── count_down_controller.dart │ │ ├── count_down_page.dart │ │ └── widgets │ │ │ ├── count_controll.dart │ │ │ └── pick_time.dart │ ├── global │ │ ├── controllers │ │ │ └── clock_controller.dart │ │ ├── global_page.dart │ │ └── widgets │ │ │ ├── body.dart │ │ │ ├── clock.dart │ │ │ ├── clock_painter.dart │ │ │ ├── country_card.dart │ │ │ └── time_in_hour_and_minute.dart │ ├── stopwatch │ │ ├── controllers │ │ │ └── stop_watch_controller.dart │ │ ├── stop_watch_page.dart │ │ └── widgets │ │ │ ├── list_stop_watch.dart │ │ │ └── stop_watch_controll.dart │ └── tab │ │ └── tab_bar_layout.dart │ ├── public │ ├── constants.dart │ └── size_config.dart │ ├── routes │ ├── app_pages.dart │ └── app_routes.dart │ ├── shared │ └── logger │ │ └── logger_utils.dart │ └── theme │ ├── theme_service.dart │ └── themes.dart ├── pubspec.lock ├── pubspec.yaml ├── screenshots ├── bed_dark.png ├── bed_light.png ├── clock_dark.png ├── clock_light.png ├── count_down_dark.png ├── count_down_light.png ├── stopwatch_dark.png └── stopwatch_light.png └── test └── widget_test.dart /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Analyze, Build, Release APK 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | 8 | jobs: 9 | test: 10 | name: Analyze project 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v1 14 | - uses: actions/setup-java@v3 15 | with: 16 | distribution: "temurin" 17 | java-version: "17" 18 | cache: "gradle" 19 | - uses: shimataro/ssh-key-action@v2 20 | with: 21 | key: ${{ secrets.SSH }} 22 | name: id_rsa 23 | known_hosts: ${{ secrets.KNOWN_HOSTS }} 24 | - uses: subosito/flutter-action@v2 25 | with: 26 | flutter-version: "3.13.7" 27 | cache: true 28 | - run: flutter pub get 29 | - run: flutter analyze 30 | # - run: flutter test 31 | 32 | build_apk: 33 | needs: [test] 34 | name: Build & Release APK 35 | runs-on: ubuntu-latest 36 | steps: 37 | - uses: actions/checkout@v1 38 | - uses: actions/setup-java@v3 39 | with: 40 | distribution: "temurin" 41 | java-version: "17" 42 | cache: "gradle" 43 | - uses: shimataro/ssh-key-action@v2 44 | with: 45 | key: ${{ secrets.SSH }} 46 | name: id_rsa 47 | known_hosts: ${{ secrets.KNOWN_HOSTS }} 48 | - uses: subosito/flutter-action@v2 49 | with: 50 | flutter-version: "3.13.7" 51 | cache: true 52 | - run: flutter pub get 53 | - run: flutter build apk --release 54 | - name: Create a Release APK 55 | uses: ncipollo/release-action@v1 56 | with: 57 | name: "Analog Clock ${{ github.ref_name }}" 58 | artifacts: "build/app/outputs/apk/release/*.apk" 59 | token: ${{ secrets.TOKEN }} 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Exceptions to above rules. 44 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 45 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "2f708eb8396e362e280fac22cf171c2cb467343c" 8 | channel: "stable" 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 2f708eb8396e362e280fac22cf171c2cb467343c 17 | base_revision: 2f708eb8396e362e280fac22cf171c2cb467343c 18 | - platform: android 19 | create_revision: 2f708eb8396e362e280fac22cf171c2cb467343c 20 | base_revision: 2f708eb8396e362e280fac22cf171c2cb467343c 21 | 22 | # User provided section 23 | 24 | # List of Local paths (relative to this file) that should be 25 | # ignored by the migrate tool. 26 | # 27 | # Files that are not part of the templates will be ignored by default. 28 | unmanaged_files: 29 | - 'lib/main.dart' 30 | - 'ios/Runner.xcodeproj/project.pbxproj' 31 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | lambiengcode@gmail.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Dao Hong Vinh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Analog Clock Light & Dark Theme 🕚 📆 2 | 3 | ## Description: 4 | - We design a nice clean analog clock app UI by using flutter. Our clock supports both the Dark and Light theme. If you want to learn how to use both Light and Dark theme then it helps a lot. 5 | 6 | ## How I can run it? 7 | - 🚀 Clone this repo 8 | - 🚀 Run below code in terminal of project 9 | ```terminal 10 | flutter pub get 11 | flutter run 12 | ``` 13 | 14 | ## Author: 15 | - Primary Author: Flutter Way 16 | - Customize by lambiengcode 17 | 18 | ## Screenshots 19 | 20 | - Light Mode 🌞 21 |

22 | 23 | 24 | 25 | 26 |

27 | 28 | - Dark Mode 🌙 29 |

30 | 31 | 32 | 33 | 34 |

35 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | analyzer: 13 | exclude: 14 | - "lib/**/*.freezed.dart" 15 | - "lib/**/*.g.dart" 16 | - "lib/**/*.config.dart" 17 | plugins: 18 | - dart_code_metrics 19 | 20 | linter: 21 | # The lint rules applied to this project can be customized in the 22 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 23 | # included above or to enable additional rules. A list of all available lints 24 | # and their documentation is published at 25 | # https://dart-lang.github.io/linter/lints/index.html. 26 | # 27 | # Instead of disabling a lint rule for the entire project in the 28 | # section below, it can also be suppressed for a single line of code 29 | # or a specific dart file by using the `// ignore: name_of_lint` and 30 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 31 | # producing the lint. 32 | rules: 33 | require_trailing_commas: true 34 | prefer_final_locals: true 35 | prefer_final_in_for_each: true 36 | null_check_on_nullable_type_parameter: true 37 | avoid_function_literals_in_foreach_calls: true 38 | avoid_relative_lib_imports: true 39 | always_use_package_imports: true 40 | avoid_types_on_closure_parameters: true 41 | avoid_void_async: true 42 | cancel_subscriptions: true 43 | close_sinks: true 44 | package_api_docs: true 45 | package_prefixed_library_names: true 46 | test_types_in_equals: true 47 | throw_in_finally: true 48 | unnecessary_statements: true 49 | use_super_parameters: true 50 | avoid_dynamic_calls: false 51 | avoid_slow_async_io: true 52 | no_duplicate_case_values: true 53 | use_build_context_synchronously: true 54 | valid_regexps: true 55 | depend_on_referenced_packages: true 56 | always_require_non_null_named_parameters: true 57 | annotate_overrides: true 58 | avoid_annotating_with_dynamic: true 59 | avoid_bool_literals_in_conditional_expressions: true 60 | avoid_catches_without_on_clauses: false 61 | avoid_catching_errors: false 62 | avoid_classes_with_only_static_members: false 63 | avoid_double_and_int_checks: true 64 | avoid_escaping_inner_quotes: true 65 | avoid_field_initializers_in_const_classes: true 66 | avoid_init_to_null: true 67 | avoid_null_checks_in_equality_operators: true 68 | # Need for LINT 69 | avoid_positional_boolean_parameters: false 70 | avoid_private_typedef_functions: true 71 | avoid_redundant_argument_values: true 72 | avoid_returning_null_for_void: true 73 | avoid_shadowing_type_parameters: true 74 | avoid_single_cascade_in_expression_statements: true 75 | avoid_unnecessary_containers: true 76 | await_only_futures: true 77 | cast_nullable_to_non_nullable: false 78 | combinators_ordering: true 79 | conditional_uri_does_not_exist: true 80 | enable_null_safety: true 81 | eol_at_end_of_file: true 82 | exhaustive_cases: true 83 | prefer_is_empty: true 84 | prefer_is_not_empty: true 85 | prefer_is_not_operator: true 86 | prefer_iterable_whereType: true 87 | prefer_null_aware_method_calls: true 88 | prefer_null_aware_operators: true 89 | prefer_spread_collections: true 90 | sized_box_for_whitespace: true 91 | sized_box_shrink_expand: true 92 | unnecessary_null_checks: true 93 | unnecessary_null_in_if_null_operators: true 94 | unnecessary_overrides: true 95 | unnecessary_parenthesis: true 96 | unnecessary_raw_strings: true 97 | unnecessary_string_interpolations: true 98 | unnecessary_this: true 99 | unnecessary_to_list_in_spreads: true 100 | unreachable_from_main: true 101 | use_colored_box: true 102 | use_enums: true 103 | use_full_hex_values_for_flutter_colors: true 104 | use_if_null_to_convert_nulls_to_bools: true 105 | use_is_even_rather_than_modulo: true 106 | use_named_constants: true 107 | use_raw_strings: true 108 | use_setters_to_change_properties: true 109 | use_string_buffers: false 110 | use_test_throws_matchers: true 111 | use_to_and_as_if_applicable: true 112 | void_checks: true 113 | 114 | # Pub rules 115 | secure_pubspec_urls: true 116 | 117 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 118 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 119 | # Additional information about this file can be found at 120 | # https://dart.dev/guides/language/analysis-options 121 | 122 | dart_code_metrics: 123 | # anti-patterns: 124 | # - long-method 125 | # - long-parameter-list 126 | metrics: 127 | cyclomatic-complexity: 20 128 | maximum-nesting-level: 5 129 | number-of-parameters: 10 130 | source-lines-of-code: 50 131 | metrics-exclude: 132 | - test/** 133 | rules: 134 | # - newline-before-return: 135 | # severity: style 136 | - no-boolean-literal-compare: 137 | severity: style 138 | # - no-empty-block: 139 | # severity: warning 140 | # - prefer-trailing-comma: 141 | # severity: none 142 | # - prefer-conditional-expressions: 143 | # severity: none 144 | - no-equal-then-else: 145 | severity: error 146 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.application" 3 | id "kotlin-android" 4 | id "dev.flutter.flutter-gradle-plugin" 5 | } 6 | 7 | def localProperties = new Properties() 8 | def localPropertiesFile = rootProject.file('local.properties') 9 | if (localPropertiesFile.exists()) { 10 | localPropertiesFile.withReader('UTF-8') { reader -> 11 | localProperties.load(reader) 12 | } 13 | } 14 | 15 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 16 | if (flutterVersionCode == null) { 17 | flutterVersionCode = '1' 18 | } 19 | 20 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 21 | if (flutterVersionName == null) { 22 | flutterVersionName = '1.0' 23 | } 24 | 25 | android { 26 | namespace "com.example.analog_clock" 27 | compileSdkVersion flutter.compileSdkVersion 28 | ndkVersion flutter.ndkVersion 29 | 30 | compileOptions { 31 | sourceCompatibility JavaVersion.VERSION_1_8 32 | targetCompatibility JavaVersion.VERSION_1_8 33 | } 34 | 35 | kotlinOptions { 36 | jvmTarget = '1.8' 37 | } 38 | 39 | sourceSets { 40 | main.java.srcDirs += 'src/main/kotlin' 41 | } 42 | 43 | defaultConfig { 44 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 45 | applicationId "com.example.analog_clock" 46 | // You can update the following values to match your application needs. 47 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. 48 | minSdkVersion flutter.minSdkVersion 49 | targetSdkVersion flutter.targetSdkVersion 50 | versionCode flutterVersionCode.toInteger() 51 | versionName flutterVersionName 52 | } 53 | 54 | buildTypes { 55 | release { 56 | // TODO: Add your own signing config for the release build. 57 | // Signing with the debug keys for now, so `flutter run --release` works. 58 | signingConfig signingConfigs.debug 59 | } 60 | } 61 | } 62 | 63 | flutter { 64 | source '../..' 65 | } 66 | 67 | dependencies {} 68 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 14 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | 29 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/analog_clock/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.analog_clock 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.7.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.3.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | tasks.register("clean", Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip 6 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | } 9 | settings.ext.flutterSdkPath = flutterSdkPath() 10 | 11 | includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") 12 | 13 | plugins { 14 | id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false 15 | } 16 | } 17 | 18 | include ":app" 19 | 20 | apply from: "${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle/app_plugin_loader.gradle" 21 | -------------------------------------------------------------------------------- /assets/fonts/Lato-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/assets/fonts/Lato-Regular.ttf -------------------------------------------------------------------------------- /assets/icons/Liberty.svg: -------------------------------------------------------------------------------- 1 | Liberty -------------------------------------------------------------------------------- /assets/icons/Moon.svg: -------------------------------------------------------------------------------- 1 | Moon -------------------------------------------------------------------------------- /assets/icons/Settings.svg: -------------------------------------------------------------------------------- 1 | Settings -------------------------------------------------------------------------------- /assets/icons/Sun.svg: -------------------------------------------------------------------------------- 1 | Sun -------------------------------------------------------------------------------- /assets/icons/Sydney.svg: -------------------------------------------------------------------------------- 1 | Sydney -------------------------------------------------------------------------------- /assets/icons/clock.svg: -------------------------------------------------------------------------------- 1 | Group 5 -------------------------------------------------------------------------------- /assets/icons/stop_watch.svg: -------------------------------------------------------------------------------- 1 | Group 4 -------------------------------------------------------------------------------- /assets/icons/watch_2.svg: -------------------------------------------------------------------------------- 1 | Shape -------------------------------------------------------------------------------- /assets/icons/world.svg: -------------------------------------------------------------------------------- 1 | Shape -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 11.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '11.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | target 'RunnerTests' do 36 | inherit! :search_paths 37 | end 38 | end 39 | 40 | post_install do |installer| 41 | installer.pods_project.targets.each do |target| 42 | flutter_additional_ios_build_settings(target) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - path_provider_foundation (0.0.1): 4 | - Flutter 5 | - FlutterMacOS 6 | 7 | DEPENDENCIES: 8 | - Flutter (from `Flutter`) 9 | - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) 10 | 11 | EXTERNAL SOURCES: 12 | Flutter: 13 | :path: Flutter 14 | path_provider_foundation: 15 | :path: ".symlinks/plugins/path_provider_foundation/darwin" 16 | 17 | SPEC CHECKSUMS: 18 | Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 19 | path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 20 | 21 | PODFILE CHECKSUM: 70d9d25280d0dd177a5f637cdb0f0b0b12c6a189 22 | 23 | COCOAPODS: 1.12.1 24 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 65 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Analog Clock 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | analog_clock 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | CADisableMinimumFrameDurationOnPhone 45 | 46 | UIApplicationSupportsIndirectInputEvents 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /ios/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | // Package imports: 5 | import 'package:get/get.dart'; 6 | import 'package:get_storage/get_storage.dart'; 7 | 8 | // Project imports: 9 | import 'package:analog_clock/src/routes/app_pages.dart'; 10 | import 'package:analog_clock/src/shared/logger/logger_utils.dart'; 11 | import 'package:analog_clock/src/theme/theme_service.dart'; 12 | import 'package:analog_clock/src/theme/themes.dart'; 13 | 14 | void main() async { 15 | await GetStorage.init(); 16 | runApp( 17 | GetMaterialApp( 18 | debugShowCheckedModeBanner: false, 19 | logWriterCallback: Logger.write, 20 | initialRoute: AppPages.rootRoute, 21 | getPages: AppPages.routes, 22 | theme: Themes().lightTheme, 23 | darkTheme: Themes().darkTheme, 24 | themeMode: ThemeService().getThemeMode(), 25 | ), 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /lib/src/app.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/services.dart'; 4 | 5 | // Project imports: 6 | import 'package:analog_clock/src/pages/tab/tab_bar_layout.dart'; 7 | import 'package:analog_clock/src/theme/theme_service.dart'; 8 | 9 | class App extends StatefulWidget { 10 | const App({super.key}); 11 | 12 | @override 13 | State createState() => _AppState(); 14 | } 15 | 16 | class _AppState extends State with WidgetsBindingObserver { 17 | @override 18 | void initState() { 19 | WidgetsBinding.instance.addObserver(this); 20 | SystemChrome.setPreferredOrientations([ 21 | DeviceOrientation.portraitDown, 22 | DeviceOrientation.portraitUp, 23 | ]); 24 | ThemeService().initBrighness(); 25 | super.initState(); 26 | } 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return const TabBarLayout(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/src/models/my_theme_provider.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | class MyThemeModel extends ChangeNotifier { 5 | bool _isLightTheme = true; 6 | 7 | void changeTheme() { 8 | _isLightTheme = !_isLightTheme; 9 | notifyListeners(); 10 | } 11 | 12 | bool get isLightTheme => _isLightTheme; 13 | } 14 | -------------------------------------------------------------------------------- /lib/src/pages/alarm/alarm_page.dart: -------------------------------------------------------------------------------- 1 | // Package imports: 2 | import 'package:flutter_neumorphic_plus/flutter_neumorphic.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:percent_indicator/percent_indicator.dart'; 5 | 6 | // Project imports: 7 | import 'package:analog_clock/src/pages/alarm/controllers/alarm_controller.dart'; 8 | import 'package:analog_clock/src/pages/alarm/widgets/list_alarm.dart'; 9 | import 'package:analog_clock/src/public/constants.dart'; 10 | 11 | class AlarmPage extends StatefulWidget { 12 | const AlarmPage({super.key}); 13 | 14 | @override 15 | State createState() => _AlarmPageState(); 16 | } 17 | 18 | class _AlarmPageState extends State { 19 | final alarmController = Get.put(AlarmController()); 20 | 21 | @override 22 | void initState() { 23 | alarmController.startTimer(); 24 | super.initState(); 25 | } 26 | 27 | @override 28 | void dispose() { 29 | alarmController.stopTimer(); 30 | super.dispose(); 31 | } 32 | 33 | @override 34 | Widget build(BuildContext context) { 35 | return Scaffold( 36 | body: SizedBox( 37 | width: width, 38 | child: Stack( 39 | children: [ 40 | SizedBox.expand( 41 | child: Column( 42 | children: [ 43 | const SizedBox(height: 16.0), 44 | StreamBuilder( 45 | stream: alarmController.currentTime.stream, 46 | builder: (context, snapshot) { 47 | if (!snapshot.hasData) { 48 | return _buildClock(DateTime.now()); 49 | } 50 | 51 | return _buildClock(snapshot.data!); 52 | }, 53 | ), 54 | const Expanded( 55 | child: ListAlarm(), 56 | ), 57 | const SizedBox(height: 12.0), 58 | ], 59 | ), 60 | ), 61 | Positioned( 62 | bottom: height * .045, 63 | left: 0.0, 64 | right: 0.0, 65 | child: Row( 66 | mainAxisAlignment: MainAxisAlignment.center, 67 | children: [ 68 | NeumorphicButton( 69 | onPressed: () {}, 70 | duration: const Duration(milliseconds: 200), 71 | padding: EdgeInsets.all(width / 22.5), 72 | style: NeumorphicStyle( 73 | shape: NeumorphicShape.convex, 74 | boxShape: const NeumorphicBoxShape.circle(), 75 | depth: 10.0, 76 | intensity: .15, 77 | surfaceIntensity: .75, 78 | color: Theme.of(context) 79 | .scaffoldBackgroundColor 80 | .withOpacity(.8), 81 | ), 82 | child: Icon( 83 | Icons.add, 84 | size: width / 12.0, 85 | ), 86 | ), 87 | ], 88 | ), 89 | ), 90 | ], 91 | ), 92 | ), 93 | ); 94 | } 95 | 96 | Widget _buildClock(DateTime dateTime) { 97 | return CircularPercentIndicator( 98 | radius: width * .45, 99 | lineWidth: 60.0, 100 | percent: alarmController.percent, 101 | circularStrokeCap: CircularStrokeCap.round, 102 | backgroundColor: Theme.of(context).colorScheme.secondary, 103 | linearGradient: LinearGradient( 104 | begin: Alignment.topLeft, 105 | end: Alignment.bottomCenter, 106 | colors: [ 107 | Theme.of(context).primaryColor, 108 | Theme.of(context).secondaryHeaderColor, 109 | ], 110 | tileMode: TileMode.mirror, 111 | ), 112 | animationDuration: 1000, 113 | animateFromLastPercent: true, 114 | rotateLinearGradient: true, 115 | center: Column( 116 | mainAxisAlignment: MainAxisAlignment.center, 117 | children: [ 118 | Text( 119 | '${alarmController.formatTime(dateTime.hour)}:${alarmController.formatTime(dateTime.minute)}:${alarmController.formatTime(dateTime.second)}', 120 | style: Theme.of(context).textTheme.displayLarge?.copyWith( 121 | fontSize: width / 10.0, 122 | fontFamily: 'Lato', 123 | fontWeight: FontWeight.w400, 124 | ), 125 | ), 126 | const SizedBox(height: 2.0), 127 | Text( 128 | 'The alarm will continue\nin 3 hours', 129 | style: Theme.of(context).textTheme.displayLarge?.copyWith( 130 | fontSize: width / 26.0, 131 | fontFamily: 'Lato', 132 | fontWeight: FontWeight.w400, 133 | color: Theme.of(context).iconTheme.color, 134 | ), 135 | textAlign: TextAlign.center, 136 | ), 137 | ], 138 | ), 139 | ); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /lib/src/pages/alarm/controllers/alarm_controller.dart: -------------------------------------------------------------------------------- 1 | // Dart imports: 2 | import 'dart:async'; 3 | 4 | // Flutter imports: 5 | import 'package:flutter/material.dart'; 6 | 7 | // Package imports: 8 | import 'package:get/get.dart'; 9 | 10 | class AlarmController extends GetxController { 11 | StreamController currentTime = 12 | StreamController.broadcast(); 13 | ScrollController scrollController = ScrollController(); 14 | Timer? timer; 15 | double percent = .0; 16 | bool isRunning = false; 17 | List dateTimes = []; 18 | 19 | formatTime(input) { 20 | if (input < 10) { 21 | return '0$input'; 22 | } 23 | return input.toString(); 24 | } 25 | 26 | startTimer() { 27 | isRunning = true; 28 | // <---> virtual data 29 | dateTimes.add(DateTime.now().add(const Duration(hours: 3))); 30 | dateTimes.add(DateTime.now().add(const Duration(hours: 4))); 31 | dateTimes.add(DateTime.now().add(const Duration(hours: 5))); 32 | dateTimes.add(DateTime.now().add(const Duration(hours: 7))); 33 | // <---> virtual data 34 | update(); 35 | timer = Timer.periodic(const Duration(milliseconds: 17), (timer) { 36 | currentTime.add(DateTime.now()); 37 | percent = DateTime.now().second / 60.0; 38 | update(); 39 | }); 40 | } 41 | 42 | stopTimer() { 43 | timer?.cancel(); 44 | isRunning = false; 45 | update(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/src/pages/alarm/widgets/list_alarm.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | // Package imports: 5 | import 'package:get/get.dart'; 6 | import 'package:intl/intl.dart'; 7 | 8 | // Project imports: 9 | import 'package:analog_clock/src/pages/alarm/controllers/alarm_controller.dart'; 10 | import 'package:analog_clock/src/public/constants.dart'; 11 | 12 | class ListAlarm extends StatefulWidget { 13 | const ListAlarm({super.key}); 14 | 15 | @override 16 | State createState() => _ListAlarmState(); 17 | } 18 | 19 | class _ListAlarmState extends State { 20 | DateFormat format = DateFormat('HH:mm:ss'); 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return GetBuilder( 25 | builder: (_) => ListView.builder( 26 | controller: _.scrollController, 27 | padding: EdgeInsets.zero, 28 | itemCount: _.dateTimes.length, 29 | itemBuilder: (context, index) { 30 | return Container( 31 | padding: const EdgeInsets.symmetric( 32 | horizontal: 24.0, 33 | vertical: 12.0, 34 | ), 35 | child: Row( 36 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 37 | children: [ 38 | Column( 39 | crossAxisAlignment: CrossAxisAlignment.start, 40 | mainAxisAlignment: MainAxisAlignment.center, 41 | children: [ 42 | Text( 43 | 'Alarm: ${index + 1}', 44 | style: Theme.of(context).textTheme.displayLarge?.copyWith( 45 | fontSize: width / 24.0, 46 | fontFamily: 'Lato', 47 | fontWeight: FontWeight.w600, 48 | ), 49 | ), 50 | const SizedBox(height: 8.0), 51 | Text( 52 | '${format.format(_.dateTimes[index])},\t\tAlarm Daily', 53 | style: Theme.of(context).textTheme.displayLarge?.copyWith( 54 | fontSize: width / 24.0, 55 | fontFamily: 'Lato', 56 | fontWeight: FontWeight.w400, 57 | color: Theme.of(context) 58 | .textTheme 59 | .displayLarge 60 | ?.color 61 | ?.withOpacity(.8), 62 | ), 63 | ), 64 | ], 65 | ), 66 | Icon( 67 | Icons.alarm, 68 | size: width / 16.0, 69 | ), 70 | ], 71 | ), 72 | ); 73 | }, 74 | ), 75 | ); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /lib/src/pages/bedtime/bed_time_page.dart: -------------------------------------------------------------------------------- 1 | // Dart imports: 2 | import 'dart:math'; 3 | 4 | // Flutter imports: 5 | import 'package:flutter/material.dart'; 6 | 7 | // Package imports: 8 | import 'package:flutter_phosphor_icons/flutter_phosphor_icons.dart'; 9 | import 'package:get/get.dart'; 10 | import 'package:percent_indicator/circular_percent_indicator.dart'; 11 | 12 | // Project imports: 13 | import 'package:analog_clock/src/pages/bedtime/widgets/pick_day.dart'; 14 | import 'package:analog_clock/src/pages/global/controllers/clock_controller.dart'; 15 | import 'package:analog_clock/src/pages/global/widgets/clock_painter.dart'; 16 | import 'package:analog_clock/src/public/constants.dart'; 17 | import 'package:analog_clock/src/public/size_config.dart'; 18 | import 'package:analog_clock/src/theme/theme_service.dart'; 19 | 20 | class BedTimePage extends StatefulWidget { 21 | const BedTimePage({super.key}); 22 | 23 | @override 24 | State createState() => _BedTimePageState(); 25 | } 26 | 27 | class _BedTimePageState extends State { 28 | final clockController = Get.put(ClockController()); 29 | bool isOn = true; 30 | 31 | @override 32 | void initState() { 33 | clockController.startTimer(); 34 | super.initState(); 35 | } 36 | 37 | @override 38 | void dispose() { 39 | clockController.dispose(); 40 | super.dispose(); 41 | } 42 | 43 | toggle() { 44 | setState(() { 45 | isOn = !isOn; 46 | }); 47 | } 48 | 49 | @override 50 | Widget build(BuildContext context) { 51 | return Column( 52 | children: [ 53 | _buildTitle(context), 54 | const Spacer(), 55 | const PickDay(), 56 | const Spacer(flex: 2), 57 | Stack( 58 | alignment: Alignment.center, 59 | children: [ 60 | CircularPercentIndicator( 61 | radius: width * .45, 62 | lineWidth: 35.0, 63 | percent: .5, 64 | circularStrokeCap: CircularStrokeCap.round, 65 | backgroundColor: Theme.of(context).colorScheme.secondary, 66 | linearGradient: LinearGradient( 67 | begin: Alignment.topLeft, 68 | end: Alignment.bottomCenter, 69 | colors: [ 70 | Theme.of(context).primaryColor, 71 | Theme.of(context).secondaryHeaderColor, 72 | ], 73 | tileMode: TileMode.mirror, 74 | ), 75 | animationDuration: 2000, 76 | animateFromLastPercent: true, 77 | rotateLinearGradient: true, 78 | center: StreamBuilder( 79 | stream: clockController.currentDay.stream, 80 | builder: (context, snapshot) { 81 | if (!snapshot.hasData) { 82 | return _buildClock(context, DateTime.now()); 83 | } 84 | 85 | return _buildClock(context, snapshot.data); 86 | }, 87 | ), 88 | ), 89 | Positioned( 90 | top: width * .45 - 5, 91 | left: 0, 92 | right: 0, 93 | child: IconButton( 94 | icon: Icon( 95 | ThemeService().getThemeMode() == ThemeMode.dark 96 | ? PhosphorIcons.moon 97 | : PhosphorIcons.sun, 98 | color: Theme.of(context).primaryColor, 99 | size: getProportionateScreenWidth(26), 100 | ), 101 | onPressed: () => ThemeService().changeThemeMode(), 102 | ), 103 | ), 104 | Positioned( 105 | top: 35 / 2 + 8, 106 | left: 0, 107 | right: 0, 108 | child: Icon( 109 | Icons.power_settings_new_sharp, 110 | color: Colors.white, 111 | size: width / 16.0, 112 | ), 113 | ), 114 | Positioned( 115 | bottom: 35 / 2 + 6, 116 | left: 0, 117 | right: 0, 118 | child: Icon( 119 | Icons.notifications_active_outlined, 120 | color: Colors.white, 121 | size: width / 16.0, 122 | ), 123 | ), 124 | ], 125 | ), 126 | const Spacer(flex: 2), 127 | Row( 128 | mainAxisAlignment: MainAxisAlignment.spaceAround, 129 | children: [ 130 | _buildBottomTime( 131 | context, 132 | '00:00', 133 | 'Bedtime', 134 | Icons.power_settings_new_sharp, 135 | ), 136 | _buildBottomTime( 137 | context, 138 | '06:00', 139 | 'Wakeup', 140 | Icons.notifications_active, 141 | ), 142 | ], 143 | ), 144 | const Spacer(flex: 3), 145 | ], 146 | ); 147 | } 148 | 149 | Widget _buildTitle(context) { 150 | return Padding( 151 | padding: const EdgeInsets.only(left: 16.0, right: 8.0), 152 | child: Row( 153 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 154 | children: [ 155 | Text( 156 | 'Bedtime Schedule', 157 | style: TextStyle( 158 | color: Theme.of(context).colorScheme.secondary, 159 | fontFamily: 'Lato', 160 | fontSize: width / 21.5, 161 | fontWeight: FontWeight.w600, 162 | ), 163 | ), 164 | Switch( 165 | activeColor: Theme.of(context).primaryColor, 166 | value: isOn, 167 | onChanged: (val) { 168 | toggle(); 169 | }, 170 | ), 171 | ], 172 | ), 173 | ); 174 | } 175 | 176 | Widget _buildClock(context, dateTime) { 177 | return Padding( 178 | padding: EdgeInsets.symmetric( 179 | horizontal: 35 + width * .05, 180 | ), 181 | child: AspectRatio( 182 | aspectRatio: 1, 183 | child: Container( 184 | decoration: BoxDecoration( 185 | color: Theme.of(context).colorScheme.surface, 186 | shape: BoxShape.circle, 187 | boxShadow: [ 188 | BoxShadow( 189 | color: kShadowColor.withOpacity(0.14), 190 | blurRadius: 64, 191 | ), 192 | ], 193 | ), 194 | child: Transform.rotate( 195 | angle: -pi / 2, 196 | child: CustomPaint( 197 | painter: ClockPainter(context, dateTime), 198 | ), 199 | ), 200 | ), 201 | ), 202 | ); 203 | } 204 | 205 | Widget _buildBottomTime(context, time, title, icon) { 206 | return Column( 207 | children: [ 208 | Text( 209 | time, 210 | style: TextStyle( 211 | color: Theme.of(context).textTheme.bodyLarge?.color, 212 | fontFamily: 'Lato', 213 | fontSize: width / 12.5, 214 | fontWeight: FontWeight.w600, 215 | ), 216 | ), 217 | const SizedBox(height: 8.0), 218 | Text( 219 | title, 220 | style: TextStyle( 221 | color: Theme.of(context).primaryColor, 222 | fontFamily: 'Lato', 223 | fontSize: width / 25.0, 224 | fontWeight: FontWeight.w600, 225 | ), 226 | ), 227 | const SizedBox(height: 6.0), 228 | Icon( 229 | icon, 230 | color: Theme.of(context).primaryColor, 231 | size: width / 14.0, 232 | ), 233 | ], 234 | ); 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /lib/src/pages/bedtime/widgets/pick_day.dart: -------------------------------------------------------------------------------- 1 | // Package imports: 2 | import 'package:flutter_neumorphic_plus/flutter_neumorphic.dart'; 3 | 4 | // Project imports: 5 | import 'package:analog_clock/src/public/constants.dart'; 6 | 7 | class PickDay extends StatefulWidget { 8 | const PickDay({super.key}); 9 | 10 | @override 11 | State createState() => _PickDayState(); 12 | } 13 | 14 | class _PickDayState extends State { 15 | List values = ['M', 'T', 'W', 'T', 'F', 'S', 'S']; 16 | List picked = List.generate(7, (index) => index < 3); 17 | 18 | pickDay(index) { 19 | setState(() { 20 | picked[index] = !picked[index]; 21 | }); 22 | } 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | return Container( 27 | padding: const EdgeInsets.symmetric(horizontal: 10.0), 28 | child: Row( 29 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 30 | children: [ 31 | _buildAction(context, 0), 32 | _buildAction(context, 1), 33 | _buildAction(context, 2), 34 | _buildAction(context, 3), 35 | _buildAction(context, 4), 36 | _buildAction(context, 5), 37 | _buildAction(context, 6), 38 | ], 39 | ), 40 | ); 41 | } 42 | 43 | Widget _buildAction(context, index) { 44 | return Container( 45 | height: width / 8, 46 | width: width / 8, 47 | alignment: Alignment.center, 48 | child: NeumorphicButton( 49 | onPressed: () => pickDay(index), 50 | style: NeumorphicStyle( 51 | shape: NeumorphicShape.convex, 52 | boxShape: const NeumorphicBoxShape.circle(), 53 | depth: 6.0, 54 | intensity: .25, 55 | color: picked[index] 56 | ? Theme.of(context).primaryColor 57 | : Theme.of(context).colorScheme.secondary, 58 | ), 59 | child: Text( 60 | values[index], 61 | style: Theme.of(context).textTheme.displayMedium?.copyWith( 62 | fontSize: width / 23.0, 63 | fontFamily: 'Lato', 64 | fontWeight: FontWeight.w400, 65 | ), 66 | ), 67 | ), 68 | ); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/src/pages/countdown/controllers/count_down_controller.dart: -------------------------------------------------------------------------------- 1 | // Dart imports: 2 | import 'dart:async'; 3 | 4 | // Package imports: 5 | import 'package:get/get.dart'; 6 | 7 | class CountDownController extends GetxController { 8 | StreamController currentTime = 9 | StreamController.broadcast(); 10 | int hour = 0, minute = 0, second = 0; 11 | double percent = .0; 12 | bool isRunning = false; 13 | DateTime dateTime = DateTime(0, 0, 0); 14 | Timer? timer; 15 | 16 | formatTime(input) { 17 | if (input < 10) { 18 | return '0$input'; 19 | } 20 | return input.toString(); 21 | } 22 | 23 | play() { 24 | if ([hour, minute, second].join() != '000') { 25 | isRunning ? stopTimer() : startTimer(); 26 | } 27 | } 28 | 29 | updateTime(h, m, s) { 30 | hour = h; 31 | minute = m; 32 | second = s; 33 | dateTime = DateTime(0, 0, 0, h, m, s); 34 | currentTime.add(dateTime); 35 | update(); 36 | } 37 | 38 | startTimer() { 39 | isRunning = true; 40 | update(); 41 | timer = Timer.periodic(const Duration(seconds: 1), (timer) { 42 | if (dateTime.hour == 0 && dateTime.minute == 0 && dateTime.second == 0) { 43 | stopTimer(); 44 | } else { 45 | dateTime = dateTime.subtract(const Duration(seconds: 1)); 46 | currentTime.add(dateTime); 47 | percent = 1.0 - 48 | ((dateTime.hour * 3600 + dateTime.minute * 60 + dateTime.second) / 49 | (hour * 3600 + minute * 60 + second)); 50 | update(); 51 | } 52 | }); 53 | } 54 | 55 | stopTimer() { 56 | timer?.cancel(); 57 | isRunning = false; 58 | update(); 59 | } 60 | 61 | resetTime() { 62 | dateTime = DateTime(0, 0, 0, hour, minute, second); 63 | timer?.cancel(); 64 | currentTime.add(dateTime); 65 | isRunning = false; 66 | update(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/src/pages/countdown/count_down_page.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | // Package imports: 5 | import 'package:get/get.dart'; 6 | import 'package:percent_indicator/circular_percent_indicator.dart'; 7 | 8 | // Project imports: 9 | import 'package:analog_clock/src/pages/countdown/controllers/count_down_controller.dart'; 10 | import 'package:analog_clock/src/pages/countdown/widgets/count_controll.dart'; 11 | import 'package:analog_clock/src/pages/countdown/widgets/pick_time.dart'; 12 | import 'package:analog_clock/src/public/constants.dart'; 13 | 14 | class CountDownPage extends StatefulWidget { 15 | const CountDownPage({super.key}); 16 | 17 | @override 18 | State createState() => _CountDownPageState(); 19 | } 20 | 21 | class _CountDownPageState extends State { 22 | final countDownController = Get.put(CountDownController()); 23 | 24 | @override 25 | void initState() { 26 | super.initState(); 27 | countDownController.updateTime(0, 0, 0); 28 | } 29 | 30 | @override 31 | void dispose() { 32 | countDownController.timer?.cancel(); 33 | super.dispose(); 34 | } 35 | 36 | @override 37 | Widget build(BuildContext context) { 38 | return Scaffold( 39 | body: SizedBox( 40 | child: Column( 41 | children: [ 42 | const Spacer(flex: 4), 43 | StreamBuilder( 44 | stream: countDownController.currentTime.stream, 45 | builder: (context, snapshot) { 46 | if (!snapshot.hasData) { 47 | return _buildClock(DateTime(0, 0, 0)); 48 | } 49 | 50 | return _buildClock(snapshot.data); 51 | }, 52 | ), 53 | const Spacer(flex: 3), 54 | const PickTime(), 55 | const Spacer(flex: 2), 56 | const CountControll(), 57 | const Spacer(flex: 8), 58 | ], 59 | ), 60 | ), 61 | ); 62 | } 63 | 64 | Widget _buildClock(dateTime) { 65 | return CircularPercentIndicator( 66 | radius: width * .45, 67 | lineWidth: 60.0, 68 | percent: countDownController.percent, 69 | circularStrokeCap: CircularStrokeCap.round, 70 | backgroundColor: Theme.of(context).colorScheme.secondary, 71 | linearGradient: LinearGradient( 72 | begin: Alignment.topLeft, 73 | end: Alignment.bottomCenter, 74 | colors: [ 75 | Theme.of(context).primaryColor, 76 | Theme.of(context).secondaryHeaderColor, 77 | ], 78 | tileMode: TileMode.mirror, 79 | ), 80 | animationDuration: 1000, 81 | animateFromLastPercent: true, 82 | rotateLinearGradient: true, 83 | center: Center( 84 | child: Text( 85 | '${countDownController.formatTime(dateTime.hour)}:${countDownController.formatTime(dateTime.minute)}:${countDownController.formatTime(dateTime.second)}', 86 | style: Theme.of(context).textTheme.displayLarge?.copyWith( 87 | fontSize: width / 10.0, 88 | fontFamily: 'Lato', 89 | fontWeight: FontWeight.w400, 90 | ), 91 | ), 92 | ), 93 | ); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /lib/src/pages/countdown/widgets/count_controll.dart: -------------------------------------------------------------------------------- 1 | // Package imports: 2 | import 'package:flutter_neumorphic_plus/flutter_neumorphic.dart'; 3 | import 'package:get/get.dart'; 4 | 5 | // Project imports: 6 | import 'package:analog_clock/src/pages/countdown/controllers/count_down_controller.dart'; 7 | import 'package:analog_clock/src/public/constants.dart'; 8 | 9 | class CountControll extends StatefulWidget { 10 | const CountControll({super.key}); 11 | 12 | @override 13 | State createState() => _CountControllState(); 14 | } 15 | 16 | class _CountControllState extends State { 17 | final countDownController = Get.put(CountDownController()); 18 | @override 19 | Widget build(BuildContext context) { 20 | return GetBuilder( 21 | builder: (_) => Container( 22 | padding: const EdgeInsets.symmetric(horizontal: 24.0), 23 | child: Row( 24 | mainAxisAlignment: MainAxisAlignment.spaceAround, 25 | children: [ 26 | IconButton( 27 | onPressed: () => _.resetTime(), 28 | icon: Icon( 29 | Icons.refresh, 30 | size: width / 12.0, 31 | ), 32 | ), 33 | NeumorphicButton( 34 | onPressed: () => _.play(), 35 | duration: const Duration(milliseconds: 200), 36 | padding: EdgeInsets.all(width / 22.5), 37 | style: NeumorphicStyle( 38 | shape: NeumorphicShape.convex, 39 | boxShape: const NeumorphicBoxShape.circle(), 40 | depth: 10.0, 41 | intensity: .18, 42 | surfaceIntensity: .5, 43 | color: Theme.of(context).scaffoldBackgroundColor, 44 | ), 45 | child: Icon( 46 | _.isRunning ? Icons.pause : Icons.play_arrow, 47 | size: width / 12.0, 48 | ), 49 | ), 50 | IconButton( 51 | onPressed: () {}, 52 | icon: Icon( 53 | Icons.notifications, 54 | size: width / 12.0, 55 | ), 56 | ), 57 | ], 58 | ), 59 | ), 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/src/pages/countdown/widgets/pick_time.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | // Package imports: 5 | import 'package:get/get.dart'; 6 | 7 | // Project imports: 8 | import 'package:analog_clock/src/pages/countdown/controllers/count_down_controller.dart'; 9 | import 'package:analog_clock/src/public/constants.dart'; 10 | 11 | class PickTime extends StatefulWidget { 12 | const PickTime({super.key}); 13 | 14 | @override 15 | State createState() => _PickTimeState(); 16 | } 17 | 18 | class _PickTimeState extends State { 19 | List hours = List.generate(99, (index) => index); 20 | List minutes = List.generate(60, (index) => index); 21 | int hour = 0, minute = 0, second = 0; 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return SizedBox( 26 | height: height * .1, 27 | width: width, 28 | child: Row( 29 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 30 | children: [ 31 | _buildColumnTime('Hour', hours, hour), 32 | _buildColumnTime('Minute', minutes, minute), 33 | _buildColumnTime('Second', minutes, second), 34 | ], 35 | ), 36 | ); 37 | } 38 | 39 | Widget _buildColumnTime(title, List datas, value) { 40 | return SizedBox( 41 | width: width * .215, 42 | child: Column( 43 | children: [ 44 | Padding( 45 | padding: const EdgeInsets.only(left: 38.0), 46 | child: GetBuilder( 47 | builder: (_) => DropdownButtonHideUnderline( 48 | child: DropdownButtonFormField( 49 | icon: const Icon(null), 50 | iconEnabledColor: Colors.grey.shade800, 51 | decoration: const InputDecoration( 52 | border: InputBorder.none, 53 | ), 54 | value: 0, 55 | style: TextStyle( 56 | fontSize: width / 28.5, 57 | fontWeight: FontWeight.w600, 58 | color: Theme.of(context).iconTheme.color, 59 | fontFamily: 'Lato', 60 | ), 61 | items: datas.map((data) { 62 | return DropdownMenuItem( 63 | value: data, 64 | child: Text( 65 | data.toString(), 66 | style: TextStyle( 67 | fontSize: width / 28.5, 68 | fontWeight: FontWeight.w600, 69 | color: Theme.of(context).iconTheme.color, 70 | fontFamily: 'Lato', 71 | ), 72 | textAlign: TextAlign.end, 73 | ), 74 | ); 75 | }).toList(), 76 | onChanged: (val) { 77 | if (val == null || val is! int) return; 78 | 79 | setState(() { 80 | switch (title) { 81 | case "Hour": 82 | hour = val; 83 | break; 84 | case "Minute": 85 | minute = val; 86 | break; 87 | case "Second": 88 | second = val; 89 | break; 90 | } 91 | _.updateTime(hour, minute, second); 92 | }); 93 | }, 94 | ), 95 | ), 96 | ), 97 | ), 98 | const SizedBox(height: 12.0), 99 | Text( 100 | title, 101 | style: Theme.of(context).textTheme.displayLarge?.copyWith( 102 | fontSize: width / 26.0, 103 | fontWeight: FontWeight.w500, 104 | color: Theme.of(context).iconTheme.color, 105 | ), 106 | textAlign: TextAlign.center, 107 | ), 108 | ], 109 | ), 110 | ); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /lib/src/pages/global/controllers/clock_controller.dart: -------------------------------------------------------------------------------- 1 | // Dart imports: 2 | import 'dart:async'; 3 | 4 | // Flutter imports: 5 | import 'package:flutter/material.dart'; 6 | 7 | // Package imports: 8 | import 'package:get/get.dart'; 9 | 10 | class ClockController extends GetxController { 11 | StreamController currentDay = 12 | StreamController.broadcast(); 13 | Timer? timer; 14 | TimeOfDay timeOfDay = TimeOfDay.now(); 15 | String formatTime(int input) { 16 | if (input < 10) { 17 | return '0$input'; 18 | } 19 | return input.toString(); 20 | } 21 | 22 | startTimer() { 23 | timer = Timer.periodic(const Duration(seconds: 1), (timer) { 24 | currentDay.add(DateTime.now()); 25 | }); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/src/pages/global/global_page.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | // Package imports: 5 | import 'package:get/get.dart'; 6 | 7 | // Project imports: 8 | import 'package:analog_clock/src/pages/global/controllers/clock_controller.dart'; 9 | import 'package:analog_clock/src/pages/global/widgets/body.dart'; 10 | import 'package:analog_clock/src/public/size_config.dart'; 11 | 12 | class GlobalPage extends StatefulWidget { 13 | const GlobalPage({super.key}); 14 | 15 | @override 16 | State createState() => _GlobalPageState(); 17 | } 18 | 19 | class _GlobalPageState extends State { 20 | final clockController = Get.put(ClockController()); 21 | 22 | @override 23 | void initState() { 24 | super.initState(); 25 | clockController.startTimer(); 26 | } 27 | 28 | @override 29 | void dispose() { 30 | clockController.timer?.cancel(); 31 | super.dispose(); 32 | } 33 | 34 | @override 35 | Widget build(BuildContext context) { 36 | return Scaffold( 37 | body: StreamBuilder( 38 | stream: clockController.currentDay.stream, 39 | builder: (context, snapshot) { 40 | if (!snapshot.hasData) { 41 | return Body( 42 | dateTime: DateTime.now(), 43 | ); 44 | } 45 | 46 | return Body( 47 | dateTime: snapshot.data!, 48 | ); 49 | }, 50 | ), 51 | ); 52 | } 53 | 54 | Padding buildAddButton(BuildContext context) { 55 | return Padding( 56 | padding: 57 | EdgeInsets.symmetric(horizontal: getProportionateScreenWidth(10)), 58 | child: InkWell( 59 | onTap: () {}, 60 | child: Container( 61 | width: getProportionateScreenWidth(32), 62 | decoration: BoxDecoration( 63 | color: Theme.of(context).primaryColor, 64 | shape: BoxShape.circle, 65 | ), 66 | child: const Icon( 67 | Icons.add, 68 | color: Colors.white, 69 | ), 70 | ), 71 | ), 72 | ); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lib/src/pages/global/widgets/body.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | // Package imports: 5 | import 'package:intl/intl.dart'; 6 | 7 | // Project imports: 8 | import 'package:analog_clock/src/pages/global/widgets/clock.dart'; 9 | import 'package:analog_clock/src/pages/global/widgets/country_card.dart'; 10 | import 'package:analog_clock/src/pages/global/widgets/time_in_hour_and_minute.dart'; 11 | import 'package:analog_clock/src/public/constants.dart'; 12 | 13 | class Body extends StatelessWidget { 14 | final DateTime dateTime; 15 | const Body({super.key, required this.dateTime}); 16 | @override 17 | Widget build(BuildContext context) { 18 | final DateFormat format = DateFormat('hh:mm'); 19 | 20 | return SizedBox( 21 | width: double.infinity, 22 | child: Column( 23 | children: [ 24 | Text( 25 | "Ho Chi Minh, Viet Nam", 26 | style: Theme.of(context).textTheme.bodyLarge?.copyWith( 27 | fontFamily: 'Lato', 28 | fontWeight: FontWeight.w600, 29 | fontSize: width / 26.0, 30 | ), 31 | ), 32 | TimeInHourAndMinute( 33 | timeOfDay: TimeOfDay.fromDateTime(dateTime), 34 | showPeriod: true, 35 | ), 36 | const Spacer(), 37 | Clock(dateTime: dateTime), 38 | const Spacer(), 39 | SingleChildScrollView( 40 | scrollDirection: Axis.horizontal, 41 | child: Row( 42 | children: [ 43 | CountryCard( 44 | country: "New York, USA", 45 | timeZone: "UTC -5 | EST", 46 | iconSrc: "assets/icons/Liberty.svg", 47 | time: format.format( 48 | DateTime.now().subtract( 49 | const Duration(hours: 11), 50 | ), 51 | ), 52 | period: "PM", 53 | ), 54 | CountryCard( 55 | country: "Sydney, AU", 56 | timeZone: "UTC +10 | AEST", 57 | iconSrc: "assets/icons/Sydney.svg", 58 | time: format.format( 59 | DateTime.now().add( 60 | const Duration(hours: 3), 61 | ), 62 | ), 63 | period: "AM", 64 | ), 65 | CountryCard( 66 | country: "HongKong, CN", 67 | timeZone: "UTC +8 | EA", 68 | iconSrc: "assets/icons/Sydney.svg", 69 | time: format.format( 70 | DateTime.now().add( 71 | const Duration(hours: 1), 72 | ), 73 | ), 74 | period: "AM", 75 | ), 76 | const SizedBox(width: 12.0), 77 | ], 78 | ), 79 | ), 80 | const SizedBox(height: 32.0), 81 | ], 82 | ), 83 | ); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /lib/src/pages/global/widgets/clock.dart: -------------------------------------------------------------------------------- 1 | // Dart imports: 2 | import 'dart:math'; 3 | 4 | // Flutter imports: 5 | import 'package:flutter/material.dart'; 6 | 7 | // Package imports: 8 | import 'package:flutter_phosphor_icons/flutter_phosphor_icons.dart'; 9 | 10 | // Project imports: 11 | import 'package:analog_clock/src/pages/global/widgets/clock_painter.dart'; 12 | import 'package:analog_clock/src/public/constants.dart'; 13 | import 'package:analog_clock/src/public/size_config.dart'; 14 | import 'package:analog_clock/src/theme/theme_service.dart'; 15 | 16 | class Clock extends StatelessWidget { 17 | final DateTime dateTime; 18 | const Clock({super.key, required this.dateTime}); 19 | @override 20 | Widget build(BuildContext context) { 21 | return Stack( 22 | children: [ 23 | Padding( 24 | padding: 25 | EdgeInsets.symmetric(horizontal: getProportionateScreenWidth(20)), 26 | child: AspectRatio( 27 | aspectRatio: 1, 28 | child: Container( 29 | decoration: BoxDecoration( 30 | color: Theme.of(context).colorScheme.surface, 31 | shape: BoxShape.circle, 32 | boxShadow: [ 33 | BoxShadow( 34 | color: kShadowColor.withOpacity(0.14), 35 | blurRadius: 64, 36 | ), 37 | ], 38 | ), 39 | child: Transform.rotate( 40 | angle: -pi / 2, 41 | child: CustomPaint( 42 | painter: ClockPainter(context, dateTime), 43 | ), 44 | ), 45 | ), 46 | ), 47 | ), 48 | Positioned( 49 | top: height * .182, 50 | left: 0, 51 | right: 0, 52 | child: IconButton( 53 | icon: Icon( 54 | ThemeService().getThemeMode() == ThemeMode.dark 55 | ? PhosphorIcons.moon 56 | : PhosphorIcons.sun, 57 | color: Theme.of(context).primaryColor, 58 | size: getProportionateScreenWidth(26), 59 | ), 60 | onPressed: () => ThemeService().changeThemeMode(), 61 | ), 62 | ), 63 | ], 64 | ); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/src/pages/global/widgets/clock_painter.dart: -------------------------------------------------------------------------------- 1 | // Dart imports: 2 | import 'dart:math'; 3 | 4 | // Flutter imports: 5 | import 'package:flutter/material.dart'; 6 | 7 | class ClockPainter extends CustomPainter { 8 | final BuildContext context; 9 | final DateTime dateTime; 10 | 11 | ClockPainter(this.context, this.dateTime); 12 | 13 | List clockOffset = [ 14 | pi / 2, 15 | pi / 3, 16 | pi / 6, 17 | 0, 18 | -pi / 6, 19 | -pi / 3, 20 | -pi / 2, 21 | -2 * pi / 3, 22 | -5 * pi / 6, 23 | pi, 24 | 5 * pi / 6, 25 | 2 * pi / 3, 26 | ]; 27 | 28 | @override 29 | void paint(Canvas canvas, Size size) { 30 | final double centerX = size.width / 2; 31 | final double centerY = size.height / 2; 32 | final Offset center = Offset(centerX, centerY); 33 | 34 | // Minute Calculation 35 | final double minX = 36 | centerX + size.width * 0.335 * cos((dateTime.minute * 6) * pi / 180); 37 | final double minY = 38 | centerY + size.width * 0.335 * sin((dateTime.minute * 6) * pi / 180); 39 | 40 | //Minute Line 41 | canvas.drawLine( 42 | center, 43 | Offset(minX, minY), 44 | Paint() 45 | ..color = Theme.of(context).colorScheme.secondary 46 | ..style = PaintingStyle.stroke 47 | ..strokeWidth = 10, 48 | ); 49 | canvas.drawCircle( 50 | Offset(minX, minY), 51 | .1, 52 | Paint() 53 | ..color = Theme.of(context).colorScheme.secondary 54 | ..style = PaintingStyle.stroke 55 | ..strokeWidth = 10, 56 | ); 57 | 58 | // Hour Calculation 59 | // dateTime.hour * 30 because 360/12 = 30 60 | // dateTime.minute * 0.5 each minute we want to turn our hour line a little 61 | final double hourX = centerX + 62 | size.width * 63 | 0.25 * 64 | cos((dateTime.hour * 30 + dateTime.minute * 0.5) * pi / 180); 65 | final double hourY = centerY + 66 | size.width * 67 | 0.25 * 68 | sin((dateTime.hour * 30 + dateTime.minute * 0.5) * pi / 180); 69 | 70 | // hour Line 71 | canvas.drawLine( 72 | center, 73 | Offset(hourX, hourY), 74 | Paint() 75 | ..color = Theme.of(context).colorScheme.secondary 76 | ..style = PaintingStyle.stroke 77 | ..strokeWidth = 15.0, 78 | ); 79 | canvas.drawCircle( 80 | Offset(hourX, hourY), 81 | 1.5, 82 | Paint() 83 | ..color = Theme.of(context).colorScheme.secondary 84 | ..style = PaintingStyle.stroke 85 | ..strokeWidth = 10, 86 | ); 87 | 88 | // Second Calculation 89 | // size.width * 0.4 define our line height 90 | // dateTime.second * 6 because 360 / 60 = 6 91 | final double secondX = 92 | centerX + size.width * 0.4 * cos((dateTime.second * 6) * pi / 180); 93 | final double secondY = 94 | centerY + size.width * 0.4 * sin((dateTime.second * 6) * pi / 180); 95 | 96 | // Second Line 97 | canvas.drawLine( 98 | center, 99 | Offset(secondX, secondY), 100 | Paint()..color = Theme.of(context).primaryColor, 101 | ); 102 | 103 | // Draw minute 104 | for (int i = 1; i < 60; i++) { 105 | if ([5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55].contains(i) == false) { 106 | final double startX = 107 | centerX + size.width * 0.475 * cos((i * 6) * pi / 180); 108 | final double startY = 109 | centerY + size.width * 0.475 * sin((i * 6) * pi / 180); 110 | final double endX = 111 | centerX + size.width * 0.49 * cos((i * 6) * pi / 180); 112 | final double endY = 113 | centerY + size.width * 0.49 * sin((i * 6) * pi / 180); 114 | 115 | canvas.drawLine( 116 | Offset(startX, startY), 117 | Offset(endX, endY), 118 | Paint()..color = Theme.of(context).primaryColor, 119 | ); 120 | } 121 | } 122 | 123 | // Draw time, 124 | // 12h 125 | clockOffset.asMap().forEach((i, e) { 126 | final double hour12XText = 127 | centerX + size.width * .4 * cos(e); // * cos((12 * 30) * pi / 180); 128 | final double hour12YText = 129 | centerY - size.width * .4 * sin(e); // * sin((12) * pi / 180); 130 | 131 | final double hour12X = centerX + 132 | size.width * 133 | ([0, 3, 6, 9].contains(i) ? .45 : .475) * 134 | cos(e); // * cos((12 * 30) * pi / 180); 135 | final double hour12Y = centerY + 136 | size.width * 137 | ([0, 3, 6, 9].contains(i) ? .45 : .475) * 138 | sin(e); // * sin((12) * pi / 180); 139 | // canvas.drawCircle( 140 | // Offset(hour12Y, hour12X), 4, Paint()..color = Colors.black); 141 | 142 | final double hour12Xx = 143 | centerX + size.width * 0.49 * cos(e); // * cos((12 * 30) * pi / 180); 144 | final double hour12Yy = 145 | centerY + size.width * 0.49 * sin(e); // * sin((12) * pi / 180); 146 | // canvas.drawCircle( 147 | // Offset(hour12Yy, hour12Xx), 4, Paint()..color = Colors.amber); 148 | // 149 | final TextSpan span = TextSpan( 150 | style: TextStyle( 151 | color: [0, 3, 6, 9].contains(i) 152 | ? Theme.of(context).primaryColor 153 | : Theme.of(context).secondaryHeaderColor, 154 | fontFamily: 'Lato', 155 | fontSize: 156 | [0, 3, 6, 9].contains(i) ? size.width / 22.0 : size.width / 28.0, 157 | fontWeight: 158 | [0, 3, 6, 9].contains(i) ? FontWeight.w600 : FontWeight.w400, 159 | ), 160 | text: i == 0 ? '12' : i.toString(), 161 | ); 162 | final TextPainter tp = TextPainter( 163 | text: span, 164 | textAlign: TextAlign.left, 165 | textDirection: TextDirection.ltr, 166 | ); 167 | canvas.rotate(pi / 2); 168 | tp.layout(); 169 | tp.paint( 170 | canvas, 171 | Offset( 172 | hour12XText - (i == 0 ? 10.0 : 5.0), 173 | hour12YText - size.width - 8.0, 174 | ), 175 | ); 176 | canvas.rotate(-pi / 2); 177 | canvas.drawLine( 178 | Offset(hour12Y, hour12X), 179 | Offset(hour12Yy, hour12Xx), 180 | Paint() 181 | ..color = [0, 3, 6, 9].contains(i) 182 | ? Theme.of(context).primaryColor 183 | : Theme.of(context).secondaryHeaderColor 184 | ..style = PaintingStyle.stroke 185 | ..strokeWidth = [0, 3, 6, 9].contains(i) ? 2.5 : 1, 186 | ); 187 | }); 188 | 189 | // Center Dots 190 | final Paint dotPainter = Paint() 191 | ..color = Theme.of(context).primaryIconTheme.color ?? Colors.grey; 192 | canvas.drawCircle(center, 24, dotPainter); 193 | canvas.drawCircle( 194 | center, 195 | 23, 196 | Paint()..color = Theme.of(context).colorScheme.background, 197 | ); 198 | // canvas.drawCircle(center, 10, dotPainter); 199 | } 200 | 201 | @override 202 | bool shouldRepaint(CustomPainter oldDelegate) { 203 | return true; 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /lib/src/pages/global/widgets/country_card.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | // Package imports: 5 | import 'package:flutter_svg/flutter_svg.dart'; 6 | 7 | // Project imports: 8 | import 'package:analog_clock/src/public/size_config.dart'; 9 | 10 | class CountryCard extends StatelessWidget { 11 | final String country, timeZone, iconSrc, time, period; 12 | 13 | const CountryCard({ 14 | super.key, 15 | required this.country, 16 | required this.iconSrc, 17 | required this.period, 18 | required this.time, 19 | required this.timeZone, 20 | }); 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return Padding( 25 | padding: EdgeInsets.only(left: getProportionateScreenWidth(20)), 26 | child: SizedBox( 27 | width: getProportionateScreenWidth(233), 28 | child: AspectRatio( 29 | aspectRatio: 1.32, 30 | child: Container( 31 | padding: EdgeInsets.all(getProportionateScreenWidth(20)), 32 | decoration: BoxDecoration( 33 | borderRadius: BorderRadius.circular(10), 34 | border: Border.all( 35 | color: Theme.of(context).primaryIconTheme.color ?? Colors.black, 36 | ), 37 | ), 38 | child: Column( 39 | crossAxisAlignment: CrossAxisAlignment.start, 40 | children: [ 41 | Text( 42 | country, 43 | style: Theme.of(context) 44 | .textTheme 45 | .headlineMedium 46 | ?.copyWith(fontSize: getProportionateScreenWidth(16)), 47 | ), 48 | const SizedBox(height: 5), 49 | Text(timeZone), 50 | const Spacer(), 51 | Row( 52 | children: [ 53 | SvgPicture.asset( 54 | iconSrc, 55 | width: getProportionateScreenWidth(40), 56 | ), 57 | const Spacer(), 58 | Text( 59 | time, 60 | style: Theme.of(context).textTheme.headlineMedium, 61 | ), 62 | RotatedBox( 63 | quarterTurns: 3, 64 | child: Text(period), 65 | ), 66 | ], 67 | ), 68 | ], 69 | ), 70 | ), 71 | ), 72 | ), 73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lib/src/pages/global/widgets/time_in_hour_and_minute.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | // Package imports: 5 | import 'package:get/get.dart'; 6 | 7 | // Project imports: 8 | import 'package:analog_clock/src/pages/global/controllers/clock_controller.dart'; 9 | import 'package:analog_clock/src/public/size_config.dart'; 10 | 11 | class TimeInHourAndMinute extends StatelessWidget { 12 | final TimeOfDay timeOfDay; 13 | final bool showPeriod; 14 | final clockController = Get.put(ClockController()); 15 | 16 | TimeInHourAndMinute({ 17 | super.key, 18 | required this.timeOfDay, 19 | required this.showPeriod, 20 | }); 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | final String period = timeOfDay.period == DayPeriod.am ? "AM" : "PM"; 25 | return Row( 26 | mainAxisAlignment: MainAxisAlignment.center, 27 | children: [ 28 | Text( 29 | // if you use _timeOfDay.hour then it will show 20:10 like that 30 | // But we want 8:10 31 | "${clockController.formatTime(timeOfDay.hourOfPeriod)}:${clockController.formatTime(timeOfDay.minute)}", 32 | style: Theme.of(context).textTheme.displayLarge, 33 | ), 34 | showPeriod 35 | ? Row( 36 | children: [ 37 | const SizedBox(width: 5), 38 | RotatedBox( 39 | quarterTurns: 3, 40 | child: Text( 41 | period, 42 | style: 43 | TextStyle(fontSize: getProportionateScreenWidth(18)), 44 | ), 45 | ), 46 | ], 47 | ) 48 | : Container(), 49 | ], 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/src/pages/stopwatch/controllers/stop_watch_controller.dart: -------------------------------------------------------------------------------- 1 | // Dart imports: 2 | import 'dart:async'; 3 | 4 | // Flutter imports: 5 | import 'package:flutter/material.dart'; 6 | 7 | // Package imports: 8 | import 'package:get/get.dart'; 9 | 10 | class StopWatchController extends GetxController { 11 | StreamController currentTime = 12 | StreamController.broadcast(); 13 | ScrollController scrollController = ScrollController(); 14 | DateTime dateTime = DateTime(0, 0, 0); 15 | Timer? timer; 16 | double percent = .0; 17 | bool isRunning = false; 18 | List dateTimes = []; 19 | 20 | formatTime(input) { 21 | if (input < 10) { 22 | return '0$input'; 23 | } 24 | return input.toString(); 25 | } 26 | 27 | play() { 28 | isRunning ? stopTimer() : startTimer(); 29 | } 30 | 31 | updateTime(h, m, s) { 32 | currentTime.add(dateTime); 33 | } 34 | 35 | startTimer() { 36 | isRunning = true; 37 | update(); 38 | timer = Timer.periodic(const Duration(milliseconds: 17), (timer) { 39 | dateTime = dateTime.add(const Duration(milliseconds: 17)); 40 | currentTime.add(dateTime); 41 | percent = dateTime.second / 60.0; 42 | update(); 43 | }); 44 | } 45 | 46 | stopTimer() { 47 | timer?.cancel(); 48 | isRunning = false; 49 | update(); 50 | } 51 | 52 | saveCurrentTime() { 53 | if (isRunning) { 54 | dateTimes.insert(0, dateTime); 55 | scrollController.animateTo( 56 | 0.0, 57 | curve: Curves.easeOut, 58 | duration: const Duration(milliseconds: 300), 59 | ); 60 | update(); 61 | } 62 | } 63 | 64 | resetTime() { 65 | dateTime = DateTime(0, 0, 0); 66 | timer?.cancel(); 67 | currentTime.add(dateTime); 68 | dateTimes.clear(); 69 | dateTimes.add(DateTime(0, 0, 0)); 70 | percent = .0; 71 | isRunning = false; 72 | update(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lib/src/pages/stopwatch/stop_watch_page.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | // Package imports: 5 | import 'package:get/get.dart'; 6 | import 'package:percent_indicator/circular_percent_indicator.dart'; 7 | 8 | // Project imports: 9 | import 'package:analog_clock/src/pages/stopwatch/controllers/stop_watch_controller.dart'; 10 | import 'package:analog_clock/src/pages/stopwatch/widgets/list_stop_watch.dart'; 11 | import 'package:analog_clock/src/pages/stopwatch/widgets/stop_watch_controll.dart'; 12 | import 'package:analog_clock/src/public/constants.dart'; 13 | 14 | class StopWatchPage extends StatefulWidget { 15 | const StopWatchPage({super.key}); 16 | 17 | @override 18 | State createState() => _StopWatchPageState(); 19 | } 20 | 21 | class _StopWatchPageState extends State { 22 | final stopWatchController = Get.put(StopWatchController()); 23 | 24 | @override 25 | void initState() { 26 | super.initState(); 27 | stopWatchController.dateTimes.add(DateTime(0, 0, 0)); 28 | stopWatchController.updateTime(0, 0, 0); 29 | } 30 | 31 | @override 32 | void dispose() { 33 | stopWatchController.timer?.cancel(); 34 | super.dispose(); 35 | } 36 | 37 | @override 38 | Widget build(BuildContext context) { 39 | return Scaffold( 40 | body: Column( 41 | children: [ 42 | const Spacer(), 43 | StreamBuilder( 44 | stream: stopWatchController.currentTime.stream, 45 | builder: (context, snapshot) { 46 | if (!snapshot.hasData) { 47 | return _buildClock( 48 | DateTime(0, 0, 0), 49 | DateTime(0, 0, 0), 50 | ); 51 | } 52 | 53 | return _buildClock( 54 | snapshot.data!, 55 | stopWatchController.dateTimes[0], 56 | ); 57 | }, 58 | ), 59 | const Spacer(), 60 | SizedBox( 61 | height: height * .3, 62 | child: const ListStopWatch(), 63 | ), 64 | const Spacer(), 65 | const StopWatchControll(), 66 | const Spacer(flex: 3), 67 | ], 68 | ), 69 | ); 70 | } 71 | 72 | Widget _buildClock(DateTime dateTime, DateTime dateTimePrevious) { 73 | return CircularPercentIndicator( 74 | radius: width * .45, 75 | lineWidth: 60.0, 76 | percent: stopWatchController.percent, 77 | circularStrokeCap: CircularStrokeCap.round, 78 | backgroundColor: Theme.of(context).colorScheme.secondary, 79 | linearGradient: LinearGradient( 80 | begin: Alignment.topLeft, 81 | end: Alignment.bottomCenter, 82 | colors: [ 83 | Theme.of(context).primaryColor, 84 | Theme.of(context).secondaryHeaderColor, 85 | ], 86 | tileMode: TileMode.mirror, 87 | ), 88 | animationDuration: 1000, 89 | animateFromLastPercent: true, 90 | rotateLinearGradient: true, 91 | center: Column( 92 | mainAxisAlignment: MainAxisAlignment.center, 93 | children: [ 94 | RichText( 95 | text: TextSpan( 96 | children: [ 97 | TextSpan( 98 | text: 99 | '${stopWatchController.formatTime(dateTime.hour)}:${stopWatchController.formatTime(dateTime.minute)}:${stopWatchController.formatTime(dateTime.second)}', 100 | style: Theme.of(context).textTheme.displayLarge?.copyWith( 101 | fontSize: width / 12.0, 102 | fontFamily: 'Lato', 103 | fontWeight: FontWeight.w400, 104 | ), 105 | ), 106 | TextSpan( 107 | text: 108 | ':${stopWatchController.formatTime((dateTime.millisecond / 17).round())}', 109 | style: Theme.of(context).textTheme.displayLarge?.copyWith( 110 | fontSize: width / 18.0, 111 | fontFamily: 'Lato', 112 | fontWeight: FontWeight.w400, 113 | ), 114 | ), 115 | ], 116 | ), 117 | ), 118 | const SizedBox(height: 8.0), 119 | RichText( 120 | text: TextSpan( 121 | children: [ 122 | TextSpan( 123 | text: 124 | '${stopWatchController.formatTime(dateTimePrevious.hour)}:${stopWatchController.formatTime(dateTimePrevious.minute)}:${stopWatchController.formatTime(dateTimePrevious.second)}', 125 | style: Theme.of(context).textTheme.displayLarge?.copyWith( 126 | fontSize: width / 18.0, 127 | fontFamily: 'Lato', 128 | fontWeight: FontWeight.w400, 129 | ), 130 | ), 131 | TextSpan( 132 | text: 133 | ':${stopWatchController.formatTime((dateTimePrevious.millisecond / 17).round())}', 134 | style: Theme.of(context).textTheme.displayLarge?.copyWith( 135 | fontSize: width / 26.0, 136 | fontFamily: 'Lato', 137 | fontWeight: FontWeight.w400, 138 | ), 139 | ), 140 | ], 141 | ), 142 | ), 143 | ], 144 | ), 145 | ); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /lib/src/pages/stopwatch/widgets/list_stop_watch.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | // Package imports: 5 | import 'package:get/get.dart'; 6 | import 'package:intl/intl.dart'; 7 | 8 | // Project imports: 9 | import 'package:analog_clock/src/pages/stopwatch/controllers/stop_watch_controller.dart'; 10 | import 'package:analog_clock/src/public/constants.dart'; 11 | 12 | class ListStopWatch extends StatefulWidget { 13 | const ListStopWatch({super.key}); 14 | 15 | @override 16 | State createState() => _ListStopWatchState(); 17 | } 18 | 19 | class _ListStopWatchState extends State { 20 | DateFormat format = DateFormat('HH:mm:ss'); 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return GetBuilder( 25 | builder: (_) => ListView.builder( 26 | controller: _.scrollController, 27 | padding: EdgeInsets.zero, 28 | itemCount: _.dateTimes.length, 29 | itemBuilder: (context, index) { 30 | return index == _.dateTimes.length - 1 31 | ? Container() 32 | : Container( 33 | padding: const EdgeInsets.symmetric( 34 | horizontal: 24.0, 35 | vertical: 12.0, 36 | ), 37 | child: Row( 38 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 39 | children: [ 40 | Column( 41 | crossAxisAlignment: CrossAxisAlignment.start, 42 | mainAxisAlignment: MainAxisAlignment.center, 43 | children: [ 44 | Text( 45 | 'Stopwatch: ${_.dateTimes.length - index - 1}', 46 | style: Theme.of(context) 47 | .textTheme 48 | .displayLarge 49 | ?.copyWith( 50 | fontSize: width / 24.0, 51 | fontFamily: 'Lato', 52 | fontWeight: FontWeight.w600, 53 | ), 54 | ), 55 | const SizedBox(height: 8.0), 56 | Text( 57 | format.format(_.dateTimes[index]), 58 | style: Theme.of(context) 59 | .textTheme 60 | .displayLarge 61 | ?.copyWith( 62 | fontSize: width / 28.0, 63 | fontFamily: 'Lato', 64 | fontWeight: FontWeight.w400, 65 | ), 66 | ), 67 | ], 68 | ), 69 | Icon( 70 | Icons.alarm_add_sharp, 71 | size: width / 16.0, 72 | ), 73 | ], 74 | ), 75 | ); 76 | }, 77 | ), 78 | ); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/src/pages/stopwatch/widgets/stop_watch_controll.dart: -------------------------------------------------------------------------------- 1 | // Package imports: 2 | import 'package:flutter_neumorphic_plus/flutter_neumorphic.dart'; 3 | import 'package:get/get.dart'; 4 | 5 | // Project imports: 6 | import 'package:analog_clock/src/pages/stopwatch/controllers/stop_watch_controller.dart'; 7 | import 'package:analog_clock/src/public/constants.dart'; 8 | 9 | class StopWatchControll extends StatefulWidget { 10 | const StopWatchControll({super.key}); 11 | 12 | @override 13 | State createState() => _StopWatchControllState(); 14 | } 15 | 16 | class _StopWatchControllState extends State { 17 | final stopWatchController = Get.put(StopWatchController()); 18 | @override 19 | Widget build(BuildContext context) { 20 | return GetBuilder( 21 | builder: (_) => Container( 22 | padding: const EdgeInsets.symmetric(horizontal: 24.0), 23 | child: Row( 24 | mainAxisAlignment: MainAxisAlignment.spaceAround, 25 | children: [ 26 | IconButton( 27 | onPressed: () => _.resetTime(), 28 | icon: Icon( 29 | Icons.refresh, 30 | size: width / 12.0, 31 | ), 32 | ), 33 | NeumorphicButton( 34 | onPressed: () => _.play(), 35 | duration: const Duration(milliseconds: 200), 36 | padding: EdgeInsets.all(width / 22.5), 37 | style: NeumorphicStyle( 38 | shape: NeumorphicShape.convex, 39 | boxShape: const NeumorphicBoxShape.circle(), 40 | depth: 10.0, 41 | intensity: .18, 42 | surfaceIntensity: .5, 43 | color: Theme.of(context).scaffoldBackgroundColor, 44 | ), 45 | child: Icon( 46 | _.isRunning ? Icons.pause : Icons.play_arrow, 47 | size: width / 12.0, 48 | ), 49 | ), 50 | IconButton( 51 | onPressed: () => _.saveCurrentTime(), 52 | icon: Icon( 53 | Icons.add_alarm_rounded, 54 | size: width / 12.0, 55 | ), 56 | ), 57 | ], 58 | ), 59 | ), 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/src/pages/tab/tab_bar_layout.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | // Project imports: 5 | import 'package:analog_clock/src/pages/alarm/alarm_page.dart'; 6 | import 'package:analog_clock/src/pages/bedtime/bed_time_page.dart'; 7 | import 'package:analog_clock/src/pages/countdown/count_down_page.dart'; 8 | import 'package:analog_clock/src/pages/global/global_page.dart'; 9 | import 'package:analog_clock/src/pages/stopwatch/stop_watch_page.dart'; 10 | import 'package:analog_clock/src/public/constants.dart'; 11 | import 'package:analog_clock/src/public/size_config.dart'; 12 | 13 | class TabBarLayout extends StatefulWidget { 14 | const TabBarLayout({super.key}); 15 | 16 | @override 17 | State createState() => _TabBarLayoutState(); 18 | } 19 | 20 | class _TabBarLayoutState extends State 21 | with SingleTickerProviderStateMixin { 22 | late TabController _tabController; 23 | 24 | final _pages = [ 25 | const AlarmPage(), 26 | const GlobalPage(), 27 | const BedTimePage(), 28 | const StopWatchPage(), 29 | const CountDownPage(), 30 | ]; 31 | 32 | @override 33 | void initState() { 34 | super.initState(); 35 | _tabController = TabController( 36 | vsync: this, 37 | length: _pages.length, 38 | ); 39 | } 40 | 41 | @override 42 | Widget build(BuildContext context) { 43 | SizeConfig().init(context); 44 | return Scaffold( 45 | body: Column( 46 | children: [ 47 | SizedBox(height: height / 20.0), 48 | _buildTopBar(context), 49 | const SizedBox(height: 16.0), 50 | Expanded( 51 | child: TabBarView( 52 | controller: _tabController, 53 | children: _pages.map((tab) { 54 | return tab; 55 | }).toList(), 56 | ), 57 | ), 58 | ], 59 | ), 60 | ); 61 | } 62 | 63 | Widget _buildTopBar(context) { 64 | return TabBar( 65 | controller: _tabController, 66 | labelColor: Theme.of(context).primaryColor, 67 | indicatorColor: Colors.transparent, 68 | unselectedLabelColor: Theme.of(context).iconTheme.color, 69 | indicatorSize: TabBarIndicatorSize.tab, 70 | indicatorWeight: .1, 71 | labelStyle: TextStyle( 72 | fontWeight: FontWeight.bold, 73 | fontSize: width / 14.0, 74 | fontFamily: 'Raleway-Bold', 75 | ), 76 | unselectedLabelStyle: TextStyle( 77 | fontWeight: FontWeight.bold, 78 | fontSize: width / 14.0, 79 | fontFamily: 'Raleway-Bold', 80 | ), 81 | tabs: [ 82 | Tab( 83 | icon: Icon( 84 | Icons.alarm, 85 | size: width / 15.0, 86 | ), 87 | ), 88 | Tab( 89 | icon: Icon( 90 | Icons.watch_later_outlined, 91 | size: width / 15.0, 92 | ), 93 | ), 94 | Tab( 95 | icon: Icon( 96 | Icons.king_bed_outlined, 97 | size: width / 15.0, 98 | ), 99 | ), 100 | Tab( 101 | icon: Icon( 102 | Icons.alarm_on_outlined, 103 | size: width / 15.0, 104 | ), 105 | ), 106 | Tab( 107 | icon: Icon( 108 | Icons.hourglass_empty_sharp, 109 | size: width / 15.0, 110 | ), 111 | ), 112 | ], 113 | ); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /lib/src/public/constants.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | // Package imports: 5 | import 'package:get/get.dart'; 6 | 7 | // Colors 8 | const kPrimaryColor = Color(0xFFFF97B3); 9 | const kSecondaryColor = Color(0xFF1DA1F2); 10 | const kSecondaryLightColor = Color(0xFFE4E9F2); 11 | const kSecondaryDarkColor = Color(0xFF404040); 12 | const kAccentLightColor = Color(0xFFB3BFD7); 13 | const kAccentDarkColor = Color(0xFF4E4E4E); 14 | const kBackgroundDarkColor = Color(0xFF3A3A3A); 15 | const kSurfaceDarkColor = Color(0xFF222225); 16 | // Icon Colors 17 | const kAccentIconLightColor = Color(0xFFECEFF5); 18 | const kAccentIconDarkColor = Color(0xFF303030); 19 | const kPrimaryIconLightColor = Color(0xFFECEFF5); 20 | const kPrimaryIconDarkColor = Color(0xFF232323); 21 | // Text Colors 22 | const kBodyTextColorLight = Color(0xFFA1B0CA); 23 | const kBodyTextColorDark = Color(0xFF7C7C7C); 24 | const kTitleTextLightColor = Color(0xFF101112); 25 | const kTitleTextDarkColor = Colors.white; 26 | 27 | const kShadowColor = Color(0xFF364564); 28 | 29 | AppBarTheme appBarTheme = const AppBarTheme(color: Colors.transparent, elevation: 0); 30 | 31 | // Device Size 32 | var height = Get.height; 33 | var width = Get.width; 34 | -------------------------------------------------------------------------------- /lib/src/public/size_config.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | class SizeConfig { 5 | static late MediaQueryData _mediaQueryData; 6 | static late double screenWidth; 7 | static late double screenHeight; 8 | static late double defaultSize; 9 | static late Orientation orientation; 10 | 11 | void init(BuildContext context) { 12 | _mediaQueryData = MediaQuery.of(context); 13 | screenWidth = _mediaQueryData.size.width; 14 | screenHeight = _mediaQueryData.size.height; 15 | orientation = _mediaQueryData.orientation; 16 | } 17 | } 18 | 19 | // Get the proportionate height as per screen size 20 | double getProportionateScreenHeight(double inputHeight) { 21 | final double screenHeight = SizeConfig.screenHeight; 22 | // 896 is the layout height that designer use 23 | // or you can say iPhone 11 24 | return (inputHeight / 896.0) * screenHeight; 25 | } 26 | 27 | // Get the proportionate height as per screen size 28 | double getProportionateScreenWidth(double inputWidth) { 29 | final double screenWidth = SizeConfig.screenWidth; 30 | // 414 is the layout width that designer use 31 | return (inputWidth / 414.0) * screenWidth; 32 | } 33 | -------------------------------------------------------------------------------- /lib/src/routes/app_pages.dart: -------------------------------------------------------------------------------- 1 | // Package imports: 2 | import 'package:get/get.dart'; 3 | 4 | // Project imports: 5 | import 'package:analog_clock/src/app.dart'; 6 | 7 | part 'app_routes.dart'; 8 | 9 | // ignore: avoid_classes_with_only_static_members 10 | class AppPages { 11 | static const rootRoute = Routes.root; 12 | 13 | static final routes = [ 14 | GetPage( 15 | name: Routes.root, 16 | page: () => const App(), 17 | ), 18 | ]; 19 | } 20 | -------------------------------------------------------------------------------- /lib/src/routes/app_routes.dart: -------------------------------------------------------------------------------- 1 | part of 'app_pages.dart'; 2 | 3 | abstract class Routes { 4 | static const root = '/root'; 5 | } 6 | -------------------------------------------------------------------------------- /lib/src/shared/logger/logger_utils.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | class Logger { 5 | static void write(String text, {bool isError = false}) { 6 | Future.microtask(() => debugPrint('** $text. isError: [$isError]')); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /lib/src/theme/theme_service.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/services.dart'; 4 | 5 | // Package imports: 6 | import 'package:get/get.dart'; 7 | import 'package:get_storage/get_storage.dart'; 8 | 9 | class ThemeService { 10 | final _getStorage = GetStorage(); 11 | final storageKey = 'isDarkMode'; 12 | 13 | ThemeMode getThemeMode() { 14 | return isSavedDarkMode() ? ThemeMode.dark : ThemeMode.light; 15 | } 16 | 17 | bool isSavedDarkMode() { 18 | return _getStorage.read(storageKey) ?? false; 19 | } 20 | 21 | void saveThemeMode(bool isDarkMode) { 22 | _getStorage.write(storageKey, isDarkMode); 23 | } 24 | 25 | void changeThemeMode() { 26 | Get.changeThemeMode(isSavedDarkMode() ? ThemeMode.light : ThemeMode.dark); 27 | saveThemeMode(!isSavedDarkMode()); 28 | setBrighness(); 29 | } 30 | 31 | void initBrighness() { 32 | SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( 33 | statusBarColor: Colors.transparent, 34 | statusBarBrightness: Theme.of(Get.context!).brightness, 35 | statusBarIconBrightness: Theme.of(Get.context!).brightness, 36 | ),); 37 | } 38 | 39 | void setBrighness() { 40 | if (GetPlatform.isAndroid) { 41 | SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( 42 | statusBarColor: Colors.transparent, 43 | statusBarBrightness: Theme.of(Get.context!).brightness, 44 | statusBarIconBrightness: Theme.of(Get.context!).brightness, 45 | ),); 46 | } else { 47 | if (Theme.of(Get.context!).brightness == Brightness.light) { 48 | SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( 49 | statusBarColor: Colors.transparent, 50 | statusBarBrightness: Brightness.dark, 51 | statusBarIconBrightness: Brightness.dark, 52 | ),); 53 | } else { 54 | SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( 55 | statusBarColor: Colors.transparent, 56 | statusBarBrightness: Brightness.light, 57 | statusBarIconBrightness: Brightness.light, 58 | ),); 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/src/theme/themes.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | // Package imports: 5 | import 'package:google_fonts/google_fonts.dart'; 6 | 7 | // Project imports: 8 | import 'package:analog_clock/src/public/constants.dart'; 9 | 10 | class Themes { 11 | final lightTheme = ThemeData.light().copyWith( 12 | appBarTheme: appBarTheme, 13 | primaryColor: kPrimaryColor, 14 | secondaryHeaderColor: kSecondaryColor, 15 | scaffoldBackgroundColor: Colors.white, 16 | iconTheme: const IconThemeData(color: kBodyTextColorLight), 17 | primaryIconTheme: const IconThemeData(color: kPrimaryIconLightColor), 18 | textTheme: GoogleFonts.latoTextTheme().copyWith( 19 | bodyLarge: const TextStyle(color: kBodyTextColorLight), 20 | bodyMedium: const TextStyle(color: kBodyTextColorLight), 21 | headlineMedium: const TextStyle(color: kTitleTextLightColor, fontSize: 32), 22 | displayLarge: const TextStyle(color: kTitleTextLightColor, fontSize: 80), 23 | displayMedium: const TextStyle(color: kTitleTextDarkColor, fontSize: 80), 24 | ), 25 | brightness: Brightness.light, colorScheme: const ColorScheme.light( 26 | secondary: kSecondaryLightColor, 27 | // on light theme surface = Colors.white by default 28 | ).copyWith(secondary: kAccentLightColor).copyWith(background: Colors.white), 29 | ); 30 | final darkTheme = ThemeData.dark().copyWith( 31 | primaryColor: kPrimaryColor, 32 | secondaryHeaderColor: kSecondaryColor, 33 | scaffoldBackgroundColor: const Color(0xFF0D0C0E), 34 | appBarTheme: appBarTheme, 35 | iconTheme: const IconThemeData(color: kBodyTextColorDark), 36 | primaryIconTheme: const IconThemeData(color: kPrimaryIconDarkColor), 37 | textTheme: GoogleFonts.latoTextTheme().copyWith( 38 | bodyLarge: const TextStyle(color: kBodyTextColorDark), 39 | bodyMedium: const TextStyle(color: kBodyTextColorDark), 40 | headlineMedium: const TextStyle(color: kTitleTextDarkColor, fontSize: 32), 41 | displayLarge: const TextStyle(color: kTitleTextDarkColor, fontSize: 80), 42 | displayMedium: const TextStyle(color: kTitleTextDarkColor, fontSize: 80), 43 | ), 44 | brightness: Brightness.dark, colorScheme: const ColorScheme.light( 45 | secondary: kSecondaryDarkColor, 46 | surface: kSurfaceDarkColor, 47 | ).copyWith(secondary: kAccentDarkColor).copyWith(background: kBackgroundDarkColor), 48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | args: 5 | dependency: transitive 6 | description: 7 | name: args 8 | sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "2.4.2" 12 | async: 13 | dependency: transitive 14 | description: 15 | name: async 16 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "2.11.0" 20 | boolean_selector: 21 | dependency: transitive 22 | description: 23 | name: boolean_selector 24 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "2.1.1" 28 | characters: 29 | dependency: transitive 30 | description: 31 | name: characters 32 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" 33 | url: "https://pub.dev" 34 | source: hosted 35 | version: "1.3.0" 36 | clock: 37 | dependency: transitive 38 | description: 39 | name: clock 40 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf 41 | url: "https://pub.dev" 42 | source: hosted 43 | version: "1.1.1" 44 | collection: 45 | dependency: transitive 46 | description: 47 | name: collection 48 | sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 49 | url: "https://pub.dev" 50 | source: hosted 51 | version: "1.17.2" 52 | crypto: 53 | dependency: transitive 54 | description: 55 | name: crypto 56 | sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab 57 | url: "https://pub.dev" 58 | source: hosted 59 | version: "3.0.3" 60 | cupertino_icons: 61 | dependency: "direct main" 62 | description: 63 | name: cupertino_icons 64 | sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d 65 | url: "https://pub.dev" 66 | source: hosted 67 | version: "1.0.6" 68 | fake_async: 69 | dependency: transitive 70 | description: 71 | name: fake_async 72 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" 73 | url: "https://pub.dev" 74 | source: hosted 75 | version: "1.3.1" 76 | ffi: 77 | dependency: transitive 78 | description: 79 | name: ffi 80 | sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" 81 | url: "https://pub.dev" 82 | source: hosted 83 | version: "2.1.0" 84 | flutter: 85 | dependency: "direct main" 86 | description: flutter 87 | source: sdk 88 | version: "0.0.0" 89 | flutter_lints: 90 | dependency: "direct dev" 91 | description: 92 | name: flutter_lints 93 | sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 94 | url: "https://pub.dev" 95 | source: hosted 96 | version: "2.0.3" 97 | flutter_neumorphic_plus: 98 | dependency: "direct main" 99 | description: 100 | name: flutter_neumorphic_plus 101 | sha256: fc702c6414c6d9e1498f84ca124b59104e0dcbac59f92a47c5776e023d985e63 102 | url: "https://pub.dev" 103 | source: hosted 104 | version: "3.3.0" 105 | flutter_phosphor_icons: 106 | dependency: "direct main" 107 | description: 108 | name: flutter_phosphor_icons 109 | sha256: "60a970f5fb5b22c93cea61e96c2cd922612dd2d4a359fa441e4d31c2e3b3c664" 110 | url: "https://pub.dev" 111 | source: hosted 112 | version: "0.0.1+6" 113 | flutter_svg: 114 | dependency: "direct main" 115 | description: 116 | name: flutter_svg 117 | sha256: "8c5d68a82add3ca76d792f058b186a0599414f279f00ece4830b9b231b570338" 118 | url: "https://pub.dev" 119 | source: hosted 120 | version: "2.0.7" 121 | flutter_test: 122 | dependency: "direct dev" 123 | description: flutter 124 | source: sdk 125 | version: "0.0.0" 126 | get: 127 | dependency: "direct main" 128 | description: 129 | name: get 130 | sha256: e4e7335ede17452b391ed3b2ede016545706c01a02292a6c97619705e7d2a85e 131 | url: "https://pub.dev" 132 | source: hosted 133 | version: "4.6.6" 134 | get_storage: 135 | dependency: "direct main" 136 | description: 137 | name: get_storage 138 | sha256: "39db1fffe779d0c22b3a744376e86febe4ade43bf65e06eab5af707dc84185a2" 139 | url: "https://pub.dev" 140 | source: hosted 141 | version: "2.1.1" 142 | google_fonts: 143 | dependency: "direct main" 144 | description: 145 | name: google_fonts 146 | sha256: f0b8d115a13ecf827013ec9fc883390ccc0e87a96ed5347a3114cac177ef18e8 147 | url: "https://pub.dev" 148 | source: hosted 149 | version: "6.1.0" 150 | http: 151 | dependency: transitive 152 | description: 153 | name: http 154 | sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" 155 | url: "https://pub.dev" 156 | source: hosted 157 | version: "1.1.0" 158 | http_parser: 159 | dependency: transitive 160 | description: 161 | name: http_parser 162 | sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" 163 | url: "https://pub.dev" 164 | source: hosted 165 | version: "4.0.2" 166 | import_sorter: 167 | dependency: "direct dev" 168 | description: 169 | name: import_sorter 170 | sha256: eb15738ccead84e62c31e0208ea4e3104415efcd4972b86906ca64a1187d0836 171 | url: "https://pub.dev" 172 | source: hosted 173 | version: "4.6.0" 174 | intl: 175 | dependency: "direct main" 176 | description: 177 | name: intl 178 | sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" 179 | url: "https://pub.dev" 180 | source: hosted 181 | version: "0.18.1" 182 | lints: 183 | dependency: transitive 184 | description: 185 | name: lints 186 | sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" 187 | url: "https://pub.dev" 188 | source: hosted 189 | version: "2.1.1" 190 | matcher: 191 | dependency: transitive 192 | description: 193 | name: matcher 194 | sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" 195 | url: "https://pub.dev" 196 | source: hosted 197 | version: "0.12.16" 198 | material_color_utilities: 199 | dependency: transitive 200 | description: 201 | name: material_color_utilities 202 | sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" 203 | url: "https://pub.dev" 204 | source: hosted 205 | version: "0.5.0" 206 | meta: 207 | dependency: transitive 208 | description: 209 | name: meta 210 | sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" 211 | url: "https://pub.dev" 212 | source: hosted 213 | version: "1.9.1" 214 | nested: 215 | dependency: transitive 216 | description: 217 | name: nested 218 | sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" 219 | url: "https://pub.dev" 220 | source: hosted 221 | version: "1.0.0" 222 | path: 223 | dependency: transitive 224 | description: 225 | name: path 226 | sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" 227 | url: "https://pub.dev" 228 | source: hosted 229 | version: "1.8.3" 230 | path_parsing: 231 | dependency: transitive 232 | description: 233 | name: path_parsing 234 | sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf 235 | url: "https://pub.dev" 236 | source: hosted 237 | version: "1.0.1" 238 | path_provider: 239 | dependency: transitive 240 | description: 241 | name: path_provider 242 | sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa 243 | url: "https://pub.dev" 244 | source: hosted 245 | version: "2.1.1" 246 | path_provider_android: 247 | dependency: transitive 248 | description: 249 | name: path_provider_android 250 | sha256: "6b8b19bd80da4f11ce91b2d1fb931f3006911477cec227cce23d3253d80df3f1" 251 | url: "https://pub.dev" 252 | source: hosted 253 | version: "2.2.0" 254 | path_provider_foundation: 255 | dependency: transitive 256 | description: 257 | name: path_provider_foundation 258 | sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" 259 | url: "https://pub.dev" 260 | source: hosted 261 | version: "2.3.1" 262 | path_provider_linux: 263 | dependency: transitive 264 | description: 265 | name: path_provider_linux 266 | sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 267 | url: "https://pub.dev" 268 | source: hosted 269 | version: "2.2.1" 270 | path_provider_platform_interface: 271 | dependency: transitive 272 | description: 273 | name: path_provider_platform_interface 274 | sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" 275 | url: "https://pub.dev" 276 | source: hosted 277 | version: "2.1.1" 278 | path_provider_windows: 279 | dependency: transitive 280 | description: 281 | name: path_provider_windows 282 | sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" 283 | url: "https://pub.dev" 284 | source: hosted 285 | version: "2.2.1" 286 | percent_indicator: 287 | dependency: "direct main" 288 | description: 289 | name: percent_indicator 290 | sha256: c37099ad833a883c9d71782321cb65c3a848c21b6939b6185f0ff6640d05814c 291 | url: "https://pub.dev" 292 | source: hosted 293 | version: "4.2.3" 294 | petitparser: 295 | dependency: transitive 296 | description: 297 | name: petitparser 298 | sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 299 | url: "https://pub.dev" 300 | source: hosted 301 | version: "5.4.0" 302 | platform: 303 | dependency: transitive 304 | description: 305 | name: platform 306 | sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59" 307 | url: "https://pub.dev" 308 | source: hosted 309 | version: "3.1.3" 310 | plugin_platform_interface: 311 | dependency: transitive 312 | description: 313 | name: plugin_platform_interface 314 | sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d 315 | url: "https://pub.dev" 316 | source: hosted 317 | version: "2.1.6" 318 | provider: 319 | dependency: "direct main" 320 | description: 321 | name: provider 322 | sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f 323 | url: "https://pub.dev" 324 | source: hosted 325 | version: "6.0.5" 326 | sky_engine: 327 | dependency: transitive 328 | description: flutter 329 | source: sdk 330 | version: "0.0.99" 331 | source_span: 332 | dependency: transitive 333 | description: 334 | name: source_span 335 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" 336 | url: "https://pub.dev" 337 | source: hosted 338 | version: "1.10.0" 339 | stack_trace: 340 | dependency: transitive 341 | description: 342 | name: stack_trace 343 | sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 344 | url: "https://pub.dev" 345 | source: hosted 346 | version: "1.11.0" 347 | stream_channel: 348 | dependency: transitive 349 | description: 350 | name: stream_channel 351 | sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" 352 | url: "https://pub.dev" 353 | source: hosted 354 | version: "2.1.1" 355 | string_scanner: 356 | dependency: transitive 357 | description: 358 | name: string_scanner 359 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" 360 | url: "https://pub.dev" 361 | source: hosted 362 | version: "1.2.0" 363 | term_glyph: 364 | dependency: transitive 365 | description: 366 | name: term_glyph 367 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 368 | url: "https://pub.dev" 369 | source: hosted 370 | version: "1.2.1" 371 | test_api: 372 | dependency: transitive 373 | description: 374 | name: test_api 375 | sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" 376 | url: "https://pub.dev" 377 | source: hosted 378 | version: "0.6.0" 379 | tint: 380 | dependency: transitive 381 | description: 382 | name: tint 383 | sha256: "9652d9a589f4536d5e392cf790263d120474f15da3cf1bee7f1fdb31b4de5f46" 384 | url: "https://pub.dev" 385 | source: hosted 386 | version: "2.0.1" 387 | typed_data: 388 | dependency: transitive 389 | description: 390 | name: typed_data 391 | sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c 392 | url: "https://pub.dev" 393 | source: hosted 394 | version: "1.3.2" 395 | vector_graphics: 396 | dependency: transitive 397 | description: 398 | name: vector_graphics 399 | sha256: b16dadf7eb610e20da044c141b4a0199a5e8082ca21daba68322756f953ce714 400 | url: "https://pub.dev" 401 | source: hosted 402 | version: "1.1.9" 403 | vector_graphics_codec: 404 | dependency: transitive 405 | description: 406 | name: vector_graphics_codec 407 | sha256: a4b01403d5c613db115e30e71eca33f7e9e09f2d3c52c3fb84e16333ecddc539 408 | url: "https://pub.dev" 409 | source: hosted 410 | version: "1.1.9" 411 | vector_graphics_compiler: 412 | dependency: transitive 413 | description: 414 | name: vector_graphics_compiler 415 | sha256: d26c0e2f237476426523eb25512e4c09fa27c6d33ed659a0e69d79e20b5dc47f 416 | url: "https://pub.dev" 417 | source: hosted 418 | version: "1.1.9" 419 | vector_math: 420 | dependency: transitive 421 | description: 422 | name: vector_math 423 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 424 | url: "https://pub.dev" 425 | source: hosted 426 | version: "2.1.4" 427 | web: 428 | dependency: transitive 429 | description: 430 | name: web 431 | sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 432 | url: "https://pub.dev" 433 | source: hosted 434 | version: "0.1.4-beta" 435 | win32: 436 | dependency: transitive 437 | description: 438 | name: win32 439 | sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3" 440 | url: "https://pub.dev" 441 | source: hosted 442 | version: "5.0.9" 443 | xdg_directories: 444 | dependency: transitive 445 | description: 446 | name: xdg_directories 447 | sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2" 448 | url: "https://pub.dev" 449 | source: hosted 450 | version: "1.0.3" 451 | xml: 452 | dependency: transitive 453 | description: 454 | name: xml 455 | sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" 456 | url: "https://pub.dev" 457 | source: hosted 458 | version: "6.3.0" 459 | yaml: 460 | dependency: transitive 461 | description: 462 | name: yaml 463 | sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" 464 | url: "https://pub.dev" 465 | source: hosted 466 | version: "3.1.2" 467 | sdks: 468 | dart: ">=3.1.2 <4.0.0" 469 | flutter: ">=3.13.5" 470 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: analog_clock 2 | description: A new Flutter project. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `pub publish`. This is preferred for private packages. 6 | publish_to: "none" # Remove this line if you wish to publish to pub.dev 7 | 8 | # The following defines the version and build number for your application. 9 | # A version number is three numbers separated by dots, like 1.2.43 10 | # followed by an optional build number separated by a +. 11 | # Both the version and the builder number may be overridden in flutter 12 | # build by specifying --build-name and --build-number, respectively. 13 | # In Android, build-name is used as versionName while build-number used as versionCode. 14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 16 | # Read more about iOS versioning at 17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 18 | version: 1.0.0+1 19 | 20 | environment: 21 | sdk: ">=3.1.2 <4.0.0" 22 | flutter: ">=3.13.5" 23 | 24 | dependencies: 25 | flutter: 26 | sdk: flutter 27 | 28 | # The following adds the Cupertino Icons font to your application. 29 | # Use with the CupertinoIcons class for iOS style icons. 30 | cupertino_icons: ^1.0.6 31 | google_fonts: ^6.1.0 # it helps us to use google fonts on our app 32 | flutter_svg: ^2.0.7 # it helps us to use SVG in our app 33 | provider: ^6.0.5 # Our state management package 34 | get: ^4.6.6 35 | get_storage: ^2.1.1 36 | intl: ^0.18.0 37 | flutter_neumorphic_plus: ^3.3.0 38 | percent_indicator: ^4.2.3 39 | flutter_phosphor_icons: ^0.0.1+6 40 | 41 | dev_dependencies: 42 | flutter_test: 43 | sdk: flutter 44 | flutter_lints: ^2.0.3 45 | import_sorter: ^4.6.0 46 | 47 | # For information on the generic Dart part of this file, see the 48 | # following page: https://dart.dev/tools/pub/pubspec 49 | 50 | # The following section is specific to Flutter. 51 | flutter: 52 | # The following line ensures that the Material Icons font is 53 | # included with your application, so that you can use the icons in 54 | # the material Icons class. 55 | uses-material-design: true 56 | 57 | # To add assets to your application, add an assets section, like this: 58 | assets: 59 | - assets/icons/ 60 | # - images/a_dot_ham.jpeg 61 | 62 | # An image asset can refer to one or more resolution-specific "variants", see 63 | # https://flutter.dev/assets-and-images/#resolution-aware. 64 | 65 | # For details regarding adding assets from package dependencies, see 66 | # https://flutter.dev/assets-and-images/#from-packages 67 | 68 | # To add custom fonts to your application, add a fonts section here, 69 | # in this "flutter" section. Each entry in this list should have a 70 | # "family" key with the font family name, and a "fonts" key with a 71 | # list giving the asset and other descriptors for the font. For 72 | # example: 73 | fonts: 74 | - family: Lato 75 | fonts: 76 | - asset: assets/fonts/Lato-Regular.ttf 77 | # - asset: fonts/Schyler-Italic.ttf 78 | # style: italic 79 | # - family: Trajan Pro 80 | # fonts: 81 | # - asset: fonts/TrajanPro.ttf 82 | # - asset: fonts/TrajanPro_Bold.ttf 83 | # weight: 700 84 | # 85 | # For details regarding fonts from package dependencies, 86 | # see https://flutter.dev/custom-fonts/#from-packages 87 | -------------------------------------------------------------------------------- /screenshots/bed_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/screenshots/bed_dark.png -------------------------------------------------------------------------------- /screenshots/bed_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/screenshots/bed_light.png -------------------------------------------------------------------------------- /screenshots/clock_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/screenshots/clock_dark.png -------------------------------------------------------------------------------- /screenshots/clock_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/screenshots/clock_light.png -------------------------------------------------------------------------------- /screenshots/count_down_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/screenshots/count_down_dark.png -------------------------------------------------------------------------------- /screenshots/count_down_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/screenshots/count_down_light.png -------------------------------------------------------------------------------- /screenshots/stopwatch_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/screenshots/stopwatch_dark.png -------------------------------------------------------------------------------- /screenshots/stopwatch_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambiengcode/analog-clock/3d588ad35ff2aa542f75a576d5a326402c0c5bd0/screenshots/stopwatch_light.png -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | // Flutter imports: 9 | import 'package:flutter/material.dart'; 10 | 11 | // Package imports: 12 | import 'package:flutter_test/flutter_test.dart'; 13 | 14 | // Project imports: 15 | import 'package:analog_clock/src/app.dart'; 16 | 17 | void main() { 18 | testWidgets('Counter increments smoke test', (tester) async { 19 | // Build our app and trigger a frame. 20 | await tester.pumpWidget(const App()); 21 | 22 | // Verify that our counter starts at 0. 23 | expect(find.text('0'), findsOneWidget); 24 | expect(find.text('1'), findsNothing); 25 | 26 | // Tap the '+' icon and trigger a frame. 27 | await tester.tap(find.byIcon(Icons.add)); 28 | await tester.pump(); 29 | 30 | // Verify that our counter has incremented. 31 | expect(find.text('0'), findsNothing); 32 | expect(find.text('1'), findsOneWidget); 33 | }); 34 | } 35 | --------------------------------------------------------------------------------