├── .firebase └── hosting.YnVpbGQvd2Vi.cache ├── .firebaserc ├── .gitignore ├── .metadata ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── app │ │ │ │ └── meedu │ │ │ │ └── my_puzzle │ │ │ │ └── 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 │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets ├── animals │ ├── cat.png │ ├── dog.png │ ├── fox.png │ ├── koala.png │ ├── lion.png │ ├── monkey.png │ ├── mouse.png │ ├── panda.png │ ├── penguin.png │ └── tiger.png ├── icons │ ├── PuzzleIcons.ttf │ ├── config.json │ └── icon.png ├── images │ ├── dash.png │ ├── hero-dash.png │ ├── jungle.png │ ├── numeric-puzzle.png │ └── relax-dash.png ├── rive │ └── winner.riv └── sounds │ ├── cat.mp3 │ ├── dog.mp3 │ ├── fox.mp3 │ ├── koala.mp3 │ ├── lion.mp3 │ ├── monkey.mp3 │ ├── mouse.mp3 │ ├── panda.mp3 │ ├── penguin.mp3 │ ├── pull-out.mp3 │ └── tiger.mp3 ├── firebase.json ├── 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 ├── lib ├── generated │ ├── intl │ │ ├── messages_all.dart │ │ ├── messages_en.dart │ │ └── messages_es.dart │ └── l10n.dart ├── main.dart └── src │ ├── data │ └── repositories_impl │ │ ├── audio_repository_impl.dart │ │ ├── images_repository_impl.dart │ │ └── settings_repository_impl.dart │ ├── domain │ ├── models │ │ ├── move_to.dart │ │ ├── position.dart │ │ ├── puzzle.dart │ │ ├── puzzle_image.dart │ │ └── tile.dart │ └── repositories │ │ ├── audio_repository.dart │ │ ├── images_repository.dart │ │ └── settings_repository.dart │ ├── inject_dependencies.dart │ ├── l10n │ ├── intl_en.arb │ └── intl_es.arb │ ├── my_app.dart │ └── ui │ ├── global │ ├── controllers │ │ └── theme_controller.dart │ └── widgets │ │ ├── max_text_scale_factor.dart │ │ ├── my_icon_button.dart │ │ ├── my_text_icon_button.dart │ │ └── up_to_down.dart │ ├── icons │ └── puzzle_icons.dart │ ├── pages │ ├── game │ │ ├── controller │ │ │ ├── game_controller.dart │ │ │ └── game_state.dart │ │ ├── game_view.dart │ │ └── widgets │ │ │ ├── background.dart │ │ │ ├── confirm_dialog.dart │ │ │ ├── game_app_bar.dart │ │ │ ├── game_buttons.dart │ │ │ ├── puzzle_interactor.dart │ │ │ ├── puzzle_options.dart │ │ │ ├── puzzle_tile.dart │ │ │ ├── time_and_moves.dart │ │ │ └── winner_dialog.dart │ ├── privacy │ │ └── privacy_view.dart │ └── splash │ │ ├── circle_transition_clipper.dart │ │ └── splash_view.dart │ ├── routes │ ├── app_routes.dart │ └── routes.dart │ └── utils │ ├── colors.dart │ ├── dark_mode_extension.dart │ ├── platform.dart │ ├── responsive.dart │ └── time_parser.dart ├── pubspec.lock ├── pubspec.yaml └── web ├── favicon.png ├── icons ├── Icon-192.png ├── Icon-512.png ├── Icon-maskable-192.png └── Icon-maskable-512.png ├── index.html └── manifest.json /.firebase/hosting.YnVpbGQvd2Vi.cache: -------------------------------------------------------------------------------- 1 | favicon.png,1631206088305,fcc7c4545d5b62ad01682589e6fdc7ea03d0a3b42069963c815c344b632eb5cf 2 | manifest.json,1643664647590,b9dbd2f9cc8a3c7c3c04786d9fa8f9ba356684c836ba52432a2872cde66c0182 3 | assets/assets/animals/cat.png,1643748514237,68e338cd333467886fbed2a66aa2e19cb7be16383c68a1ca4b8e79e2bb0bd403 4 | assets/assets/animals/dog.png,1643748514237,361ecc495b3d001ddcdad3d6c4f7d7af40da403bfe7eb5f2233c2812ca154263 5 | assets/assets/animals/fox.png,1643748514238,c158424176148fa50287a95e91026256fbae36c9b2eac5a5e5c8976ef5b628a6 6 | assets/assets/animals/koala.png,1643748514239,7090de1b1be675e784699f73569accbdd11466784ef0b94cd3ec1599832da138 7 | assets/assets/animals/lion.png,1643748514239,1322a5f5d56477a2d136e39354fa9c4c9eca8b70f9d5caaf4ef4b44279f5aac0 8 | assets/assets/animals/monkey.png,1643748514240,8fa484e292b77d80eca7a91326727eced975482048d0a2175e61885f085628e7 9 | assets/assets/animals/mouse.png,1643748514240,6d2e7b62175688685ff19aaa20188aca3ac7214ecfe6b45063ba1148078168e1 10 | assets/assets/animals/panda.png,1643748514241,be3f7f9dcb9e160d05a572ec379963c648f7d0ebee6661bf878d75107ed5a7ce 11 | assets/assets/animals/penguin.png,1643748514241,228736cb71fdbc93deea399b2ed16f56f1aec13a4cb7906cc4f92f93cf266caa 12 | assets/assets/animals/tiger.png,1643748514241,37c66167c0cdb045a8ed0934e415a29bbbbf4dd8fc6bb6355b8c901cac32c802 13 | assets/assets/icons/PuzzleIcons.ttf,1643748514242,3651b6d9299c3881ff6682c474f60e885b3e99898174fa738600918b60a6d95c 14 | assets/assets/images/dash.png,1643748514244,07efb6040866fba7279af050199559aeec72db31f328e986032697f067e509dd 15 | assets/assets/images/hero-dash.png,1643748514246,3b67c1048dc2be6c9606700a664be48e182ee03ab6c101017ae6eb187f338f5f 16 | assets/assets/images/jungle.png,1643748514247,c813bfc521adcdcaabdd3aa8a1f50ff18ab2394442341fede491fb58f5fe355b 17 | assets/assets/images/numeric-puzzle.png,1643923353824,05c6d5e550fcf279ccf587f64a134b89b3923be1933e7ba26b30a31ab1eedc2f 18 | assets/assets/images/relax-dash.png,1643748514250,f2d0b6d453359b61a9a8cb572402b26c051b9dd3297ee4e0845afcc5ce3138b3 19 | assets/assets/sounds/cat.mp3,1643909563458,1894f0487f23cc274582fe702b0a8b2bf81c5ab87c8d846d12f847d7d922b776 20 | assets/assets/sounds/dog.mp3,1643909620527,5bff40a41a09433b62b011fda87b717dcc3f7a8bd8417c337228749eb56f8737 21 | assets/assets/sounds/fox.mp3,1643910560818,0556dce1dff2bacbe10985db60b18197a9e4ea972deb5e54f4f81164ef5f262f 22 | assets/assets/sounds/koala.mp3,1643910984913,5054507cb0205691b0370fef45987f8fd9352c0afc485d1f060288a0932dd8cf 23 | assets/assets/sounds/lion.mp3,1643909466155,5d74d57b0fd608e218a964480bf5b9dbe592ca7ae22a416c5bb8447804e39603 24 | assets/assets/sounds/monkey.mp3,1643909745432,1e786553bbc8d44a40844a98afe9130feafe1b42858faadfd76599c9926af548 25 | assets/assets/sounds/mouse.mp3,1643910865820,0045f996170eecf03b75c93b4ab42883d0ca68d582a0b5cb931f0946ce5b5dcb 26 | assets/assets/sounds/panda.mp3,1643909987724,1bd8ba3541b3f5be5387f02a9070385918086e31499dd8ef73d2b765791cbd6f 27 | assets/assets/sounds/penguin.mp3,1643911629979,bff839482aa958207083e21bf5562e9ed62ffaa9c90afa0bfe5f6fcb8f642001 28 | assets/assets/sounds/pull-out.mp3,1643748514251,0410b36a0c5de8f8da952e68c5eaf207cc56344f3f705241cef3f71661645651 29 | assets/assets/sounds/tiger.mp3,1643911226710,6cad33b3e7cb4d79df71599e0a102cb7bbe8b65522f5b6231bb33a1c4a443950 30 | assets/fonts/MaterialIcons-Regular.otf,1615596762000,5f71a8843e4edc9656c39061c2232458a6fc77e1603305960e4efa9c77f8b7a2 31 | canvaskit/canvaskit.js,315464400000,332d67a51b86f5129fc7d929d6bb6bd0416b17fd853899efc1f5044770954ed6 32 | canvaskit/canvaskit.wasm,315464400000,8dae2a06cf716711e3578aa55ee7b03ccdc54b4bdc9be9ee50c33515d2b3a7fe 33 | canvaskit/profiling/canvaskit.js,315464400000,41ae97b4ac8a386f55b22f1962c7b564da96df256fd938d684e73a8061e70b61 34 | canvaskit/profiling/canvaskit.wasm,315464400000,cb4c2221f1c20811ac3a33666833b4458656193de55b276b3c8fc31856b2f3a0 35 | icons/Icon-192.png,1631206088305,d2e0131bb7851eb9d98f7885edb5ae4b4d6b7a6c7addf8a25b9b712b39274c0f 36 | icons/Icon-512.png,1631206088305,7a31ce91e554f1941158ca46f31c7f3f2b7c8c129229ea74a8fae1affe335033 37 | icons/Icon-maskable-192.png,1624907668000,dd96c123fdf6817cdf7e63d9693bcc246bac2e3782a41a6952fa41c0617c5573 38 | icons/Icon-maskable-512.png,1624907668000,e7983524dc70254adc61764657d7e03d19284de8da586b5818d737bc08c6d14e 39 | index.html,1643987060788,7273cf247784cafe1d985221967cb2c77de4bf923d902529c5fe27f46e4d1a7d 40 | version.json,1643987060670,5527fe959775d7233e24c641a2db3ed5b38a03a9b25644f3d498d24b6e36ebc2 41 | assets/AssetManifest.json,1643987060783,55843880abb502448fb0405d9bef6020755f55b6157b573c37c1959d0f37436f 42 | flutter_service_worker.js,1643987061170,794108aaf140df0acb9fe623270ab3470e45bdd2b24b631adefa7dab5201de0d 43 | assets/FontManifest.json,1643987060783,f5af44545a98526565c7914cf43f0d009e53427d27c704b483b105db189092e6 44 | assets/NOTICES,1643987060783,26c42f554fd9febb527f1dac3a3862ee083040a9f30c64fbb04f64aa08cf23a3 45 | main.dart.js,1643987060379,6bf464a33b02b2a4cef0d3f31fa9fe34db9379fa37c7c8008a42870cc7360e37 46 | -------------------------------------------------------------------------------- /.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "darwin-puzzle" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.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 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /.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: 77d935af4db863f6abd0b9c31c7e6df2a13de57b 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Jungle puzzle 2 | 3 | A slide puzzle game created with flutter for the flutter hack https://flutter.dev/events/puzzle-hack 4 | 5 | 6 | | Numeric | Image | Dark Mode | 7 | |--------|----------------|----| 8 | | ![](https://user-images.githubusercontent.com/15864336/152590496-b3f52cf5-f151-402d-9c99-89521cd2067b.png) | ![](https://user-images.githubusercontent.com/15864336/152590537-57dc231e-ce73-4802-a16c-7364d6caea85.png) |![](https://user-images.githubusercontent.com/15864336/152590811-0b5a24c7-cd03-48ab-abb1-5a2000079b5f.png) 9 | 10 | ## How to run? 11 | 12 | first run the next command to install all dependencies. 13 | ```shell 14 | flutter pub get 15 | ``` 16 | 17 | Now for Android and iOS use 18 | ```shell 19 | flutter run 20 | ``` 21 | 22 | For web use 23 | ```shell 24 | flutter run -d chrome --profile 25 | ``` 26 | 27 | 28 | 29 | 30 | --- 31 | Check the next flutter puzzle tutorial (Spanish) 32 | 33 | [![Flutter puzzle hack - tutorial](https://img.youtube.com/vi/DEDO1yHXKHY/0.jpg)](https://www.youtube.com/watch?v=DEDO1yHXKHY "Flutter puzzle hack - tutorial") 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 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /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 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | def keystoreProperties = new Properties() 29 | def keystorePropertiesFile = rootProject.file('key.properties') 30 | if (keystorePropertiesFile.exists()) { 31 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 32 | } 33 | 34 | android { 35 | compileSdkVersion flutter.compileSdkVersion 36 | 37 | compileOptions { 38 | sourceCompatibility JavaVersion.VERSION_1_8 39 | targetCompatibility JavaVersion.VERSION_1_8 40 | } 41 | 42 | kotlinOptions { 43 | jvmTarget = '1.8' 44 | } 45 | 46 | sourceSets { 47 | main.java.srcDirs += 'src/main/kotlin' 48 | } 49 | 50 | defaultConfig { 51 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 52 | applicationId "app.meedu.my_puzzle" 53 | minSdkVersion 21 54 | targetSdkVersion flutter.targetSdkVersion 55 | versionCode flutterVersionCode.toInteger() 56 | versionName flutterVersionName 57 | } 58 | 59 | signingConfigs { 60 | release { 61 | keyAlias keystoreProperties['keyAlias'] 62 | keyPassword keystoreProperties['keyPassword'] 63 | storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null 64 | storePassword keystoreProperties['storePassword'] 65 | } 66 | } 67 | 68 | buildTypes { 69 | release { 70 | // TODO: Add your own signing config for the release build. 71 | // Signing with the debug keys for now, so `flutter run --release` works. 72 | signingConfig signingConfigs.release 73 | } 74 | } 75 | } 76 | 77 | flutter { 78 | source '../..' 79 | } 80 | 81 | dependencies { 82 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 83 | } 84 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 15 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/app/meedu/my_puzzle/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package app.meedu.my_puzzle 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/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/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/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Jungle Puzzle 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:4.1.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 | task clean(type: 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 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip 7 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /assets/animals/cat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/assets/animals/cat.png -------------------------------------------------------------------------------- /assets/animals/dog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/assets/animals/dog.png -------------------------------------------------------------------------------- /assets/animals/fox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/assets/animals/fox.png -------------------------------------------------------------------------------- /assets/animals/koala.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/assets/animals/koala.png -------------------------------------------------------------------------------- /assets/animals/lion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/assets/animals/lion.png -------------------------------------------------------------------------------- /assets/animals/monkey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/assets/animals/monkey.png -------------------------------------------------------------------------------- /assets/animals/mouse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/assets/animals/mouse.png -------------------------------------------------------------------------------- /assets/animals/panda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/assets/animals/panda.png -------------------------------------------------------------------------------- /assets/animals/penguin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/assets/animals/penguin.png -------------------------------------------------------------------------------- /assets/animals/tiger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/assets/animals/tiger.png -------------------------------------------------------------------------------- /assets/icons/PuzzleIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/assets/icons/PuzzleIcons.ttf -------------------------------------------------------------------------------- /assets/icons/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PuzzleIcons", 3 | "css_prefix_text": "", 4 | "css_use_suffix": false, 5 | "hinting": true, 6 | "units_per_em": 1000, 7 | "ascent": 850, 8 | "glyphs": [ 9 | { 10 | "uid": "7007994809eee2570f2b07eeef5b45c6", 11 | "css": "watch", 12 | "code": 59392, 13 | "src": "custom_icons", 14 | "selected": true, 15 | "svg": { 16 | "path": "M461.9 42.2C423.9 51.3 377.5 81.5 367.7 103.5 347.5 149.1 403.6 188.1 439.5 153.4 473.3 120.5 521.8 119.9 557.1 151.9 590 181.7 633.9 164.9 633.7 122.5L633.6 105.4 623.9 95.5C579 49 517.6 28.9 461.9 42.2M454.1 208C317.7 230.1 221.5 298.3 161.9 415.4 69.1 597.7 146.5 828.3 331.2 919.7 416.3 961.8 525.7 969.5 615.2 939.6 914.2 839.7 968.9 441.2 707.3 268.7 634.1 220.4 530.6 195.6 454.1 208M553 298.6C742.5 337.1 843.4 541.7 756.8 712 648.9 924.5 347.1 923.5 239.9 710.3 134 499.6 322.5 251.7 553 298.6M474.5 379.8C469.4 383.7 463.6 390.4 460.9 395.9L456.1 405.3 456.1 500C456.1 614.4 449.2 595.1 512.1 657.5 559.5 704.7 559.6 704.7 570.8 707 607.6 714.5 632.6 686.7 621.9 650.3 619 640.5 615.8 636.6 580.9 601.5L543 563.4 543 486.3C542.9 399.6 542.7 398.2 527.1 383.4 513.2 370.3 489.3 368.7 474.5 379.8", 17 | "width": 1000 18 | }, 19 | "search": [ 20 | "watch" 21 | ] 22 | }, 23 | { 24 | "uid": "68d57aa6cfd6660963fded3b5925f6db", 25 | "css": "mute", 26 | "code": 59393, 27 | "src": "custom_icons", 28 | "selected": true, 29 | "svg": { 30 | "path": "M723.9 85.1C690.6 91.7 683 97.4 595.9 180.5L523.3 249.8 387.1 250.5 251 251.1 232.4 256.2C93.6 294.3 14.6 427.6 50 563.8 72.9 652.4 139.8 718.4 232.4 743.8L251 748.9 387.1 749.5 523.3 750.2 595.9 819.5C635.9 857.7 672.4 891.5 677.1 894.8 746 942.2 841.4 912 869.5 834 873.9 821.6 874 819.5 874 756.7L874 692.1 868.4 684C853.7 662.7 820 660.1 802.4 679 791.8 690.2 791 694.8 791 747.5 791 809.1 788.1 818.6 766.6 828.4 742.5 839.4 737.6 836.3 654.8 757L584 689.3 584 500 584 310.7 654.8 243C737.6 163.7 742.5 160.6 766.6 171.6 788.1 181.4 791 190.8 791 252.9 791.1 306.7 792 311.4 804 322.6 822.4 339.9 854.1 336.6 868.4 316L874 307.9 874 243.3C874 180.5 873.9 178.4 869.5 166 848.2 106.9 786.6 72.6 723.9 85.1M500 500.1L500 666.2 383.3 665.6C251.5 664.8 253.1 665 219.7 648.6 96.7 588.2 95.6 412.7 217.8 352.1 251.8 335.2 259.5 334.4 389.2 334.2L500 334 500 500.1M736.3 376C720.4 380.9 707.2 398.5 707.1 414.8 707 432 710 436.7 741.9 469.2L772.2 500 741.9 530.8C710 563.3 707 568 707.1 585.2 707.2 611 733.4 630.8 759.6 625 770 622.7 771.9 621.2 801.8 591.6L833.2 560.6 861.4 589.1C893.6 621.5 900 625.6 917.2 625.7 939.9 625.8 957 610.2 958.6 587.9 960 568.5 957.7 564.6 924.1 530.8L893.7 500 924.1 469.2C957.7 435.3 960 431.5 958.6 411.9 957 389.7 939.9 374.2 917.1 374.3 900 374.4 893.6 378.6 861.4 410.9L833.2 439.4 801.8 408.5C764.7 371.8 759 369 736.3 376", 31 | "width": 1000 32 | }, 33 | "search": [ 34 | "mute" 35 | ] 36 | }, 37 | { 38 | "uid": "c7ee00388f90ecc171c19efc5648bf4f", 39 | "css": "sound", 40 | "code": 59394, 41 | "src": "custom_icons", 42 | "selected": true, 43 | "svg": { 44 | "path": "M561.6 84C525.9 90.4 515.1 98.3 428.9 180.7L357.3 249 304.2 250.2C235.7 251.7 207.5 258.5 165.1 283.7 1.1 381.5 1.1 618.5 165.1 716.3 207.5 741.5 235.7 748.3 304.1 749.8L357.3 751 407.5 798.8C496.3 883.6 505.8 892.3 517.2 899.2 589.7 943.2 686.8 903.6 705.9 822.2 711.2 800 710.4 195.7 705.1 175 689.5 113.9 624.4 72.6 561.6 84M606 175C610.5 178.2 616.5 184.8 619.1 189.5L624 198.2 624 500 624 801.8 619.1 810.5C608.7 829.1 586.7 836.9 567.8 828.5 561.4 825.7 539.4 805.9 488.5 757.5L418.1 690.4 418.1 500 418.1 309.6 487.8 243.2C573.3 161.7 580.8 157.3 606 175M843 299.7C816.4 309.2 807.9 342.2 825.7 366.6 885.2 448.5 889.9 539.2 838.5 614.5 818.8 643.4 817.4 646.2 817.4 659.2 817.4 699.1 865.9 716.3 892.7 685.9 963.4 605.7 978.9 474.2 929.6 372.1 900.2 310.9 873.7 288.8 843 299.7M332 500.3L332 666.5 299.3 665.5C195.2 662.3 127.9 597.3 127.9 500 127.9 400.9 198.5 334.9 305.2 334.2L332 334 332 500.3M745.1 380.5C716.5 394.1 712 423 733.6 453.1 743.3 466.6 748 482.1 748 500 748 517.9 743.3 533.4 733.6 546.9 715.6 571.8 715.3 594 732.5 610.8 774.6 651.9 833 587.5 833 500 833 422.9 787.2 360.6 745.1 380.5", 45 | "width": 1000 46 | }, 47 | "search": [ 48 | "sound" 49 | ] 50 | }, 51 | { 52 | "uid": "8d7048685cfa9c42e1dd8d12a1fde003", 53 | "css": "vibration", 54 | "code": 59395, 55 | "src": "custom_icons", 56 | "selected": true, 57 | "svg": { 58 | "path": "M367.2 66.5C312.1 80.7 267.8 125.3 253.9 180.5 247.8 204.6 247.8 795.3 253.9 819.5 267.6 874.4 313.1 919.9 368 933.6 391.8 939.6 608.3 939.6 632 933.6 686.9 919.9 732.4 874.4 746.1 819.5 752.2 795.3 752.2 204.7 746.1 180.5 732.4 125.6 686.9 80.1 632 66.4 608.6 60.5 390 60.6 367.2 66.5M621.9 130.5C651.9 141.6 673.9 164.6 683 194.3 688.5 212.5 688.5 787.5 683 805.7 673.9 835.4 652.1 858.2 621.9 869.6 605.5 875.8 401.8 876.5 381.8 870.5 350.5 860.9 325.5 835.7 317.1 805.2 311.5 784.9 311.4 212.7 317 194.3 328.2 157.8 357.9 132.3 395.5 127 423.9 123 609.4 125.9 621.9 130.5M420.4 160.1C410.9 166 406.3 174.9 406.3 187.5 406.3 217.2 410.8 218.7 500 218.7 581.8 218.7 581.2 218.8 589.9 204.6 598.5 190.4 593.5 168.6 579.6 160.1 569.8 154.1 430.2 154.1 420.4 160.1M76.6 347.6C61.7 356.8 62.5 347.9 62.5 500 62.5 632 62.6 635.9 66.4 642.1 78.1 661.4 109.4 661.4 121.1 642.1 127.2 632.2 127.2 367.8 121.1 357.9 112.7 344 90.8 339 76.6 347.6M889.1 347.6C874.2 356.8 875 347.9 875 500 875 632 875.1 635.9 878.9 642.1 890.6 661.4 921.9 661.4 933.6 642.1 939.7 632.2 939.7 367.8 933.6 357.9 925.2 344 903.3 339 889.1 347.6M170.4 378.9C155.7 387.8 156.3 383.1 156.3 500 156.3 616.9 155.7 612.2 170.4 621.1 184.6 629.7 206.4 624.7 214.9 610.9 220.9 600.9 220.9 399.1 214.9 389.1 206.4 375.3 184.6 370.3 170.4 378.9M795.4 378.9C780.7 387.8 781.3 383.1 781.3 500 781.3 616.9 780.7 612.2 795.4 621.1 809.6 629.7 831.4 624.7 839.9 610.9 845.9 600.9 845.9 399.1 839.9 389.1 831.4 375.3 809.6 370.3 795.4 378.9M475 722.9C424.8 742.2 425.2 820.8 475.6 839.7 519.3 856 562.5 827 562.5 781.3 562.5 735.4 518.6 706.2 475 722.9", 59 | "width": 1000 60 | }, 61 | "search": [ 62 | "vibration" 63 | ] 64 | }, 65 | { 66 | "uid": "d66b4f3bf8427f7d6eca39c5f276234d", 67 | "css": "vibration_off", 68 | "code": 59396, 69 | "src": "custom_icons", 70 | "selected": true, 71 | "svg": { 72 | "path": "M405 62.3C336.7 65.6 292.5 92.7 265 148.1 250 178.4 250 178.4 248.7 445L247.5 686.3 157.5 773.6 67.6 860.9 80.7 875.2C87.9 883 94.5 889.3 95.3 889.1 96.2 888.9 131.1 855.8 172.8 815.5L248.8 742.3 250.2 780.6C252.4 835.5 261.9 859.5 295 892.7 337.3 935.2 351.7 938.4 500 938.4 648.3 938.4 662.5 935.3 705.2 892.7 751.8 846 750.3 858.3 751.2 527.6L752.1 256.5 780.4 228.8C796 213.5 830.5 180.1 857 154.6L905.3 108.1 891.6 93.4 877.8 78.8 862 94C853.4 102.3 825 129.6 799 154.7 773.1 179.7 751.2 199.6 750.5 198.8 749.8 198.1 748.3 191.1 747.2 183.2 740.3 132.3 683.4 74.8 630.1 65 612.6 61.7 455.5 59.9 405 62.3M629 135.9C665.8 152.7 681.2 179.2 683.7 230.2L685.4 264.2 509.4 434.2C412.7 527.7 329.3 608 324.1 612.6L314.6 620.9 315.4 409.8 316.3 198.8 324 182.5C336.1 157 355.6 140.2 383.8 130.9 391.6 128.3 417.7 127.7 503.8 128.2L613.8 128.9 629 135.9M431.3 156.5C428.5 157.2 423.7 158.2 420.5 158.9 402.4 162.8 398.8 205.2 415.9 214.4 425.8 219.8 556.7 222.4 574.6 217.6 597.9 211.3 602.6 171.4 581.5 159.3 576.3 156.3 442.9 153.8 431.3 156.5M684.5 577.5L683.8 801.3 676.8 816.3C667.9 835.2 650.6 853.1 631.3 863.3L616.3 871.3 501.3 871.3 386.3 871.3 371.9 864.5C327.2 843.6 317.9 823.1 315.8 740.9L314.3 678.8 499 500.4 683.8 322.1 684.5 337.9C685 346.6 685 454.4 684.5 577.5M73.8 347.4C62.6 354.8 62.9 351.5 62.7 500.6L62.5 639.9 69.8 648.1C76.6 655.7 78.1 656.3 93.5 656.3 121.4 656.3 122.9 653.6 125.7 595.8 128.2 543.8 126.2 373.1 123 361.3 118.4 344.5 90.3 336.6 73.8 347.4M886.3 347.4C875.1 354.8 875.4 351.5 875.2 500.6L875 639.9 882.3 648.1C889.1 655.7 890.6 656.3 906 656.3 933.9 656.3 935.4 653.6 938.2 595.8 940.7 543.8 938.7 373.1 935.5 361.3 930.9 344.5 902.8 336.6 886.3 347.4M170.7 377.4C163.3 380.8 159.6 384.2 158.2 389.2 155.7 398.4 155.7 601.6 158.2 610.8 162.9 627.7 194.9 631.7 210.9 617.4L218.8 610.4 218.8 500.4 218.8 390.4 212.7 383.9C202 372.5 186.7 370.1 170.7 377.4M795.7 377.4C788.3 380.8 784.6 384.2 783.2 389.2 780.6 398.4 780.7 601.6 783.2 610.8 787.9 627.7 819.9 631.7 835.9 617.4L843.8 610.4 843.8 500.4 843.8 390.4 837.7 383.9C827 372.5 811.7 370.1 795.7 377.4M471.1 723.3C422.8 746.3 426 822.2 476 841.3 533.1 863.1 586 798.4 553.7 746.1 537.3 719.7 500.3 709.4 471.1 723.3", 73 | "width": 1000 74 | }, 75 | "search": [ 76 | "vibration_off" 77 | ] 78 | }, 79 | { 80 | "uid": "f31858df2cbafba6481862c42dc00915", 81 | "css": "grid", 82 | "code": 59398, 83 | "src": "custom_icons", 84 | "selected": true, 85 | "svg": { 86 | "path": "M126.5 66.3C98.2 75.3 73.9 100.2 65.6 128.8 60.9 144.7 60.9 386.5 65.6 402.5 74 431.4 98.3 456.1 127.1 465.1 145.4 470.8 385.8 470.8 404.1 465.1 432.9 456.1 457.2 431.4 465.7 402.5 470.3 386.5 470.3 144.7 465.7 128.8 457.2 99.9 432.9 75.2 404.1 66.2 386.1 60.6 144 60.7 126.5 66.3M595.2 66.3C566.9 75.3 542.7 100.2 534.3 128.8 529.7 144.7 529.7 386.5 534.3 402.5 542.8 431.4 567.1 456.1 595.9 465.1 614.2 470.8 854.5 470.8 872.9 465.1 901.7 456.1 926 431.4 934.4 402.5 939.1 386.5 939.1 144.7 934.4 128.8 926 99.9 901.7 75.2 872.9 66.2 854.9 60.6 612.8 60.7 595.2 66.3M379.7 126.9C405.9 132.9 405.3 129.7 405.3 265.6 405.3 401.5 405.9 398.3 379.7 404.4 369.7 406.7 161.5 406.7 151.5 404.4 125.4 398.3 126 401.5 126 265.6 126 130.5 125.5 132.9 150.6 127 159.9 124.8 370.4 124.7 379.7 126.9M848.5 126.9C874.6 132.9 874 129.7 874 265.6 874 401.5 874.6 398.3 848.5 404.4 838.5 406.7 630.3 406.7 620.3 404.4 594.1 398.3 594.7 401.5 594.7 265.6 594.7 130.5 594.3 132.9 619.4 127 628.6 124.8 839.1 124.7 848.5 126.9M126.5 535C98.2 544.1 73.9 569 65.6 597.5 60.9 613.5 60.9 855.3 65.6 871.2 74 900.1 98.3 924.8 127.1 933.8 145.4 939.5 385.8 939.5 404.1 933.8 432.9 924.8 457.2 900.1 465.7 871.2 470.3 855.3 470.3 613.5 465.7 597.5 457.2 568.6 432.9 543.9 404.1 534.9 386.1 529.4 144 529.4 126.5 535M547.3 534.5C534.9 540.6 529.1 554.9 532.1 571.7 533.3 578.2 542.7 588.3 611 656.8L688.4 734.4 611.9 811C569.8 853.2 534.4 890.2 533.3 893.2 525.9 913.1 537.2 934.4 556.6 937 575.5 939.5 572.6 941.7 656.7 857.8L734.4 780.3 812 857.8C896.1 941.7 893.3 939.5 912.1 937 931.5 934.4 942.9 913.1 935.5 893.2 934.3 890.2 899 853.2 856.9 811L780.3 734.4 856.7 657.7C939 575.2 937.5 577 937.5 563.4 937.5 540.8 920.8 527.8 897.1 532.1 890.5 533.3 880.4 542.7 812 611L734.4 688.4 657.7 611.9C615.5 569.8 578.6 534.4 575.6 533.3 567.6 530.3 554.7 530.9 547.3 534.5M379.7 595.6C405.9 601.7 405.3 598.5 405.3 734.4 405.3 870.3 405.9 867.1 379.7 873.1 369.7 875.4 161.5 875.4 151.5 873.1 125.4 867.1 126 870.3 126 734.4 126 599.2 125.5 601.6 150.6 595.7 159.9 593.5 370.4 593.4 379.7 595.6", 87 | "width": 1000 88 | }, 89 | "search": [ 90 | "grid" 91 | ] 92 | }, 93 | { 94 | "uid": "ba216ae4682f58bee75b880dafdae983", 95 | "css": "brightness", 96 | "code": 59399, 97 | "src": "custom_icons", 98 | "selected": true, 99 | "svg": { 100 | "path": "M480.5 3.8C460 14.7 457 24.1 457 76.9 457.1 124 458.1 129.9 469.2 141.8 490.8 165.3 532.5 156.1 540.8 126 544.3 113.4 543.6 37.6 540 27.1 531.9 4.2 501.9-7.6 480.5 3.8M460 188.7C223.5 223 108 489.7 245.7 683.5 334.2 808 507.7 849.2 644.5 778.1 868.6 661.7 868.6 338.2 644.5 221.9 592.2 194.8 514.4 180.8 460 188.7M95.7 234.3C63.3 240.8 50.2 283.3 73.7 305.5 82.4 313.7 148.1 347.1 158.6 348.6 191.5 353.6 217.7 318.1 203.2 288.2 197 275.5 190.6 270.9 151.4 251.8 115.5 234.3 107.8 231.8 95.7 234.3M886.3 234.6C872.5 237.8 812 269.5 804.3 277.6 779 304.1 798.5 348.6 835.5 348.6 849.2 348.6 913.7 318.2 926.3 305.7 956 276.4 927.1 225.1 886.3 234.6M151.4 653C138.5 657.3 78.9 689.2 73.2 694.9 53.2 714.7 60.5 749.6 87.6 763.6 107.9 774.1 190.9 737.1 203 712.2 219.1 678.8 186.5 641.4 151.4 653M822 653.1C792 663.8 783.1 701.3 805.4 723.3 818.7 736.5 880.5 765.9 894.8 765.9 922.6 765.9 939 748.2 937.2 720 935.8 699.1 931.7 695 891.4 673.8 846.6 650.2 837.6 647.5 822 653.1M486.1 846.8C475.1 850.2 462.2 863.4 459.1 874.3 456.3 884.6 456.4 961.3 459.3 970.9 470.6 1008.9 526.8 1010.3 540 972.9 543.6 962.6 544.3 886.6 540.9 874.3 534.7 852.3 509.5 839.6 486.1 846.8", 101 | "width": 1000 102 | }, 103 | "search": [ 104 | "brightness" 105 | ] 106 | }, 107 | { 108 | "uid": "6ae07062287078d1b411bab00d3ae34e", 109 | "css": "movements", 110 | "code": 59400, 111 | "src": "custom_icons", 112 | "selected": true, 113 | "svg": { 114 | "path": "M230.2 212.5C211 222.5 202.4 244.9 210 265.2 212.9 273.1 229.3 290.3 325.9 387.2L438.4 500 325.9 612.8C202.7 736.3 205.5 733 207.5 753.7 210.3 783.1 243.7 801.7 269.5 788.3 278.5 783.7 533.7 528.5 538.3 519.5 544 508.6 543.3 489.5 536.8 478.5 528.2 464.2 278.3 215.9 267.6 211.1 255 205.4 242.9 205.9 230.2 212.5M494.1 210.8C466.1 216.4 451.4 246.8 464.2 272.4 465.9 275.7 517 328.2 577.8 389.2L688.4 500 575.9 612.8C452.7 736.3 455.5 733 457.4 753.7 460.3 783.1 493.7 801.7 519.5 788.3 523.3 786.4 585 726.2 656.6 654.6 804.2 507 796.2 516.7 791 490.7L788.7 479.5 657.6 348.2C585.5 275.9 523.8 215.6 520.6 214.1 512.5 210.4 502.4 209.2 494.1 210.8", 115 | "width": 1000 116 | }, 117 | "search": [ 118 | "movements" 119 | ] 120 | }, 121 | { 122 | "uid": "faecfb3c03f0cafc14950f89ea97532b", 123 | "css": "dark_mode", 124 | "code": 59401, 125 | "src": "custom_icons", 126 | "selected": true, 127 | "svg": { 128 | "path": "M454.1 2.2C83.5 46.5-110.5 467.4 96.3 778.3 320.4 1115.2 839.9 1055.9 981.2 677.3 998.1 632.1 975.3 609.9 933.6 630.9 817.4 689.3 684.2 666.8 596.7 573.8 455.4 423.7 516.6 179.1 712.2 112 748.4 99.6 758.4 84.7 745.6 62.1 736.1 45.1 661 17.3 593.8 5.7 565.9 0.9 482.5-1.2 454.1 2.2", 129 | "width": 1000 130 | }, 131 | "search": [ 132 | "dark_mode" 133 | ] 134 | }, 135 | { 136 | "uid": "b620bb1a08b74ab3e1db89031e4c31ba", 137 | "css": "heart", 138 | "code": 59397, 139 | "src": "custom_icons", 140 | "selected": true, 141 | "svg": { 142 | "path": "M232.4 57.7C94.6 81.6 0.1 202.4 0.1 354.5 0.1 482.6 60 580.1 234.1 735.4 316.4 808.7 464.8 934.4 474.6 939.2 489.8 946.5 510.2 946.5 525.4 939.2 540.4 932 749.6 752 819.5 686.1 972.1 542.5 1024.9 416.4 991.5 275.4 937.5 47.4 664.1-23.2 516.4 152.8 498.1 174.6 501.9 174.6 483.6 152.8 420 77 326 41.5 232.4 57.7", 143 | "width": 1000 144 | }, 145 | "search": [ 146 | "heart" 147 | ] 148 | } 149 | ] 150 | } -------------------------------------------------------------------------------- /assets/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/assets/icons/icon.png -------------------------------------------------------------------------------- /assets/images/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/assets/images/dash.png -------------------------------------------------------------------------------- /assets/images/hero-dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/assets/images/hero-dash.png -------------------------------------------------------------------------------- /assets/images/jungle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/assets/images/jungle.png -------------------------------------------------------------------------------- /assets/images/numeric-puzzle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/assets/images/numeric-puzzle.png -------------------------------------------------------------------------------- /assets/images/relax-dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/assets/images/relax-dash.png -------------------------------------------------------------------------------- /assets/rive/winner.riv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/assets/rive/winner.riv -------------------------------------------------------------------------------- /assets/sounds/cat.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/assets/sounds/cat.mp3 -------------------------------------------------------------------------------- /assets/sounds/dog.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/assets/sounds/dog.mp3 -------------------------------------------------------------------------------- /assets/sounds/fox.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/assets/sounds/fox.mp3 -------------------------------------------------------------------------------- /assets/sounds/koala.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/assets/sounds/koala.mp3 -------------------------------------------------------------------------------- /assets/sounds/lion.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/assets/sounds/lion.mp3 -------------------------------------------------------------------------------- /assets/sounds/monkey.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/assets/sounds/monkey.mp3 -------------------------------------------------------------------------------- /assets/sounds/mouse.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/assets/sounds/mouse.mp3 -------------------------------------------------------------------------------- /assets/sounds/panda.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/assets/sounds/panda.mp3 -------------------------------------------------------------------------------- /assets/sounds/penguin.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/assets/sounds/penguin.mp3 -------------------------------------------------------------------------------- /assets/sounds/pull-out.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/assets/sounds/pull-out.mp3 -------------------------------------------------------------------------------- /assets/sounds/tiger.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/assets/sounds/tiger.mp3 -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "build/web", 4 | "ignore": [ 5 | "firebase.json", 6 | "**/.*", 7 | "**/node_modules/**" 8 | ], 9 | "rewrites": [ 10 | { 11 | "source": "**", 12 | "destination": "/index.html" 13 | } 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /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 | 9.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, '12.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 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | 41 | # ADD THE NEXT SECTION 42 | target.build_configurations.each do |config| 43 | config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [ 44 | '$(inherited)', 45 | 'AUDIO_SESSION_MICROPHONE=0' 46 | ] 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - audio_session (0.0.1): 3 | - Flutter 4 | - Flutter (1.0.0) 5 | - just_audio (0.0.1): 6 | - Flutter 7 | - path_provider_ios (0.0.1): 8 | - Flutter 9 | - shared_preferences_ios (0.0.1): 10 | - Flutter 11 | 12 | DEPENDENCIES: 13 | - audio_session (from `.symlinks/plugins/audio_session/ios`) 14 | - Flutter (from `Flutter`) 15 | - just_audio (from `.symlinks/plugins/just_audio/ios`) 16 | - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) 17 | - shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`) 18 | 19 | EXTERNAL SOURCES: 20 | audio_session: 21 | :path: ".symlinks/plugins/audio_session/ios" 22 | Flutter: 23 | :path: Flutter 24 | just_audio: 25 | :path: ".symlinks/plugins/just_audio/ios" 26 | path_provider_ios: 27 | :path: ".symlinks/plugins/path_provider_ios/ios" 28 | shared_preferences_ios: 29 | :path: ".symlinks/plugins/shared_preferences_ios/ios" 30 | 31 | SPEC CHECKSUMS: 32 | audio_session: 4f3e461722055d21515cf3261b64c973c062f345 33 | Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a 34 | just_audio: baa7252489dbcf47a4c7cc9ca663e9661c99aafa 35 | path_provider_ios: 7d7ce634493af4477d156294792024ec3485acd5 36 | shared_preferences_ios: aef470a42dc4675a1cdd50e3158b42e3d1232b32 37 | 38 | PODFILE CHECKSUM: 66bbd5299d7b1f708996813bfb657e12403bc5e0 39 | 40 | COCOAPODS: 1.11.2 41 | -------------------------------------------------------------------------------- /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 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /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/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/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/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/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/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/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/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/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/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/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/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/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/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/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/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/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/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/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/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/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/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/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/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/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/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/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/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/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/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/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/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/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 | Slide Puzzle 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleLocalizations 16 | 17 | en 18 | es 19 | 20 | CFBundleName 21 | my_puzzle 22 | CFBundlePackageType 23 | APPL 24 | CFBundleShortVersionString 25 | $(FLUTTER_BUILD_NAME) 26 | CFBundleSignature 27 | ???? 28 | CFBundleVersion 29 | $(CURRENT_PROJECT_VERSION) 30 | LSRequiresIPhoneOS 31 | 32 | UILaunchStoryboardName 33 | LaunchScreen 34 | UIMainStoryboardFile 35 | Main 36 | UISupportedInterfaceOrientations 37 | 38 | UIInterfaceOrientationPortrait 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UISupportedInterfaceOrientations~ipad 43 | 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationPortraitUpsideDown 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | UIViewControllerBasedStatusBarAppearance 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /lib/generated/intl/messages_all.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that looks up messages for specific locales by 3 | // delegating to the appropriate library. 4 | 5 | // Ignore issues from commonly used lints in this file. 6 | // ignore_for_file:implementation_imports, file_names, unnecessary_new 7 | // ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering 8 | // ignore_for_file:argument_type_not_assignable, invalid_assignment 9 | // ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases 10 | // ignore_for_file:comment_references 11 | 12 | import 'dart:async'; 13 | 14 | import 'package:intl/intl.dart'; 15 | import 'package:intl/message_lookup_by_library.dart'; 16 | import 'package:intl/src/intl_helpers.dart'; 17 | 18 | import 'messages_en.dart' as messages_en; 19 | import 'messages_es.dart' as messages_es; 20 | 21 | typedef Future LibraryLoader(); 22 | Map _deferredLibraries = { 23 | 'en': () => new Future.value(null), 24 | 'es': () => new Future.value(null), 25 | }; 26 | 27 | MessageLookupByLibrary? _findExact(String localeName) { 28 | switch (localeName) { 29 | case 'en': 30 | return messages_en.messages; 31 | case 'es': 32 | return messages_es.messages; 33 | default: 34 | return null; 35 | } 36 | } 37 | 38 | /// User programs should call this before using [localeName] for messages. 39 | Future initializeMessages(String localeName) async { 40 | var availableLocale = Intl.verifiedLocale( 41 | localeName, (locale) => _deferredLibraries[locale] != null, 42 | onFailure: (_) => null); 43 | if (availableLocale == null) { 44 | return new Future.value(false); 45 | } 46 | var lib = _deferredLibraries[availableLocale]; 47 | await (lib == null ? new Future.value(false) : lib()); 48 | initializeInternalMessageLookup(() => new CompositeMessageLookup()); 49 | messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor); 50 | return new Future.value(true); 51 | } 52 | 53 | bool _messagesExistFor(String locale) { 54 | try { 55 | return _findExact(locale) != null; 56 | } catch (e) { 57 | return false; 58 | } 59 | } 60 | 61 | MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) { 62 | var actualLocale = 63 | Intl.verifiedLocale(locale, _messagesExistFor, onFailure: (_) => null); 64 | if (actualLocale == null) return null; 65 | return _findExact(actualLocale); 66 | } 67 | -------------------------------------------------------------------------------- /lib/generated/intl/messages_en.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that provides messages for a en locale. All the 3 | // messages from the main program should be duplicated here with the same 4 | // function name. 5 | 6 | // Ignore issues from commonly used lints in this file. 7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new 8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering 9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases 10 | // ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes 11 | // ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes 12 | 13 | import 'package:intl/intl.dart'; 14 | import 'package:intl/message_lookup_by_library.dart'; 15 | 16 | final messages = new MessageLookup(); 17 | 18 | typedef String MessageIfAbsent(String messageStr, List args); 19 | 20 | class MessageLookup extends MessageLookupByLibrary { 21 | String get localeName => 'en'; 22 | 23 | final messages = _notInlinedMessages(_notInlinedMessages); 24 | static Map _notInlinedMessages(_) => { 25 | "are_you_sure": MessageLookupByLibrary.simpleMessage("Are you sure?"), 26 | "back_to_game": 27 | MessageLookupByLibrary.simpleMessage("back to the game"), 28 | "completed": MessageLookupByLibrary.simpleMessage( 29 | "You have completed the puzzle"), 30 | "dou_you_really": MessageLookupByLibrary.simpleMessage( 31 | "Do you really want to restart the current puzzle"), 32 | "great_job": MessageLookupByLibrary.simpleMessage("GREAT JOB!"), 33 | "movements": MessageLookupByLibrary.simpleMessage("moves"), 34 | "no": MessageLookupByLibrary.simpleMessage("NO"), 35 | "ok": MessageLookupByLibrary.simpleMessage("OK"), 36 | "privacy": MessageLookupByLibrary.simpleMessage( 37 | "This is a free non-profit game, the game does not collect information of any kind from users or their traffic, it does not need internet."), 38 | "restart": MessageLookupByLibrary.simpleMessage("Restart"), 39 | "start": MessageLookupByLibrary.simpleMessage("START"), 40 | "time": MessageLookupByLibrary.simpleMessage("Time"), 41 | "yes": MessageLookupByLibrary.simpleMessage("YES") 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /lib/generated/intl/messages_es.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that provides messages for a es locale. All the 3 | // messages from the main program should be duplicated here with the same 4 | // function name. 5 | 6 | // Ignore issues from commonly used lints in this file. 7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new 8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering 9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases 10 | // ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes 11 | // ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes 12 | 13 | import 'package:intl/intl.dart'; 14 | import 'package:intl/message_lookup_by_library.dart'; 15 | 16 | final messages = new MessageLookup(); 17 | 18 | typedef String MessageIfAbsent(String messageStr, List args); 19 | 20 | class MessageLookup extends MessageLookupByLibrary { 21 | String get localeName => 'es'; 22 | 23 | final messages = _notInlinedMessages(_notInlinedMessages); 24 | static Map _notInlinedMessages(_) => { 25 | "are_you_sure": MessageLookupByLibrary.simpleMessage("¿Estás seguro?"), 26 | "back_to_game": 27 | MessageLookupByLibrary.simpleMessage("Regresar al juego"), 28 | "completed": MessageLookupByLibrary.simpleMessage( 29 | "Has completado el rompecabezas"), 30 | "dou_you_really": MessageLookupByLibrary.simpleMessage( 31 | "Realmente quieres reiniciar el rompecabezas actual"), 32 | "great_job": MessageLookupByLibrary.simpleMessage("¡GRAN TRABAJO!"), 33 | "movements": MessageLookupByLibrary.simpleMessage("movimientos"), 34 | "no": MessageLookupByLibrary.simpleMessage("NO"), 35 | "ok": MessageLookupByLibrary.simpleMessage("ACEPTAR"), 36 | "privacy": MessageLookupByLibrary.simpleMessage( 37 | "Este es un juego gratuito sin fines de lucro, el juego no recolecta información de ningun tipo de los usuarios ni tampoco de su trafico, no necesita internet."), 38 | "restart": MessageLookupByLibrary.simpleMessage("Reiniciar"), 39 | "start": MessageLookupByLibrary.simpleMessage("INICIAR"), 40 | "time": MessageLookupByLibrary.simpleMessage("Tiempo"), 41 | "yes": MessageLookupByLibrary.simpleMessage("SI") 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /lib/generated/l10n.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | import 'package:flutter/material.dart'; 3 | import 'package:intl/intl.dart'; 4 | import 'intl/messages_all.dart'; 5 | 6 | // ************************************************************************** 7 | // Generator: Flutter Intl IDE plugin 8 | // Made by Localizely 9 | // ************************************************************************** 10 | 11 | // ignore_for_file: non_constant_identifier_names, lines_longer_than_80_chars 12 | // ignore_for_file: join_return_with_assignment, prefer_final_in_for_each 13 | // ignore_for_file: avoid_redundant_argument_values, avoid_escaping_inner_quotes 14 | 15 | class S { 16 | S(); 17 | 18 | static S? _current; 19 | 20 | static S get current { 21 | assert(_current != null, 22 | 'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.'); 23 | return _current!; 24 | } 25 | 26 | static const AppLocalizationDelegate delegate = AppLocalizationDelegate(); 27 | 28 | static Future load(Locale locale) { 29 | final name = (locale.countryCode?.isEmpty ?? false) 30 | ? locale.languageCode 31 | : locale.toString(); 32 | final localeName = Intl.canonicalizedLocale(name); 33 | return initializeMessages(localeName).then((_) { 34 | Intl.defaultLocale = localeName; 35 | final instance = S(); 36 | S._current = instance; 37 | 38 | return instance; 39 | }); 40 | } 41 | 42 | static S of(BuildContext context) { 43 | final instance = S.maybeOf(context); 44 | assert(instance != null, 45 | 'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?'); 46 | return instance!; 47 | } 48 | 49 | static S? maybeOf(BuildContext context) { 50 | return Localizations.of(context, S); 51 | } 52 | 53 | /// `Are you sure?` 54 | String get are_you_sure { 55 | return Intl.message( 56 | 'Are you sure?', 57 | name: 'are_you_sure', 58 | desc: '', 59 | args: [], 60 | ); 61 | } 62 | 63 | /// `YES` 64 | String get yes { 65 | return Intl.message( 66 | 'YES', 67 | name: 'yes', 68 | desc: '', 69 | args: [], 70 | ); 71 | } 72 | 73 | /// `NO` 74 | String get no { 75 | return Intl.message( 76 | 'NO', 77 | name: 'no', 78 | desc: '', 79 | args: [], 80 | ); 81 | } 82 | 83 | /// `Do you really want to restart the current puzzle` 84 | String get dou_you_really { 85 | return Intl.message( 86 | 'Do you really want to restart the current puzzle', 87 | name: 'dou_you_really', 88 | desc: '', 89 | args: [], 90 | ); 91 | } 92 | 93 | /// `moves` 94 | String get movements { 95 | return Intl.message( 96 | 'moves', 97 | name: 'movements', 98 | desc: '', 99 | args: [], 100 | ); 101 | } 102 | 103 | /// `Restart` 104 | String get restart { 105 | return Intl.message( 106 | 'Restart', 107 | name: 'restart', 108 | desc: '', 109 | args: [], 110 | ); 111 | } 112 | 113 | /// `START` 114 | String get start { 115 | return Intl.message( 116 | 'START', 117 | name: 'start', 118 | desc: '', 119 | args: [], 120 | ); 121 | } 122 | 123 | /// `GREAT JOB!` 124 | String get great_job { 125 | return Intl.message( 126 | 'GREAT JOB!', 127 | name: 'great_job', 128 | desc: '', 129 | args: [], 130 | ); 131 | } 132 | 133 | /// `You have completed the puzzle` 134 | String get completed { 135 | return Intl.message( 136 | 'You have completed the puzzle', 137 | name: 'completed', 138 | desc: '', 139 | args: [], 140 | ); 141 | } 142 | 143 | /// `Time` 144 | String get time { 145 | return Intl.message( 146 | 'Time', 147 | name: 'time', 148 | desc: '', 149 | args: [], 150 | ); 151 | } 152 | 153 | /// `OK` 154 | String get ok { 155 | return Intl.message( 156 | 'OK', 157 | name: 'ok', 158 | desc: '', 159 | args: [], 160 | ); 161 | } 162 | 163 | /// `This is a free non-profit game, the game does not collect information of any kind from users or their traffic, it does not need internet.` 164 | String get privacy { 165 | return Intl.message( 166 | 'This is a free non-profit game, the game does not collect information of any kind from users or their traffic, it does not need internet.', 167 | name: 'privacy', 168 | desc: '', 169 | args: [], 170 | ); 171 | } 172 | 173 | /// `back to the game` 174 | String get back_to_game { 175 | return Intl.message( 176 | 'back to the game', 177 | name: 'back_to_game', 178 | desc: '', 179 | args: [], 180 | ); 181 | } 182 | } 183 | 184 | class AppLocalizationDelegate extends LocalizationsDelegate { 185 | const AppLocalizationDelegate(); 186 | 187 | List get supportedLocales { 188 | return const [ 189 | Locale.fromSubtags(languageCode: 'en'), 190 | Locale.fromSubtags(languageCode: 'es'), 191 | ]; 192 | } 193 | 194 | @override 195 | bool isSupported(Locale locale) => _isSupported(locale); 196 | @override 197 | Future load(Locale locale) => S.load(locale); 198 | @override 199 | bool shouldReload(AppLocalizationDelegate old) => false; 200 | 201 | bool _isSupported(Locale locale) { 202 | for (var supportedLocale in supportedLocales) { 203 | if (supportedLocale.languageCode == locale.languageCode) { 204 | return true; 205 | } 206 | } 207 | return false; 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:my_puzzle/src/inject_dependencies.dart'; 3 | import 'package:url_strategy/url_strategy.dart'; 4 | import 'src/my_app.dart'; 5 | 6 | void main() async{ 7 | setPathUrlStrategy(); 8 | WidgetsFlutterBinding.ensureInitialized(); 9 | await injectDependencies(); 10 | runApp(const MyApp()); 11 | } 12 | 13 | -------------------------------------------------------------------------------- /lib/src/data/repositories_impl/audio_repository_impl.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:just_audio/just_audio.dart'; 3 | import 'package:my_puzzle/src/domain/repositories/audio_repository.dart'; 4 | 5 | class AudioRepositoryImpl implements AudioRepository { 6 | bool _isPlaying = false; 7 | final AudioPlayer _player; 8 | 9 | AudioRepositoryImpl(this._player) { 10 | _player.setVolume(0.5); 11 | } 12 | 13 | @override 14 | Future playMove() async { 15 | if (_isPlaying) { 16 | return; 17 | } 18 | 19 | _isPlaying = true; 20 | final duration = await _player.setAsset( 21 | 'assets/sounds/pull-out.mp3', 22 | ); 23 | if (duration != null) { 24 | unawaited(_player.play()); 25 | } 26 | _isPlaying = false; 27 | } 28 | 29 | @override 30 | Future playWinner() async {} 31 | 32 | @override 33 | Future dispose() { 34 | return _player.dispose(); 35 | } 36 | 37 | @override 38 | Future play(String asset) async { 39 | final duration = await _player.setAsset( 40 | asset, 41 | ); 42 | if (duration != null) { 43 | unawaited(_player.play()); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/src/data/repositories_impl/images_repository_impl.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | import 'dart:math' as math; 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter/services.dart'; 5 | import 'package:image/image.dart'; 6 | import 'package:my_puzzle/src/domain/models/puzzle_image.dart'; 7 | import 'package:my_puzzle/src/domain/repositories/images_repository.dart'; 8 | 9 | const puzzleOptions = [ 10 | PuzzleImage( 11 | name: 'Numeric', 12 | assetPath: 'assets/images/numeric-puzzle.png', 13 | soundPath: '', 14 | ), 15 | PuzzleImage( 16 | name: 'Lion', 17 | assetPath: 'assets/animals/lion.png', 18 | soundPath: 'assets/sounds/lion.mp3', 19 | ), 20 | PuzzleImage( 21 | name: 'Cat', 22 | assetPath: 'assets/animals/cat.png', 23 | soundPath: 'assets/sounds/cat.mp3', 24 | ), 25 | PuzzleImage( 26 | name: 'Dog', 27 | assetPath: 'assets/animals/dog.png', 28 | soundPath: 'assets/sounds/dog.mp3', 29 | ), 30 | PuzzleImage( 31 | name: 'Fox', 32 | assetPath: 'assets/animals/fox.png', 33 | soundPath: 'assets/sounds/fox.mp3', 34 | ), 35 | PuzzleImage( 36 | name: 'Koala', 37 | assetPath: 'assets/animals/koala.png', 38 | soundPath: 'assets/sounds/koala.mp3', 39 | ), 40 | PuzzleImage( 41 | name: 'Monkey', 42 | assetPath: 'assets/animals/monkey.png', 43 | soundPath: 'assets/sounds/monkey.mp3', 44 | ), 45 | PuzzleImage( 46 | name: 'Mouse', 47 | assetPath: 'assets/animals/mouse.png', 48 | soundPath: 'assets/sounds/mouse.mp3', 49 | ), 50 | PuzzleImage( 51 | name: 'Panda', 52 | assetPath: 'assets/animals/panda.png', 53 | soundPath: 'assets/sounds/panda.mp3', 54 | ), 55 | PuzzleImage( 56 | name: 'Penguin', 57 | assetPath: 'assets/animals/penguin.png', 58 | soundPath: 'assets/sounds/penguin.mp3', 59 | ), 60 | PuzzleImage( 61 | name: 'Tiger', 62 | assetPath: 'assets/animals/tiger.png', 63 | soundPath: 'assets/sounds/tiger.mp3', 64 | ), 65 | ]; 66 | 67 | Future decodeAsset(ByteData bytes) async { 68 | return decodeImage( 69 | bytes.buffer.asUint8List(), 70 | )!; 71 | } 72 | 73 | class SPlitData { 74 | final Image image; 75 | final int crossAxisCount; 76 | 77 | SPlitData(this.image, this.crossAxisCount); 78 | } 79 | 80 | Future> splitImage(SPlitData data) { 81 | final image = data.image; 82 | final crossAxisCount = data.crossAxisCount; 83 | final int length = (image.width / crossAxisCount).round(); 84 | List pieceList = []; 85 | 86 | for (int y = 0; y < crossAxisCount; y++) { 87 | for (int x = 0; x < crossAxisCount; x++) { 88 | pieceList.add( 89 | Uint8List.fromList( 90 | encodePng( 91 | copyCrop( 92 | image, 93 | x * length, 94 | y * length, 95 | length, 96 | length, 97 | ), 98 | ), 99 | ), 100 | ); 101 | } 102 | } 103 | return Future.value(pieceList); 104 | } 105 | 106 | class ImagesRepositoryImpl implements ImagesRepository { 107 | Map cache = {}; 108 | 109 | @override 110 | Future> split(String asset, int crossAxisCount) async { 111 | late Image image; 112 | if (cache.containsKey(asset)) { 113 | image = cache[asset]!; 114 | } else { 115 | final bytes = await rootBundle.load(asset); 116 | 117 | /// use compute because theimage package is a pure dart package 118 | /// so to avoid bad ui performance we do this task in a different 119 | /// isolate 120 | image = await compute(decodeAsset, bytes); 121 | 122 | final width = math.min(image.width, image.height); 123 | 124 | /// convert to square 125 | image = copyResizeCropSquare(image, width); 126 | cache[asset] = image; 127 | } 128 | 129 | final pieces = await compute( 130 | splitImage, 131 | SPlitData(image, crossAxisCount), 132 | ); 133 | 134 | return pieces; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /lib/src/data/repositories_impl/settings_repository_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:my_puzzle/src/domain/repositories/settings_repository.dart'; 2 | import 'package:shared_preferences/shared_preferences.dart'; 3 | 4 | const darkModeKey = 'darkModeKey'; 5 | 6 | class SettingsRepositoryImpl implements SettingsRepository { 7 | final SharedPreferences _preferences; 8 | 9 | SettingsRepositoryImpl(this._preferences); 10 | 11 | @override 12 | bool get isDarkMode => _preferences.getBool(darkModeKey) ?? false; 13 | 14 | @override 15 | Future updateDarkMode(bool isDark) { 16 | return _preferences.setBool(darkModeKey, isDark); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/src/domain/models/move_to.dart: -------------------------------------------------------------------------------- 1 | enum MoveTo { up, down, left, right } 2 | 3 | extension MoveToExt on String { 4 | /// hable the keyboard events and return 5 | /// the direction for the move 6 | MoveTo? get moveTo { 7 | switch (this) { 8 | case "W": 9 | case "Arrow Up": 10 | return MoveTo.up; 11 | case "S": 12 | case "Arrow Down": 13 | return MoveTo.down; 14 | case "A": 15 | case "Arrow Left": 16 | return MoveTo.left; 17 | case "D": 18 | case "Arrow Right": 19 | return MoveTo.right; 20 | } 21 | return null; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/src/domain/models/position.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | class Position extends Equatable { 4 | final int x; // column 5 | final int y; // row 6 | 7 | const Position({ 8 | required this.x, 9 | required this.y, 10 | }); 11 | 12 | @override 13 | List get props => [x, y]; 14 | } 15 | -------------------------------------------------------------------------------- /lib/src/domain/models/puzzle.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:equatable/equatable.dart'; 4 | import 'package:my_puzzle/src/domain/models/position.dart'; 5 | import 'package:my_puzzle/src/domain/models/puzzle_image.dart'; 6 | import 'tile.dart'; 7 | import 'dart:math' as math; 8 | 9 | class Puzzle extends Equatable { 10 | /// tiles of the current puzzle 11 | final List tiles; 12 | 13 | /// the empty position in the puzzle 14 | final Position emptyPosition; 15 | 16 | /// a split image for the current puzzle 17 | final List? segmentedImage; 18 | 19 | final PuzzleImage? image; 20 | 21 | const Puzzle._({ 22 | required this.tiles, 23 | required this.emptyPosition, 24 | required this.image, 25 | required this.segmentedImage, 26 | }); 27 | 28 | /// a tile can be moved if is in the same 29 | /// row or in the same column of emptyPosition 30 | bool canMove(Position tilePosition) { 31 | if (tilePosition.x == emptyPosition.x || tilePosition.y == emptyPosition.y) { 32 | return true; 33 | } 34 | return false; 35 | } 36 | 37 | /// moves one or more tile vertically or horizontally 38 | Puzzle move(Tile tile) { 39 | final copy = [...tiles]; 40 | // left or right 41 | if (tile.position.y == emptyPosition.y) { 42 | final row = tiles.where( 43 | (e) => e.position.y == emptyPosition.y, 44 | ); 45 | 46 | // right 47 | if (tile.position.x < emptyPosition.x) { 48 | for (final e in row) { 49 | if (e.position.x < tile.position.x || e.position.x > emptyPosition.x) { 50 | continue; 51 | } 52 | copy[e.value - 1] = e.move( 53 | Position( 54 | x: e.position.x + 1, 55 | y: e.position.y, 56 | ), 57 | ); 58 | } 59 | } else { 60 | // left 61 | for (final e in row) { 62 | if (e.position.x > tile.position.x || e.position.x < emptyPosition.x) { 63 | continue; 64 | } 65 | copy[e.value - 1] = e.move( 66 | Position( 67 | x: e.position.x - 1, 68 | y: e.position.y, 69 | ), 70 | ); 71 | } 72 | } 73 | } else { 74 | // top or bottom 75 | final column = tiles.where( 76 | (e) => e.position.x == emptyPosition.x, 77 | ); 78 | 79 | // bottom 80 | if (tile.position.y < emptyPosition.y) { 81 | for (final e in column) { 82 | if (e.position.y > emptyPosition.y || e.position.y < tile.position.y) { 83 | continue; 84 | } 85 | copy[e.value - 1] = e.move( 86 | Position( 87 | x: e.position.x, 88 | y: e.position.y + 1, 89 | ), 90 | ); 91 | } 92 | } else { 93 | // top 94 | for (final e in column) { 95 | if (e.position.y < emptyPosition.y || e.position.y > tile.position.y) { 96 | continue; 97 | } 98 | copy[e.value - 1] = e.move( 99 | Position( 100 | x: e.position.x, 101 | y: e.position.y - 1, 102 | ), 103 | ); 104 | } 105 | } 106 | } 107 | return Puzzle._( 108 | tiles: copy, 109 | emptyPosition: tile.position, 110 | image: image, 111 | segmentedImage: segmentedImage, 112 | ); 113 | } 114 | 115 | /// creates a sorted puzzle 116 | factory Puzzle.create( 117 | int crossAxisCount, { 118 | List? segmentedImage, 119 | PuzzleImage? image, 120 | }) { 121 | int value = 1; 122 | final tiles = []; 123 | 124 | final emptyPosition = Position( 125 | x: crossAxisCount, 126 | y: crossAxisCount, 127 | ); 128 | for (int y = 1; y <= crossAxisCount; y++) { 129 | for (int x = 1; x <= crossAxisCount; x++) { 130 | final add = !(x == crossAxisCount && y == crossAxisCount); 131 | if (add) { 132 | final position = Position(x: x, y: y); 133 | final tile = Tile( 134 | value: value, 135 | position: position, 136 | correctPosition: position, 137 | ); 138 | tiles.add(tile); 139 | value++; 140 | } 141 | } 142 | } 143 | 144 | return Puzzle._( 145 | tiles: tiles, 146 | emptyPosition: emptyPosition, 147 | image: image, 148 | segmentedImage: segmentedImage, 149 | ); 150 | } 151 | 152 | /// shuffle the puzzle tiles 153 | /// and return a solvable puzzle 154 | Puzzle shuffle() { 155 | final values = List.generate( 156 | tiles.length, 157 | (index) => index + 1, 158 | ); 159 | values.add(0); 160 | values.shuffle(); 161 | 162 | // [1,2,3,4,5,6,7,8,9,0] => [1,3,4,0,5,7,8,9,2,6] 163 | 164 | if (_isSolvable(values)) { 165 | int x = 1, y = 1; 166 | late Position emptyPosition; 167 | final copy = [...tiles]; 168 | final int crossAxisCount = math.sqrt(values.length).toInt(); 169 | 170 | for (int i = 0; i < values.length; i++) { 171 | final value = values[i]; 172 | final position = Position(x: x, y: y); 173 | if (value == 0) { 174 | emptyPosition = position; 175 | } else { 176 | copy[value - 1] = copy[value - 1].move( 177 | position, 178 | ); 179 | } 180 | 181 | if ((i + 1) % crossAxisCount == 0) { 182 | y++; 183 | x = 1; 184 | } else { 185 | x++; 186 | } 187 | } 188 | 189 | return Puzzle._( 190 | tiles: copy, 191 | emptyPosition: emptyPosition, 192 | image: image, 193 | segmentedImage: segmentedImage, 194 | ); 195 | } else { 196 | return shuffle(); 197 | } 198 | } 199 | 200 | /// check if a list of int is a solvable puzzle 201 | /// 202 | /// for more info check https://www.geeksforgeeks.org/check-instance-15-puzzle-solvable/ 203 | bool _isSolvable(List values) { 204 | final n = math.sqrt(values.length); 205 | 206 | /// inversions 207 | int inversions = 0; 208 | int y = 1; 209 | int emptyPositionY = 1; 210 | 211 | for (int i = 0; i < values.length; i++) { 212 | if (i > 0 && i % n == 0) { 213 | y++; 214 | } 215 | 216 | final current = values[i]; 217 | if (current == 1 || current == 0) { 218 | if (current == 0) { 219 | emptyPositionY = y; 220 | } 221 | continue; 222 | } 223 | for (int j = i + 1; j < values.length; j++) { 224 | final next = values[j]; 225 | 226 | if (current > next && next != 0) { 227 | inversions++; 228 | } 229 | } 230 | } 231 | 232 | // is odd 233 | if (n % 2 != 0) { 234 | return inversions % 2 == 0; 235 | } else { 236 | // is even 237 | 238 | final yFromBottom = n - emptyPositionY + 1; 239 | 240 | if (yFromBottom % 2 == 0) { 241 | return inversions % 2 != 0; 242 | } else { 243 | return inversions % 2 == 0; 244 | } 245 | } 246 | } 247 | 248 | /// check if the current puzzle is solved 249 | bool isSolved() { 250 | final crossAxisCount = math.sqrt(tiles.length + 1).toInt(); 251 | if (emptyPosition.x == crossAxisCount && emptyPosition.y == crossAxisCount) { 252 | for (final tile in tiles) { 253 | if (tile.position != tile.correctPosition) { 254 | return false; 255 | } 256 | } 257 | return true; 258 | } 259 | return false; 260 | } 261 | 262 | @override 263 | List get props => [ 264 | tiles, 265 | emptyPosition, 266 | image, 267 | segmentedImage, 268 | ]; 269 | } 270 | -------------------------------------------------------------------------------- /lib/src/domain/models/puzzle_image.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | class PuzzleImage extends Equatable { 4 | final String name; 5 | final String assetPath; 6 | final String soundPath; 7 | 8 | const PuzzleImage({ 9 | required this.name, 10 | required this.assetPath, 11 | required this.soundPath, 12 | }); 13 | 14 | @override 15 | List get props => [ 16 | name, 17 | assetPath, 18 | soundPath, 19 | ]; 20 | } 21 | -------------------------------------------------------------------------------- /lib/src/domain/models/tile.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:my_puzzle/src/domain/models/position.dart'; 3 | 4 | class Tile extends Equatable { 5 | final int value; 6 | final Position position; 7 | final Position correctPosition; 8 | 9 | const Tile({ 10 | required this.value, 11 | required this.position, 12 | required this.correctPosition, 13 | }); 14 | 15 | Tile move(Position newPosition) { 16 | return Tile( 17 | value: value, 18 | correctPosition: correctPosition, 19 | position: newPosition, 20 | ); 21 | } 22 | 23 | @override 24 | List get props => [ 25 | position, 26 | correctPosition, 27 | value, 28 | ]; 29 | } 30 | -------------------------------------------------------------------------------- /lib/src/domain/repositories/audio_repository.dart: -------------------------------------------------------------------------------- 1 | abstract class AudioRepository { 2 | Future playMove(); 3 | Future play(String asset); 4 | Future playWinner(); 5 | Future dispose(); 6 | } 7 | -------------------------------------------------------------------------------- /lib/src/domain/repositories/images_repository.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | abstract class ImagesRepository { 4 | /// segmentand image for a puzzle 5 | /// 6 | /// [asset] the asset path 7 | /// 8 | /// [crossAxisCount] number of columns and rows 9 | Future> split(String asset, int crossAxisCount); 10 | } 11 | -------------------------------------------------------------------------------- /lib/src/domain/repositories/settings_repository.dart: -------------------------------------------------------------------------------- 1 | abstract class SettingsRepository { 2 | bool get isDarkMode; 3 | Future updateDarkMode(bool isDark); 4 | } 5 | -------------------------------------------------------------------------------- /lib/src/inject_dependencies.dart: -------------------------------------------------------------------------------- 1 | import 'package:audio_session/audio_session.dart'; 2 | import 'package:get_it/get_it.dart'; 3 | import 'package:just_audio/just_audio.dart'; 4 | import 'package:shared_preferences/shared_preferences.dart'; 5 | 6 | import 'data/repositories_impl/audio_repository_impl.dart'; 7 | import 'data/repositories_impl/images_repository_impl.dart'; 8 | import 'data/repositories_impl/settings_repository_impl.dart'; 9 | import 'domain/repositories/audio_repository.dart'; 10 | import 'domain/repositories/images_repository.dart'; 11 | import 'domain/repositories/settings_repository.dart'; 12 | 13 | Future injectDependencies() async { 14 | final preferences = await SharedPreferences.getInstance(); 15 | final session = await AudioSession.instance; 16 | await session.configure( 17 | const AudioSessionConfiguration.music(), 18 | ); 19 | 20 | GetIt.I.registerLazySingleton( 21 | () => SettingsRepositoryImpl(preferences), 22 | ); 23 | 24 | GetIt.I.registerLazySingleton( 25 | () => ImagesRepositoryImpl(), 26 | ); 27 | 28 | GetIt.I.registerLazySingleton( 29 | () => AudioRepositoryImpl( 30 | AudioPlayer(), 31 | ), 32 | dispose: (repository) { 33 | repository.dispose(); 34 | }, 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /lib/src/l10n/intl_en.arb: -------------------------------------------------------------------------------- 1 | { 2 | "are_you_sure": "Are you sure?", 3 | "yes": "YES", 4 | "no": "NO", 5 | "dou_you_really": "Do you really want to restart the current puzzle", 6 | "movements": "moves", 7 | "restart": "Restart", 8 | "start": "START", 9 | "great_job": "GREAT JOB!", 10 | "completed": "You have completed the puzzle", 11 | "time": "Time", 12 | "ok": "OK", 13 | "privacy":"This is a free non-profit game, the game does not collect information of any kind from users or their traffic, it does not need internet.", 14 | "back_to_game":"back to the game" 15 | } -------------------------------------------------------------------------------- /lib/src/l10n/intl_es.arb: -------------------------------------------------------------------------------- 1 | { 2 | "are_you_sure": "¿Estás seguro?", 3 | "yes": "SI", 4 | "no": "NO", 5 | "dou_you_really": "Realmente quieres reiniciar el rompecabezas actual", 6 | "movements": "movimientos", 7 | "restart": "Reiniciar", 8 | "start": "INICIAR", 9 | "great_job": "¡GRAN TRABAJO!", 10 | "completed": "Has completado el rompecabezas", 11 | "time": "Tiempo", 12 | "ok": "ACEPTAR", 13 | "privacy":"Este es un juego gratuito sin fines de lucro, el juego no recolecta información de ningun tipo de los usuarios ni tampoco de su trafico, no necesita internet.", 14 | "back_to_game":"Regresar al juego" 15 | } -------------------------------------------------------------------------------- /lib/src/my_app.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_localizations/flutter_localizations.dart'; 3 | import 'package:my_puzzle/generated/l10n.dart'; 4 | import 'package:my_puzzle/src/ui/routes/app_routes.dart'; 5 | import 'package:my_puzzle/src/ui/routes/routes.dart'; 6 | import 'package:provider/provider.dart'; 7 | 8 | import 'ui/global/controllers/theme_controller.dart'; 9 | import 'ui/global/widgets/max_text_scale_factor.dart'; 10 | 11 | class MyApp extends StatelessWidget { 12 | const MyApp({Key? key}) : super(key: key); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return ChangeNotifierProvider( 17 | create: (_) => ThemeController(), 18 | child: Consumer( 19 | builder: (_, controller, __) => MaterialApp( 20 | builder: (_, page) => MaxTextScaleFactor( 21 | child: page!, 22 | ), 23 | localizationsDelegates: const [ 24 | S.delegate, 25 | GlobalMaterialLocalizations.delegate, 26 | GlobalWidgetsLocalizations.delegate, 27 | GlobalCupertinoLocalizations.delegate, 28 | ], 29 | supportedLocales: const [ 30 | Locale('en'), 31 | Locale('es'), 32 | ], 33 | debugShowCheckedModeBanner: false, 34 | themeMode: controller.themeMode, 35 | theme: controller.lightTheme, 36 | darkTheme: controller.darkTheme, 37 | initialRoute: Routes.splash, 38 | routes: appRoutes, 39 | ), 40 | ), 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/src/ui/global/controllers/theme_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:get_it/get_it.dart'; 4 | import 'package:google_fonts/google_fonts.dart'; 5 | import 'package:my_puzzle/src/domain/repositories/settings_repository.dart'; 6 | import 'package:my_puzzle/src/ui/utils/colors.dart'; 7 | 8 | 9 | class ThemeController extends ChangeNotifier { 10 | final SettingsRepository _settings = GetIt.I.get(); 11 | 12 | 13 | 14 | late bool _isDarkMode; 15 | bool get isDarkMode => _isDarkMode; 16 | 17 | ThemeMode get themeMode => isDarkMode ? ThemeMode.dark : ThemeMode.light; 18 | 19 | ThemeController() { 20 | _isDarkMode = _settings.isDarkMode; 21 | if (isDarkMode) { 22 | SystemChrome.setSystemUIOverlayStyle( 23 | SystemUiOverlayStyle.light, 24 | ); 25 | } 26 | } 27 | 28 | TextTheme get _textTheme { 29 | return GoogleFonts.latoTextTheme(); 30 | } 31 | 32 | ThemeData get lightTheme { 33 | return ThemeData.light().copyWith( 34 | brightness: Brightness.light, 35 | scaffoldBackgroundColor: Colors.white, 36 | appBarTheme: const AppBarTheme( 37 | systemOverlayStyle: SystemUiOverlayStyle.light, 38 | backgroundColor: Colors.white, 39 | elevation: 0, 40 | iconTheme: IconThemeData( 41 | color: lightColor, 42 | ), 43 | ), 44 | textTheme: _textTheme, 45 | primaryColorLight: lightColor, 46 | colorScheme: ColorScheme.fromSwatch( 47 | primarySwatch: MaterialColor(lightColor.value, swatch), 48 | ), 49 | textButtonTheme: TextButtonThemeData( 50 | style: TextButton.styleFrom( 51 | primary: darkColor, 52 | ), 53 | ), 54 | ); 55 | } 56 | 57 | ThemeData get darkTheme { 58 | return ThemeData.dark().copyWith( 59 | brightness: Brightness.dark, 60 | appBarTheme: const AppBarTheme( 61 | systemOverlayStyle: SystemUiOverlayStyle.light, 62 | backgroundColor: darkColor, 63 | elevation: 0, 64 | iconTheme: IconThemeData( 65 | color: Colors.white, 66 | ), 67 | ), 68 | textTheme: _textTheme 69 | .merge( 70 | ThemeData.dark().textTheme, 71 | ) 72 | .apply( 73 | fontFamily: _textTheme.bodyText1!.fontFamily, 74 | ), 75 | scaffoldBackgroundColor: const Color(0xff102027), 76 | primaryColorDark: darkColor, 77 | textSelectionTheme: const TextSelectionThemeData( 78 | cursorColor: darkColor, 79 | ), 80 | textButtonTheme: TextButtonThemeData( 81 | style: TextButton.styleFrom( 82 | primary: Colors.white, 83 | ), 84 | ), 85 | switchTheme: SwitchThemeData( 86 | thumbColor: MaterialStateProperty.all(acentColor), 87 | trackColor: MaterialStateProperty.all( 88 | acentColor.withOpacity(0.6), 89 | ), 90 | ), 91 | ); 92 | } 93 | 94 | void toggle() { 95 | _isDarkMode = !isDarkMode; 96 | _settings.updateDarkMode(isDarkMode); 97 | if (isDarkMode) { 98 | SystemChrome.setSystemUIOverlayStyle( 99 | SystemUiOverlayStyle.light, 100 | ); 101 | } else { 102 | SystemChrome.setSystemUIOverlayStyle( 103 | SystemUiOverlayStyle.dark, 104 | ); 105 | } 106 | notifyListeners(); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /lib/src/ui/global/widgets/max_text_scale_factor.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class MaxTextScaleFactor extends StatelessWidget { 4 | final Widget child; 5 | final double? max; 6 | const MaxTextScaleFactor({ 7 | Key? key, 8 | required this.child, 9 | this.max, 10 | }) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | final mediaQueryData = MediaQuery.of(context); 15 | 16 | var textScaleFactor = mediaQueryData.textScaleFactor; 17 | if (max == null && textScaleFactor > 1.4) { 18 | textScaleFactor = 1.4; 19 | } else if (max != null && textScaleFactor >= max!) { 20 | textScaleFactor = max!; 21 | } 22 | 23 | return MediaQuery( 24 | data: mediaQueryData.copyWith( 25 | textScaleFactor: textScaleFactor, 26 | ), 27 | child: child, 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/src/ui/global/widgets/my_icon_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:my_puzzle/src/ui/utils/colors.dart'; 3 | import 'package:my_puzzle/src/ui/utils/responsive.dart'; 4 | import 'package:my_puzzle/src/ui/utils/dark_mode_extension.dart'; 5 | 6 | class MyIconButton extends StatelessWidget { 7 | final VoidCallback onPressed; 8 | final IconData iconData; 9 | const MyIconButton({ 10 | Key? key, 11 | required this.onPressed, 12 | required this.iconData, 13 | }) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | final isDarkMode = context.isDarkMode; 18 | final size = Responsive.of(context).dp(6).clamp(40, 80).toDouble(); 19 | return MaterialButton( 20 | onPressed: onPressed, 21 | child: Icon( 22 | iconData, 23 | size: size * 0.4, 24 | ), 25 | minWidth: size, 26 | height: size, 27 | elevation: 0, 28 | shape: RoundedRectangleBorder( 29 | borderRadius: BorderRadius.circular(size / 2), 30 | ), 31 | color: lightColor.withOpacity(isDarkMode ? 0.2 : 1), 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/src/ui/global/widgets/my_text_icon_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:my_puzzle/src/ui/utils/colors.dart'; 3 | import 'package:my_puzzle/src/ui/utils/responsive.dart'; 4 | import '../../utils/dark_mode_extension.dart'; 5 | 6 | class MyTextIconButton extends StatelessWidget { 7 | final VoidCallback? onPressed; 8 | final Widget icon; 9 | final String label; 10 | final double height; 11 | const MyTextIconButton({ 12 | Key? key, 13 | required this.onPressed, 14 | required this.icon, 15 | required this.label, 16 | required this.height, 17 | }) : super(key: key); 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | final isDarkMode = context.isDarkMode; 22 | final padding = Responsive.of(context).dp(1.3); 23 | return TextButton.icon( 24 | onPressed: onPressed, 25 | style: ButtonStyle( 26 | backgroundColor: MaterialStateProperty.all( 27 | lightColor.withOpacity(isDarkMode ? 0.3 : 0.8), 28 | ), 29 | fixedSize: MaterialStateProperty.all( 30 | Size.fromHeight(height), 31 | ), 32 | padding: MaterialStateProperty.all( 33 | EdgeInsets.symmetric( 34 | horizontal: padding, 35 | ).copyWith(right: padding * 2), 36 | ), 37 | elevation: MaterialStateProperty.all(0), 38 | shadowColor: MaterialStateProperty.all( 39 | Colors.black38, 40 | ), 41 | shape: MaterialStateProperty.all( 42 | RoundedRectangleBorder( 43 | borderRadius: BorderRadius.circular(30), 44 | ), 45 | ), 46 | ), 47 | icon: icon, 48 | label: Text( 49 | label, 50 | style: const TextStyle( 51 | fontWeight: FontWeight.w600, 52 | ), 53 | ), 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/src/ui/global/widgets/up_to_down.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class UpToDown extends StatefulWidget { 4 | final Duration duration; 5 | final Widget child; 6 | const UpToDown({ 7 | Key? key, 8 | this.duration = const Duration(milliseconds: 300), 9 | required this.child, 10 | }) : super(key: key); 11 | 12 | @override 13 | _UpToDownState createState() => _UpToDownState(); 14 | } 15 | 16 | class _UpToDownState extends State with SingleTickerProviderStateMixin { 17 | late AnimationController _controller; 18 | late Animation _animation; 19 | 20 | @override 21 | void initState() { 22 | super.initState(); 23 | _controller = AnimationController( 24 | vsync: this, 25 | duration: widget.duration, 26 | ); 27 | // _animation = Tween( 28 | // begin: 0.0, 29 | // end: 1.0, 30 | // ).animate(_controller); 31 | _animation = CurvedAnimation( 32 | parent: _controller, 33 | curve: Curves.easeIn, 34 | ); 35 | _controller.forward(); 36 | } 37 | 38 | @override 39 | void dispose() { 40 | _controller.dispose(); 41 | super.dispose(); 42 | } 43 | 44 | @override 45 | Widget build(BuildContext context) { 46 | return LayoutBuilder( 47 | builder: (_, constraints) => AnimatedBuilder( 48 | animation: _animation, 49 | builder: (_, child) { 50 | final value = _animation.value; 51 | return Transform.translate( 52 | offset: Offset( 53 | 0, 54 | -(1 - value) * constraints.maxHeight * 0.1, 55 | ), 56 | child: Opacity( 57 | opacity: value.clamp(0, 1), 58 | child: child!, 59 | ), 60 | ); 61 | }, 62 | child: widget.child, 63 | ), 64 | ); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/src/ui/icons/puzzle_icons.dart: -------------------------------------------------------------------------------- 1 | /// Flutter icons PuzzleIcons 2 | /// Copyright (C) 2022 by original authors @ fluttericon.com, fontello.com 3 | /// This font was generated by FlutterIcon.com, which is derived from Fontello. 4 | /// 5 | /// To use this font, place it in your fonts/ directory and include the 6 | /// following in your pubspec.yaml 7 | /// 8 | /// flutter: 9 | /// fonts: 10 | /// - family: PuzzleIcons 11 | /// fonts: 12 | /// - asset: fonts/PuzzleIcons.ttf 13 | /// 14 | /// 15 | /// 16 | // ignore_for_file: constant_identifier_names 17 | 18 | import 'package:flutter/widgets.dart'; 19 | 20 | class PuzzleIcons { 21 | PuzzleIcons._(); 22 | 23 | static const _kFontFam = 'PuzzleIcons'; 24 | static const String? _kFontPkg = null; 25 | 26 | static const IconData watch = IconData(0xe800, fontFamily: _kFontFam, fontPackage: _kFontPkg); 27 | static const IconData mute = IconData(0xe801, fontFamily: _kFontFam, fontPackage: _kFontPkg); 28 | static const IconData sound = IconData(0xe802, fontFamily: _kFontFam, fontPackage: _kFontPkg); 29 | static const IconData vibration = IconData(0xe803, fontFamily: _kFontFam, fontPackage: _kFontPkg); 30 | static const IconData vibration_off = IconData(0xe804, fontFamily: _kFontFam, fontPackage: _kFontPkg); 31 | static const IconData heart = IconData(0xe805, fontFamily: _kFontFam, fontPackage: _kFontPkg); 32 | static const IconData grid = IconData(0xe806, fontFamily: _kFontFam, fontPackage: _kFontPkg); 33 | static const IconData brightness = IconData(0xe807, fontFamily: _kFontFam, fontPackage: _kFontPkg); 34 | static const IconData movements = IconData(0xe808, fontFamily: _kFontFam, fontPackage: _kFontPkg); 35 | static const IconData dark_mode = IconData(0xe809, fontFamily: _kFontFam, fontPackage: _kFontPkg); 36 | } 37 | -------------------------------------------------------------------------------- /lib/src/ui/pages/game/controller/game_controller.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:typed_data'; 3 | 4 | import 'package:flutter/foundation.dart'; 5 | import 'package:flutter/services.dart'; 6 | import 'package:get_it/get_it.dart'; 7 | import 'package:my_puzzle/src/domain/models/move_to.dart'; 8 | import 'package:my_puzzle/src/domain/models/puzzle.dart'; 9 | import 'package:my_puzzle/src/domain/models/puzzle_image.dart'; 10 | import 'package:my_puzzle/src/domain/models/tile.dart'; 11 | import 'package:my_puzzle/src/domain/repositories/audio_repository.dart'; 12 | import 'package:my_puzzle/src/domain/repositories/images_repository.dart'; 13 | import 'package:my_puzzle/src/ui/pages/game/controller/game_state.dart'; 14 | 15 | class GameController extends ChangeNotifier { 16 | final ImagesRepository _imagesRepository = GetIt.I.get(); 17 | final AudioRepository audioRepository = GetIt.I.get(); 18 | 19 | GameState _state = GameState( 20 | crossAxisCount: 3, 21 | puzzle: Puzzle.create(3), 22 | solved: false, 23 | moves: 0, 24 | status: GameStatus.created, 25 | sound: true, 26 | vibration: true, 27 | ); 28 | 29 | final ValueNotifier time = ValueNotifier(0); 30 | 31 | final StreamController _streamController = StreamController.broadcast(); 32 | 33 | Stream get onFinish => _streamController.stream; 34 | 35 | Timer? _timer; 36 | 37 | GameState get state => _state; 38 | 39 | Puzzle get puzzle => _state.puzzle; 40 | 41 | void onTileTapped(Tile tile) { 42 | final canMove = puzzle.canMove(tile.position); 43 | 44 | /// if the tile can be moved 45 | if (canMove) { 46 | // move the tile or multiples tiles 47 | final newPuzzle = puzzle.move(tile); 48 | 49 | // check if the puzzle was solved 50 | final solved = newPuzzle.isSolved(); 51 | _state = state.copyWith( 52 | puzzle: newPuzzle, 53 | moves: state.moves + 1, 54 | status: solved ? GameStatus.solved : state.status, 55 | ); 56 | notifyListeners(); 57 | 58 | if (state.vibration) { 59 | HapticFeedback.lightImpact(); 60 | } 61 | 62 | if (state.sound) { 63 | // play a sound 64 | audioRepository.playMove(); 65 | } 66 | 67 | if (solved) { 68 | _timer?.cancel(); 69 | 70 | // notify to the game view 71 | _streamController.sink.add(null); 72 | } 73 | } 74 | } 75 | 76 | /// shuffle the current puzzle 77 | void shuffle() { 78 | if (_timer != null) { 79 | time.value = 0; 80 | _timer!.cancel(); 81 | } 82 | _state = state.copyWith( 83 | puzzle: puzzle.shuffle(), 84 | status: GameStatus.playing, 85 | moves: 0, 86 | ); 87 | notifyListeners(); 88 | _timer = Timer.periodic( 89 | const Duration(seconds: 1), 90 | (_) { 91 | time.value++; 92 | }, 93 | ); 94 | } 95 | 96 | /// change the current size of the puzzle 97 | /// 98 | /// 99 | /// [crossAxisCount] number of rows and columns 100 | /// 101 | /// [image] is different of null when the puzzle 102 | /// has to be a segmented image 103 | Future changeGrid( 104 | int crossAxisCount, 105 | PuzzleImage? image, 106 | ) async { 107 | _timer?.cancel(); 108 | time.value = 0; 109 | 110 | List? segmentedImage; 111 | 112 | if (image != null) { 113 | segmentedImage = await _imagesRepository.split( 114 | image.assetPath, 115 | crossAxisCount, 116 | ); 117 | } 118 | 119 | /// reset the game with a new puzzle 120 | final newState = GameState( 121 | crossAxisCount: crossAxisCount, 122 | puzzle: Puzzle.create( 123 | crossAxisCount, 124 | segmentedImage: segmentedImage, 125 | image: image, 126 | ), 127 | solved: false, 128 | moves: 0, 129 | status: GameStatus.created, 130 | sound: state.sound, 131 | vibration: state.vibration, 132 | ); 133 | _state = newState; 134 | notifyListeners(); 135 | } 136 | 137 | void toggleSound() { 138 | _state = state.copyWith( 139 | sound: !state.sound, 140 | ); 141 | notifyListeners(); 142 | } 143 | 144 | void toggleVibration() { 145 | _state = state.copyWith( 146 | vibration: !state.vibration, 147 | ); 148 | notifyListeners(); 149 | } 150 | 151 | /// handle the keyboard events and check if 152 | /// the keyboard action can move a tile using as 153 | /// reference the empty position 154 | void onMoveByKeyboard(MoveTo moveTo) { 155 | final index = _handleKeyboard(moveTo, state); 156 | 157 | if (index != null) { 158 | onTileTapped( 159 | puzzle.tiles[index], 160 | ); 161 | } 162 | } 163 | 164 | /// handle the keyboard events and check if 165 | /// the keyboard action can move a tile using as 166 | /// reference the empty position 167 | /// 168 | /// return [int] the index tile to be moved, if any 169 | /// tile can be moved the value returned is null 170 | int? _handleKeyboard(MoveTo moveTo, GameState state) { 171 | final crossAxisCount = state.crossAxisCount; 172 | final emptyPosition = puzzle.emptyPosition; 173 | final tiles = puzzle.tiles; 174 | 175 | /// will store the tile index to be moved 176 | int? index; 177 | 178 | switch (moveTo) { 179 | case MoveTo.up: 180 | if (emptyPosition.y + 1 <= crossAxisCount) { 181 | index = tiles.indexWhere( 182 | (e) { 183 | return e.position.x == emptyPosition.x && e.position.y == emptyPosition.y + 1; 184 | }, 185 | ); 186 | } 187 | break; 188 | case MoveTo.down: 189 | if (emptyPosition.y > 0) { 190 | index = tiles.indexWhere( 191 | (e) { 192 | return e.position.x == emptyPosition.x && e.position.y == emptyPosition.y - 1; 193 | }, 194 | ); 195 | } 196 | break; 197 | case MoveTo.left: 198 | if (emptyPosition.x >= 1) { 199 | index = tiles.indexWhere( 200 | (e) { 201 | return e.position.x - 1 == emptyPosition.x && e.position.y == emptyPosition.y; 202 | }, 203 | ); 204 | } 205 | break; 206 | case MoveTo.right: 207 | if (emptyPosition.x <= crossAxisCount) { 208 | index = tiles.indexWhere( 209 | (e) { 210 | return e.position.x + 1 == emptyPosition.x && e.position.y == emptyPosition.y; 211 | }, 212 | ); 213 | } 214 | break; 215 | } 216 | 217 | return index; 218 | } 219 | 220 | /// release memory 221 | @override 222 | void dispose() { 223 | _streamController.close(); 224 | _timer?.cancel(); 225 | super.dispose(); 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /lib/src/ui/pages/game/controller/game_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:my_puzzle/src/domain/models/puzzle.dart'; 3 | 4 | enum GameStatus { 5 | created, 6 | playing, 7 | solved, 8 | } 9 | 10 | class GameState extends Equatable { 11 | /// numbers of columns and rows 12 | final int crossAxisCount; 13 | final Puzzle puzzle; 14 | final bool solved; 15 | final int moves; 16 | final GameStatus status; 17 | final bool vibration; 18 | final bool sound; 19 | 20 | const GameState({ 21 | required this.crossAxisCount, 22 | required this.puzzle, 23 | required this.solved, 24 | required this.moves, 25 | required this.status, 26 | required this.vibration, 27 | required this.sound, 28 | }) : assert(crossAxisCount >= 3); 29 | 30 | GameState copyWith({ 31 | int? crossAxisCount, 32 | int? moves, 33 | Puzzle? puzzle, 34 | bool? solved, 35 | GameStatus? status, 36 | bool? sound, 37 | bool? vibration, 38 | }) { 39 | return GameState( 40 | status: status ?? this.status, 41 | moves: moves ?? this.moves, 42 | crossAxisCount: crossAxisCount ?? this.crossAxisCount, 43 | puzzle: puzzle ?? this.puzzle, 44 | solved: solved ?? this.solved, 45 | sound: sound ?? this.sound, 46 | vibration: vibration ?? this.vibration, 47 | ); 48 | } 49 | 50 | @override 51 | List get props => [ 52 | moves, 53 | crossAxisCount, 54 | puzzle, 55 | solved, 56 | status, 57 | sound, 58 | vibration, 59 | ]; 60 | } 61 | -------------------------------------------------------------------------------- /lib/src/ui/pages/game/game_view.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/services.dart'; 5 | import 'package:my_puzzle/src/domain/models/move_to.dart'; 6 | import 'package:my_puzzle/src/ui/pages/game/controller/game_controller.dart'; 7 | import 'package:my_puzzle/src/ui/pages/game/widgets/background.dart'; 8 | import 'package:my_puzzle/src/ui/pages/game/widgets/game_app_bar.dart'; 9 | import 'package:my_puzzle/src/ui/pages/game/widgets/game_buttons.dart'; 10 | import 'package:my_puzzle/src/ui/pages/game/widgets/puzzle_interactor.dart'; 11 | import 'package:my_puzzle/src/ui/pages/game/widgets/puzzle_options.dart'; 12 | import 'package:my_puzzle/src/ui/pages/game/widgets/time_and_moves.dart'; 13 | import 'package:my_puzzle/src/ui/pages/game/widgets/winner_dialog.dart'; 14 | import 'package:my_puzzle/src/ui/utils/responsive.dart'; 15 | import 'package:provider/provider.dart'; 16 | 17 | class GameView extends StatelessWidget { 18 | const GameView({Key? key}) : super(key: key); 19 | 20 | void _onKeyBoardEvent(BuildContext context, RawKeyEvent event) { 21 | if (event is RawKeyDownEvent) { 22 | final moveTo = event.logicalKey.keyLabel.moveTo; 23 | if (moveTo != null) { 24 | context.read().onMoveByKeyboard(moveTo); 25 | } 26 | } 27 | } 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | final responsive = Responsive.of(context); 32 | final width = responsive.width; 33 | return ChangeNotifierProvider( 34 | create: (_) { 35 | final controller = GameController(); 36 | controller.onFinish.listen( 37 | (_) { 38 | Timer( 39 | const Duration( 40 | milliseconds: 200, 41 | ), 42 | () { 43 | showWinnerDialog( 44 | context, 45 | moves: controller.state.moves, 46 | time: controller.time.value, 47 | ); 48 | }, 49 | ); 50 | }, 51 | ); 52 | return controller; 53 | }, 54 | builder: (context, child) => RawKeyboardListener( 55 | autofocus: true, 56 | includeSemantics: false, 57 | focusNode: FocusNode(), 58 | onKey: (event) => _onKeyBoardEvent(context, event), 59 | child: child!, 60 | ), 61 | child: GameBackground( 62 | child: Scaffold( 63 | backgroundColor: Colors.transparent, 64 | body: SafeArea( 65 | child: OrientationBuilder( 66 | builder: (_, orientation) { 67 | final isPortrait = orientation == Orientation.portrait; 68 | 69 | return Column( 70 | mainAxisAlignment: MainAxisAlignment.center, 71 | children: [ 72 | const GameAppBar(), 73 | Expanded( 74 | child: LayoutBuilder( 75 | builder: (_, constraints) { 76 | final height = constraints.maxHeight; 77 | final puzzleHeight = (isPortrait ? height * 0.45 : height * 0.5) 78 | .clamp(250, 700) 79 | .toDouble(); 80 | final optionsHeight = (isPortrait ? height * 0.25 : height * 0.2) 81 | .clamp(120, 200) 82 | .toDouble(); 83 | 84 | return SizedBox( 85 | height: height, 86 | child: SingleChildScrollView( 87 | child: Column( 88 | children: [ 89 | SizedBox( 90 | height: optionsHeight, 91 | child: PuzzleOptions( 92 | width: width, 93 | ), 94 | ), 95 | SizedBox( 96 | height: height * 0.1, 97 | ), 98 | const TimeAndMoves(), 99 | Padding( 100 | padding: const EdgeInsets.all(20), 101 | child: SizedBox( 102 | height: puzzleHeight, 103 | child: const AspectRatio( 104 | aspectRatio: 1, 105 | child: PuzzleInteractor(), 106 | ), 107 | ), 108 | ), 109 | const GameButtons(), 110 | ], 111 | ), 112 | ), 113 | ); 114 | }, 115 | ), 116 | ) 117 | ], 118 | ); 119 | }, 120 | ), 121 | ), 122 | ), 123 | ), 124 | ); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /lib/src/ui/pages/game/widgets/background.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import '../../../utils/colors.dart'; 5 | import '../../../utils/dark_mode_extension.dart'; 6 | 7 | class GameBackground extends StatelessWidget { 8 | final Widget child; 9 | const GameBackground({ 10 | Key? key, 11 | required this.child, 12 | }) : super(key: key); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | final bool isDarkMode = context.isDarkMode; 17 | // Colors.primaries[i].withOpacity(0.2); 18 | return Stack( 19 | children: [ 20 | Positioned.fill( 21 | child: Container( 22 | color: isDarkMode ? darkColor : lightColor2, 23 | ), 24 | ), 25 | Positioned( 26 | bottom: 0, 27 | left: 0, 28 | right: 0, 29 | child: Transform.rotate( 30 | angle: pi, 31 | child: Image.asset( 32 | 'assets/images/jungle.png', 33 | color: Colors.primaries[8].withOpacity(0.1), 34 | ), 35 | ), 36 | ), 37 | Positioned.fill( 38 | child: child, 39 | ), 40 | ], 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/src/ui/pages/game/widgets/confirm_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:my_puzzle/generated/l10n.dart'; 3 | import 'package:my_puzzle/src/ui/global/widgets/up_to_down.dart'; 4 | import 'package:my_puzzle/src/ui/utils/colors.dart'; 5 | import 'package:my_puzzle/src/ui/utils/dark_mode_extension.dart'; 6 | 7 | 8 | Future showConfirmDialog(BuildContext context) async { 9 | final result = await showDialog( 10 | context: context, 11 | builder: (context) { 12 | final texts = S.current; 13 | final isDarkMode = context.isDarkMode; 14 | return Center( 15 | child: UpToDown( 16 | child: Material( 17 | shape: RoundedRectangleBorder( 18 | borderRadius: BorderRadius.circular(20), 19 | ), 20 | color: isDarkMode ? darkColor : lightColor, 21 | child: SizedBox( 22 | width: 320, 23 | child: Column( 24 | mainAxisSize: MainAxisSize.min, 25 | children: [ 26 | Padding( 27 | padding: const EdgeInsets.symmetric( 28 | vertical: 20, 29 | ), 30 | child: Transform.scale( 31 | scale: 1.5, 32 | child: Image.asset( 33 | 'assets/images/relax-dash.png', 34 | width: 200, 35 | ), 36 | ), 37 | ), 38 | Text( 39 | texts.are_you_sure, 40 | style: const TextStyle( 41 | fontSize: 25, 42 | ), 43 | ), 44 | Padding( 45 | padding: const EdgeInsets.symmetric( 46 | horizontal: 20, 47 | ), 48 | child: Text( 49 | texts.dou_you_really, 50 | style: const TextStyle( 51 | fontSize: 16, 52 | ), 53 | textAlign: TextAlign.center, 54 | ), 55 | ), 56 | const SizedBox(height: 5), 57 | Row( 58 | mainAxisSize: MainAxisSize.min, 59 | children: [ 60 | Expanded( 61 | child: TextButton( 62 | onPressed: () => Navigator.pop( 63 | context, 64 | true, 65 | ), 66 | child: Text(texts.yes), 67 | ), 68 | ), 69 | Container( 70 | height: 20, 71 | width: 1, 72 | color: (isDarkMode ? Colors.white : darkColor).withOpacity(0.2), 73 | ), 74 | Expanded( 75 | child: TextButton( 76 | onPressed: () => Navigator.pop(context), 77 | child: Text(texts.no), 78 | ), 79 | ), 80 | ], 81 | ), 82 | ], 83 | ), 84 | ), 85 | ), 86 | ), 87 | ); 88 | }, 89 | ); 90 | return result ?? false; 91 | } 92 | -------------------------------------------------------------------------------- /lib/src/ui/pages/game/widgets/game_app_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:my_puzzle/src/ui/global/controllers/theme_controller.dart'; 3 | import 'package:my_puzzle/src/ui/global/widgets/my_icon_button.dart'; 4 | import 'package:my_puzzle/src/ui/icons/puzzle_icons.dart'; 5 | import 'package:my_puzzle/src/ui/pages/game/controller/game_controller.dart'; 6 | import 'package:my_puzzle/src/ui/utils/dark_mode_extension.dart'; 7 | import 'package:my_puzzle/src/ui/utils/platform.dart'; 8 | import 'package:provider/provider.dart'; 9 | 10 | const whiteFlutterLogoColorFilter = ColorFilter.matrix( 11 | [1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0], 12 | ); 13 | 14 | class GameAppBar extends StatelessWidget { 15 | const GameAppBar({Key? key}) : super(key: key); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | final logo = isIOS 20 | ? const Icon( 21 | PuzzleIcons.heart, 22 | color: Colors.redAccent, 23 | size: 30, 24 | ) 25 | : const FlutterLogo( 26 | size: 40, 27 | ); 28 | 29 | return SafeArea( 30 | child: Padding( 31 | padding: const EdgeInsets.symmetric( 32 | horizontal: 15, 33 | vertical: 10, 34 | ), 35 | child: Row( 36 | children: [ 37 | FittedBox( 38 | child: Text.rich( 39 | TextSpan( 40 | text: "${isIOS ? "Created" : "Powered"}\n", 41 | children: const [ 42 | TextSpan( 43 | text: "with", 44 | style: TextStyle( 45 | fontSize: 24, 46 | height: 1, 47 | ), 48 | ), 49 | ], 50 | ), 51 | textAlign: TextAlign.right, 52 | ), 53 | ), 54 | const SizedBox(width: 5), 55 | if (context.isDarkMode) 56 | ColorFiltered( 57 | colorFilter: whiteFlutterLogoColorFilter, 58 | child: logo, 59 | ) 60 | else 61 | logo, 62 | const Spacer(), 63 | Consumer( 64 | builder: (_, controller, __) => Row( 65 | children: [ 66 | MyIconButton( 67 | onPressed: controller.toggleVibration, 68 | iconData: controller.state.vibration 69 | ? PuzzleIcons.vibration 70 | : PuzzleIcons.vibration_off, 71 | ), 72 | const SizedBox(width: 10), 73 | MyIconButton( 74 | onPressed: controller.toggleSound, 75 | iconData: controller.state.sound ? PuzzleIcons.sound : PuzzleIcons.mute, 76 | ), 77 | ], 78 | ), 79 | ), 80 | const SizedBox(width: 10), 81 | Consumer( 82 | builder: (_, controller, __) => MyIconButton( 83 | onPressed: controller.toggle, 84 | iconData: controller.isDarkMode ? PuzzleIcons.dark_mode : PuzzleIcons.brightness, 85 | ), 86 | ), 87 | ], 88 | ), 89 | ), 90 | ); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /lib/src/ui/pages/game/widgets/game_buttons.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:my_puzzle/generated/l10n.dart'; 5 | import 'package:my_puzzle/src/ui/global/widgets/my_text_icon_button.dart'; 6 | import 'package:my_puzzle/src/ui/pages/game/controller/game_controller.dart'; 7 | import 'package:my_puzzle/src/ui/pages/game/controller/game_state.dart'; 8 | import 'package:my_puzzle/src/ui/pages/game/widgets/confirm_dialog.dart'; 9 | import 'package:my_puzzle/src/ui/utils/colors.dart'; 10 | import 'package:my_puzzle/src/ui/utils/dark_mode_extension.dart'; 11 | import 'package:my_puzzle/src/ui/utils/responsive.dart'; 12 | import 'package:provider/provider.dart'; 13 | 14 | class GameButtons extends StatelessWidget { 15 | const GameButtons({Key? key}) : super(key: key); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | final controller = context.watch(); 20 | final state = controller.state; 21 | final isDarkMode = context.isDarkMode; 22 | final responsive = Responsive.of(context); 23 | final buttonHeight = responsive.dp(3).clamp(kMinInteractiveDimension, 100).toDouble(); 24 | 25 | return Padding( 26 | padding: const EdgeInsets.all(10).copyWith( 27 | bottom: 20, 28 | ), 29 | child: Row( 30 | mainAxisAlignment: MainAxisAlignment.center, 31 | children: [ 32 | MyTextIconButton( 33 | height: buttonHeight, 34 | onPressed: () => _reset(context), 35 | icon: const Icon( 36 | Icons.replay_rounded, 37 | ), 38 | label: state.status == GameStatus.created ? S.current.start : S.current.restart, 39 | ), 40 | const SizedBox(width: 20), 41 | Container( 42 | decoration: BoxDecoration( 43 | color: lightColor.withOpacity(isDarkMode ? 0.3 : 1), 44 | borderRadius: BorderRadius.circular(30), 45 | ), 46 | child: DropdownButtonHideUnderline( 47 | child: DropdownButton( 48 | itemHeight: buttonHeight, 49 | dropdownColor: (isDarkMode ? darkColor : lightColor).withOpacity(0.9), 50 | borderRadius: BorderRadius.circular(30), 51 | icon: Padding( 52 | padding: const EdgeInsets.only( 53 | right: 10, 54 | ), 55 | child: Transform.rotate( 56 | angle: 90 * pi / 180, 57 | child: Icon( 58 | Icons.arrow_forward_ios_rounded, 59 | color: isDarkMode ? Colors.white : darkColor, 60 | ), 61 | ), 62 | ), 63 | items: [3, 4, 5, 6] 64 | .map( 65 | (e) => DropdownMenuItem( 66 | child: Padding( 67 | padding: const EdgeInsets.symmetric( 68 | horizontal: 5, 69 | ).copyWith(left: 15), 70 | child: Text( 71 | "${e}x$e ", 72 | textAlign: TextAlign.center, 73 | style: TextStyle( 74 | color: isDarkMode ? Colors.white : darkColor, 75 | fontWeight: FontWeight.w600, 76 | ), 77 | ), 78 | ), 79 | value: e, 80 | ), 81 | ) 82 | .toList(), 83 | onChanged: (crossAxisCount) { 84 | if (crossAxisCount != null && crossAxisCount != state.crossAxisCount) { 85 | controller.changeGrid( 86 | crossAxisCount, 87 | controller.puzzle.image, 88 | ); 89 | } 90 | }, 91 | value: state.crossAxisCount, 92 | ), 93 | ), 94 | ), 95 | ], 96 | ), 97 | ); 98 | } 99 | 100 | Future _reset(BuildContext context) async { 101 | final controller = context.read(); 102 | final state = controller.state; 103 | if (state.moves == 0 || state.status == GameStatus.solved) { 104 | controller.shuffle(); 105 | } else { 106 | final isOk = await showConfirmDialog(context); 107 | if (isOk) { 108 | controller.shuffle(); 109 | } 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /lib/src/ui/pages/game/widgets/puzzle_interactor.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:my_puzzle/src/ui/pages/game/controller/game_controller.dart'; 3 | import 'package:my_puzzle/src/ui/pages/game/controller/game_state.dart'; 4 | import 'package:my_puzzle/src/ui/pages/game/widgets/puzzle_tile.dart'; 5 | import 'package:my_puzzle/src/ui/utils/colors.dart'; 6 | import 'package:my_puzzle/src/ui/utils/dark_mode_extension.dart'; 7 | import 'package:provider/provider.dart'; 8 | 9 | /// render the puzzle 10 | class PuzzleInteractor extends StatelessWidget { 11 | const PuzzleInteractor({Key? key}) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Container( 16 | padding: const EdgeInsets.all(1), 17 | decoration: BoxDecoration( 18 | color: lightColor.withOpacity(context.isDarkMode ? 0.1 : 0.9), 19 | borderRadius: BorderRadius.circular(4), 20 | ), 21 | child: LayoutBuilder( 22 | builder: (context, constraints) { 23 | final controller = context.watch(); 24 | final state = controller.state; 25 | final tileSize = constraints.maxWidth / state.crossAxisCount; 26 | 27 | final puzzle = state.puzzle; 28 | 29 | /// the puzzle is locked is the game status is not playing 30 | return AbsorbPointer( 31 | absorbing: state.status != GameStatus.playing, 32 | child: Stack( 33 | children: puzzle.tiles 34 | .map( 35 | (e) => PuzzleTile( 36 | tile: e, 37 | size: tileSize, 38 | gameStatus: state.status, 39 | showNumbersInTileImage: state.crossAxisCount > 4, 40 | onTap: () => controller.onTileTapped(e), 41 | imageTile: puzzle.segmentedImage != null 42 | ? puzzle.segmentedImage![e.value - 1] 43 | : null, 44 | ), 45 | ) 46 | .toList(), 47 | ), 48 | ); 49 | }, 50 | ), 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/src/ui/pages/game/widgets/puzzle_options.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:my_puzzle/src/data/repositories_impl/images_repository_impl.dart'; 5 | import 'package:my_puzzle/src/ui/pages/game/controller/game_controller.dart'; 6 | import 'package:my_puzzle/src/ui/utils/colors.dart'; 7 | import 'package:provider/provider.dart'; 8 | import 'package:smooth_page_indicator/smooth_page_indicator.dart'; 9 | import '../../../utils/dark_mode_extension.dart'; 10 | 11 | /// widget to show a horizontal pageview with the posible 12 | /// images for the puzzle 13 | class PuzzleOptions extends StatefulWidget { 14 | final double width; 15 | const PuzzleOptions({Key? key, required this.width}) : super(key: key); 16 | 17 | @override 18 | State createState() => _PuzzleOptionsState(); 19 | } 20 | 21 | class _PuzzleOptionsState extends State with AutomaticKeepAliveClientMixin { 22 | late PageController _pageController; 23 | 24 | int _page = 0; 25 | 26 | @override 27 | void initState() { 28 | super.initState(); 29 | 30 | _setViewportFraction(); 31 | } 32 | 33 | void _setViewportFraction() { 34 | double viewportFraction = 0.5; 35 | if (widget.width >= 400 && widget.width < 600) { 36 | viewportFraction = 0.4; 37 | } else if (widget.width >= 600) { 38 | viewportFraction = 0.2; 39 | } 40 | 41 | _pageController = PageController( 42 | viewportFraction: viewportFraction, 43 | ); 44 | } 45 | 46 | @override 47 | void didUpdateWidget(covariant PuzzleOptions oldWidget) { 48 | super.didUpdateWidget(oldWidget); 49 | if (oldWidget.width != widget.width) { 50 | _setViewportFraction(); 51 | setState(() {}); 52 | } 53 | } 54 | 55 | @override 56 | void dispose() { 57 | _pageController.dispose(); 58 | super.dispose(); 59 | } 60 | 61 | @override 62 | Widget build(BuildContext context) { 63 | super.build(context); 64 | 65 | final isDarkMode = context.isDarkMode; 66 | 67 | return Stack( 68 | children: [ 69 | Padding( 70 | padding: const EdgeInsets.only(top: 15, bottom: 20), 71 | child: NotificationListener( 72 | onNotification: (t) { 73 | if (t is ScrollEndNotification) { 74 | if (_page != _pageController.page) { 75 | _page = _pageController.page!.round(); 76 | final image = puzzleOptions[_page]; 77 | final controller = context.read(); 78 | controller.changeGrid( 79 | controller.state.crossAxisCount, 80 | image.name != 'Numeric' ? image : null, 81 | ); 82 | 83 | if (image.name != 'Numeric' && controller.state.sound) { 84 | controller.audioRepository.play( 85 | image.soundPath, 86 | ); 87 | } 88 | } 89 | } 90 | return true; 91 | }, 92 | child: PageView.builder( 93 | scrollDirection: Axis.horizontal, 94 | controller: _pageController, 95 | itemBuilder: (_, index) { 96 | final item = puzzleOptions[index]; 97 | return Padding( 98 | padding: const EdgeInsets.all(8.0), 99 | child: Material( 100 | color: lightColor.withOpacity(isDarkMode ? 0.3 : 0.5), 101 | shape: RoundedRectangleBorder( 102 | borderRadius: BorderRadius.circular(10), 103 | ), 104 | elevation: 0, 105 | child: ClipRRect( 106 | borderRadius: BorderRadius.circular(10), 107 | child: Padding( 108 | padding: const EdgeInsets.all(8.0), 109 | child: Image.asset( 110 | item.assetPath, 111 | ), 112 | ), 113 | ), 114 | ), 115 | ); 116 | }, 117 | itemCount: puzzleOptions.length, 118 | ), 119 | ), 120 | ), 121 | Align( 122 | alignment: Alignment.bottomCenter, 123 | child: SmoothPageIndicator( 124 | controller: _pageController, // PageController 125 | count: puzzleOptions.length, 126 | effect: CustomizableEffect( 127 | activeDotDecoration: DotDecoration( 128 | width: 32, 129 | height: 12, 130 | color: Colors.indigo, 131 | rotationAngle: 180, 132 | verticalOffset: 0, 133 | borderRadius: BorderRadius.circular(24), 134 | ), 135 | dotDecoration: DotDecoration( 136 | width: 24, 137 | height: 12, 138 | color: Colors.grey, 139 | borderRadius: BorderRadius.circular(16), 140 | verticalOffset: 0, 141 | ), 142 | spacing: 6.0, 143 | activeColorOverride: (i) => Colors.primaries[i], 144 | inActiveColorOverride: (i) => Colors.primaries[i].withOpacity(0.2), 145 | ), 146 | onDotClicked: (index) { 147 | _pageController.animateToPage( 148 | index, 149 | duration: const Duration(milliseconds: 300), 150 | curve: Curves.ease, 151 | ); 152 | }, 153 | ), 154 | ), 155 | Align( 156 | alignment: Alignment.topCenter, 157 | child: Transform.rotate( 158 | angle: 90 * pi / 180, 159 | child: const Icon( 160 | Icons.play_arrow_rounded, 161 | size: 50, 162 | color: acentColor, 163 | ), 164 | ), 165 | ), 166 | ], 167 | ); 168 | } 169 | 170 | @override 171 | bool get wantKeepAlive => true; 172 | } 173 | -------------------------------------------------------------------------------- /lib/src/ui/pages/game/widgets/puzzle_tile.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | import 'dart:math' as math; 3 | import 'package:flutter/material.dart'; 4 | import 'package:my_puzzle/src/domain/models/tile.dart'; 5 | import 'package:my_puzzle/src/ui/pages/game/controller/game_state.dart'; 6 | import 'package:my_puzzle/src/ui/utils/colors.dart'; 7 | import 'package:my_puzzle/src/ui/utils/dark_mode_extension.dart'; 8 | 9 | /// widget to render every tile in the puzzle 10 | class PuzzleTile extends StatefulWidget { 11 | final Tile tile; 12 | final Uint8List? imageTile; 13 | final double size; 14 | final VoidCallback onTap; 15 | final bool showNumbersInTileImage; 16 | final GameStatus gameStatus; 17 | 18 | const PuzzleTile({ 19 | Key? key, 20 | required this.tile, 21 | required this.size, 22 | required this.onTap, 23 | required this.imageTile, 24 | required this.showNumbersInTileImage, 25 | required this.gameStatus, 26 | }) : super(key: key); 27 | 28 | @override 29 | State createState() => _PuzzleTileState(); 30 | } 31 | 32 | class _PuzzleTileState extends State with SingleTickerProviderStateMixin { 33 | late AnimationController _controller; 34 | late Animation _angle; 35 | bool _rotating = false; 36 | 37 | @override 38 | void initState() { 39 | super.initState(); 40 | 41 | _controller = AnimationController( 42 | duration: const Duration(seconds: 1), 43 | vsync: this, 44 | ); 45 | 46 | _angle = TweenSequence( 47 | [ 48 | TweenSequenceItem( 49 | tween: Tween( 50 | begin: 0, 51 | end: 180 * math.pi / 180, 52 | ), 53 | weight: 0.1, 54 | ), 55 | TweenSequenceItem( 56 | tween: Tween( 57 | begin: 180 * math.pi / 180, 58 | end: 180 * math.pi / 180, 59 | ), 60 | weight: 0.2, 61 | ), 62 | TweenSequenceItem( 63 | tween: Tween( 64 | begin: 180 * math.pi / 180, 65 | end: 270 * math.pi / 180, 66 | ), 67 | weight: 0.1, 68 | ), 69 | TweenSequenceItem( 70 | tween: Tween( 71 | begin: 270 * math.pi / 180, 72 | end: 0, 73 | ), 74 | weight: 0.1, 75 | ), 76 | ], 77 | ).animate(_controller); 78 | 79 | _controller.addStatusListener((status) { 80 | if (status == AnimationStatus.completed) { 81 | _rotating = false; 82 | } 83 | }); 84 | } 85 | 86 | @override 87 | void didUpdateWidget(covariant PuzzleTile oldWidget) { 88 | super.didUpdateWidget(oldWidget); 89 | 90 | if (oldWidget.gameStatus == GameStatus.created && widget.gameStatus == GameStatus.playing) { 91 | _rotating = true; 92 | _controller.forward(from: 0); 93 | } 94 | } 95 | 96 | @override 97 | void dispose() { 98 | _controller.dispose(); 99 | super.dispose(); 100 | } 101 | 102 | @override 103 | Widget build(BuildContext context) { 104 | final isDarkMode = context.isDarkMode; 105 | return AnimatedPositioned( 106 | duration: const Duration( 107 | milliseconds: 200, 108 | ), 109 | left: (widget.tile.position.x - 1) * widget.size + 1, 110 | top: (widget.tile.position.y - 1) * widget.size + 1, 111 | child: GestureDetector( 112 | onTap: () { 113 | if (!_rotating) { 114 | widget.onTap(); 115 | } 116 | }, 117 | child: AnimatedBuilder( 118 | animation: _angle, 119 | builder: (_, child) => Transform( 120 | transform: Matrix4.identity() 121 | ..rotateY( 122 | _angle.value, 123 | ), 124 | alignment: Alignment.center, 125 | child: child, 126 | ), 127 | child: ClipRRect( 128 | borderRadius: BorderRadius.circular(4), 129 | child: Container( 130 | color: Colors.white.withOpacity(isDarkMode ? 0.2 : 1), 131 | width: widget.size - 2, 132 | height: widget.size - 2, 133 | alignment: Alignment.center, 134 | child: AnimatedSwitcher( 135 | duration: const Duration( 136 | milliseconds: 200, 137 | ), 138 | child: widget.imageTile != null 139 | ? Stack( 140 | key: Key( 141 | widget.imageTile.hashCode.toString(), 142 | ), 143 | children: [ 144 | Positioned.fill( 145 | child: Image.memory( 146 | widget.imageTile!, 147 | fit: BoxFit.fill, 148 | ), 149 | ), 150 | if (widget.showNumbersInTileImage) 151 | Positioned( 152 | left: 0, 153 | top: 0, 154 | child: Container( 155 | color: isDarkMode ? darkColor : lightColor, 156 | width: widget.size * 0.25, 157 | height: widget.size * 0.25, 158 | padding: const EdgeInsets.all(2), 159 | alignment: Alignment.center, 160 | child: Text( 161 | widget.tile.value.toString(), 162 | style: TextStyle( 163 | fontSize: widget.size * 0.15, 164 | ), 165 | ), 166 | ), 167 | ) 168 | ], 169 | ) 170 | : Text( 171 | widget.tile.value.toString(), 172 | style: const TextStyle( 173 | fontSize: 20, 174 | ), 175 | ), 176 | ), 177 | ), 178 | ), 179 | ), 180 | ), 181 | ); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /lib/src/ui/pages/game/widgets/time_and_moves.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:my_puzzle/generated/l10n.dart'; 3 | import 'package:my_puzzle/src/ui/global/widgets/max_text_scale_factor.dart'; 4 | import 'package:my_puzzle/src/ui/icons/puzzle_icons.dart'; 5 | import 'package:my_puzzle/src/ui/pages/game/controller/game_controller.dart'; 6 | import 'package:my_puzzle/src/ui/utils/time_parser.dart'; 7 | import 'package:provider/provider.dart'; 8 | 9 | /// widget to show the time and the moves counter 10 | class TimeAndMoves extends StatelessWidget { 11 | const TimeAndMoves({Key? key}) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | const textStyle = TextStyle( 16 | fontSize: 25, 17 | fontWeight: FontWeight.w600, 18 | ); 19 | 20 | final time = Provider.of(context, listen: false).time; 21 | return MaxTextScaleFactor( 22 | max: 1, 23 | child: ConstrainedBox( 24 | constraints: const BoxConstraints( 25 | maxWidth: 500, 26 | ), 27 | child: Row( 28 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 29 | children: [ 30 | ValueListenableBuilder( 31 | valueListenable: time, 32 | builder: (_, time, icon) { 33 | return Row( 34 | children: [ 35 | icon!, 36 | Text( 37 | " ${parseTime(time)}", 38 | style: textStyle, 39 | ), 40 | ], 41 | ); 42 | }, 43 | child: const Icon( 44 | PuzzleIcons.watch, 45 | size: 25, 46 | ), 47 | ), 48 | Selector( 49 | builder: (_, moves, icon) { 50 | return Row( 51 | crossAxisAlignment: CrossAxisAlignment.center, 52 | children: [ 53 | icon!, 54 | const SizedBox(width: 5), 55 | Text( 56 | "$moves ${S.current.movements}", 57 | style: textStyle, 58 | ), 59 | ], 60 | ); 61 | }, 62 | selector: (_, controller) => controller.state.moves, 63 | child: const Icon( 64 | Icons.multiple_stop_rounded, 65 | size: 27, 66 | ), 67 | ), 68 | ], 69 | ), 70 | ), 71 | ); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /lib/src/ui/pages/game/widgets/winner_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:my_puzzle/generated/l10n.dart'; 3 | import 'package:my_puzzle/src/ui/global/widgets/up_to_down.dart'; 4 | import 'package:my_puzzle/src/ui/icons/puzzle_icons.dart'; 5 | import 'package:my_puzzle/src/ui/utils/colors.dart'; 6 | import 'package:my_puzzle/src/ui/utils/dark_mode_extension.dart'; 7 | import 'package:my_puzzle/src/ui/utils/time_parser.dart'; 8 | import 'package:rive/rive.dart'; 9 | 10 | Future showWinnerDialog( 11 | BuildContext context, { 12 | required int moves, 13 | required int time, 14 | }) { 15 | return showDialog( 16 | context: context, 17 | builder: (_) => WinnerDialog( 18 | moves: moves, 19 | time: time, 20 | ), 21 | ); 22 | } 23 | 24 | class WinnerDialog extends StatelessWidget { 25 | final int moves; 26 | final int time; 27 | const WinnerDialog({ 28 | Key? key, 29 | required this.moves, 30 | required this.time, 31 | }) : super(key: key); 32 | 33 | @override 34 | Widget build(BuildContext context) { 35 | final texts = S.current; 36 | final isDarkMode = context.isDarkMode; 37 | return Center( 38 | child: UpToDown( 39 | child: Material( 40 | shape: RoundedRectangleBorder( 41 | borderRadius: BorderRadius.circular(20), 42 | ), 43 | color: isDarkMode ? darkColor : lightColor, 44 | child: SizedBox( 45 | width: 340, 46 | child: Column( 47 | mainAxisSize: MainAxisSize.min, 48 | children: [ 49 | AspectRatio( 50 | aspectRatio: 1.2, 51 | child: Stack( 52 | children: [ 53 | const Center( 54 | child: RiveAnimation.asset( 55 | 'assets/rive/winner.riv', 56 | ), 57 | ), 58 | Align( 59 | alignment: Alignment.topCenter, 60 | child: Padding( 61 | padding: const EdgeInsets.only(top: 30), 62 | child: Column( 63 | mainAxisSize: MainAxisSize.min, 64 | children: [ 65 | Text( 66 | texts.great_job, 67 | style: const TextStyle( 68 | fontSize: 25, 69 | ), 70 | ), 71 | Text( 72 | texts.completed, 73 | style: const TextStyle( 74 | fontSize: 16, 75 | ), 76 | textAlign: TextAlign.center, 77 | ) 78 | ], 79 | ), 80 | ), 81 | ), 82 | Align( 83 | alignment: Alignment.bottomCenter, 84 | child: Padding( 85 | padding: const EdgeInsets.symmetric( 86 | horizontal: 15, 87 | ), 88 | child: Row( 89 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 90 | children: [ 91 | Row( 92 | children: [ 93 | const Icon( 94 | PuzzleIcons.watch, 95 | ), 96 | Text( 97 | parseTime(time), 98 | style: const TextStyle( 99 | fontSize: 20, 100 | ), 101 | ), 102 | ], 103 | ), 104 | Row( 105 | children: [ 106 | const Icon( 107 | Icons.multiple_stop_rounded, 108 | ), 109 | Text( 110 | "${texts.movements} $moves", 111 | style: const TextStyle( 112 | fontSize: 20, 113 | ), 114 | ), 115 | ], 116 | ), 117 | ], 118 | ), 119 | ), 120 | ), 121 | ], 122 | ), 123 | ), 124 | const SizedBox(height: 15), 125 | Container( 126 | height: 0.6, 127 | color: isDarkMode ? Colors.white24 : Colors.black12, 128 | ), 129 | SizedBox( 130 | width: double.infinity, 131 | child: TextButton( 132 | onPressed: () => Navigator.pop(context), 133 | child: Text( 134 | texts.ok, 135 | style: const TextStyle( 136 | fontSize: 20, 137 | ), 138 | ), 139 | ), 140 | ), 141 | ], 142 | ), 143 | ), 144 | ), 145 | ), 146 | ); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /lib/src/ui/pages/privacy/privacy_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:my_puzzle/generated/l10n.dart'; 3 | import 'package:my_puzzle/src/ui/global/widgets/my_text_icon_button.dart'; 4 | import 'package:my_puzzle/src/ui/routes/routes.dart'; 5 | 6 | class PrivacyView extends StatelessWidget { 7 | const PrivacyView({Key? key}) : super(key: key); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return Scaffold( 12 | body: Center( 13 | child: Column( 14 | mainAxisSize: MainAxisSize.min, 15 | children: [ 16 | Text( 17 | S.current.privacy, 18 | style: const TextStyle(fontSize: 20), 19 | ), 20 | const SizedBox(height: 20), 21 | MyTextIconButton( 22 | onPressed: () => Navigator.pushReplacementNamed( 23 | context, 24 | Routes.splash, 25 | ), 26 | icon: const Icon(Icons.check), 27 | label: S.current.back_to_game, 28 | height: 55, 29 | ), 30 | ], 31 | ), 32 | ), 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/src/ui/pages/splash/circle_transition_clipper.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_renaming_method_parameters 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class CircleTransitionClipper extends CustomClipper { 6 | final Offset center; 7 | final double radius; 8 | 9 | CircleTransitionClipper({ 10 | required this.center, 11 | required this.radius, 12 | }); 13 | 14 | @override 15 | Path getClip(Size size) { 16 | return Path() 17 | ..addOval( 18 | Rect.fromCircle(center: center, radius: radius), 19 | ); 20 | } 21 | 22 | @override 23 | bool shouldReclip(covariant CustomClipper _) { 24 | return true; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/src/ui/pages/splash/splash_view.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:math' as math; 3 | 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter/services.dart'; 6 | import 'package:my_puzzle/src/data/repositories_impl/images_repository_impl.dart'; 7 | import 'package:my_puzzle/src/ui/pages/game/game_view.dart'; 8 | import 'package:my_puzzle/src/ui/pages/splash/circle_transition_clipper.dart'; 9 | import 'package:my_puzzle/src/ui/utils/colors.dart'; 10 | import 'package:my_puzzle/src/ui/utils/dark_mode_extension.dart'; 11 | import 'package:my_puzzle/src/ui/utils/responsive.dart'; 12 | 13 | class SplashView extends StatefulWidget { 14 | const SplashView({Key? key}) : super(key: key); 15 | 16 | @override 17 | _SplashViewState createState() => _SplashViewState(); 18 | } 19 | 20 | class _SplashViewState extends State with SingleTickerProviderStateMixin { 21 | late AnimationController _controller; 22 | late Animation _animation; 23 | 24 | @override 25 | void initState() { 26 | super.initState(); 27 | _controller = AnimationController( 28 | vsync: this, 29 | duration: const Duration(seconds: 1), 30 | ); 31 | _animation = CurvedAnimation( 32 | parent: _controller, 33 | curve: Curves.elasticIn, 34 | ); 35 | _controller.forward(); 36 | _controller.addStatusListener( 37 | (status) { 38 | if (status == AnimationStatus.completed) { 39 | Timer( 40 | const Duration(seconds: 1), 41 | _goToGame, 42 | ); 43 | } 44 | }, 45 | ); 46 | _setDeviceOrientation(); 47 | } 48 | 49 | void _goToGame() { 50 | final route = PageRouteBuilder( 51 | pageBuilder: (_, animation, secondaryAnimation) => const GameView(), 52 | transitionDuration: const Duration(milliseconds: 1500), 53 | transitionsBuilder: ( 54 | context, 55 | animation, 56 | secondaryAnimation, 57 | child, 58 | ) { 59 | final size = MediaQuery.of(context).size; 60 | 61 | final radiusTween = Tween( 62 | begin: 0.0, 63 | end: size.height, 64 | ); 65 | 66 | return ClipPath( 67 | clipper: CircleTransitionClipper( 68 | center: Offset( 69 | size.width * 0.5, 70 | size.height * 0.5, 71 | ), 72 | radius: animation.drive(radiusTween).value, 73 | ), 74 | child: child, 75 | ); 76 | }, 77 | ); 78 | Navigator.pushReplacement(context, route); 79 | } 80 | 81 | void _setDeviceOrientation() { 82 | WidgetsBinding.instance?.addPostFrameCallback( 83 | (_) { 84 | // The equivalent of the "smallestWidth" qualifier on Android. 85 | var shortestSide = MediaQuery.of(context).size.shortestSide; 86 | 87 | // Determine if we should use mobile layout or not, 600 here is 88 | // a common breakpoint for a typical 7-inch tablet. 89 | final bool useMobileLayout = shortestSide < 600; 90 | if (useMobileLayout) { 91 | SystemChrome.setPreferredOrientations( 92 | [ 93 | DeviceOrientation.portraitDown, 94 | DeviceOrientation.portraitUp, 95 | ], 96 | ); 97 | } 98 | }, 99 | ); 100 | } 101 | 102 | @override 103 | void dispose() { 104 | _controller.dispose(); 105 | super.dispose(); 106 | } 107 | 108 | @override 109 | Widget build(BuildContext context) { 110 | final options = puzzleOptions.getRange( 111 | 1, 112 | puzzleOptions.length, 113 | ); 114 | 115 | final half = options.length ~/ 2; 116 | final responsive = Responsive.of(context); 117 | 118 | return Scaffold( 119 | backgroundColor: context.isDarkMode ? darkColor : lightColor2, 120 | body: SizedBox( 121 | width: double.infinity, 122 | height: double.infinity, 123 | child: OrientationBuilder( 124 | builder: (_, orientation) { 125 | final isPortrait = orientation == Orientation.portrait; 126 | 127 | final size = responsive.dp(isPortrait ? 10 : 7); 128 | final bottom = responsive.hp(30); 129 | final titleSize = responsive.dp(5); 130 | 131 | return AnimatedBuilder( 132 | animation: _animation, 133 | builder: (_, jungle) { 134 | final offset = _animation.value; 135 | final dx = -size * 0.3 - (size * 0.7 - offset * size * 0.7); 136 | 137 | return Stack( 138 | children: [ 139 | Positioned( 140 | bottom: -100 + (offset * 100), 141 | left: 0, 142 | right: 0, 143 | child: jungle!, 144 | ), 145 | Center( 146 | child: Transform.scale( 147 | scale: offset.clamp(0.5, 1), 148 | child: Opacity( 149 | opacity: offset.clamp(0, 1), 150 | child: Text( 151 | "jungle\npuzzle", 152 | textAlign: TextAlign.center, 153 | style: TextStyle( 154 | fontSize: titleSize, 155 | fontWeight: FontWeight.bold, 156 | height: 0.9, 157 | ), 158 | ), 159 | ), 160 | ), 161 | ), 162 | ...List.generate( 163 | half, 164 | (index) { 165 | final dy = bottom + (size * 1.1 * index); 166 | return Positioned( 167 | left: dx, 168 | bottom: dy, 169 | child: Transform.rotate( 170 | angle: 15 * math.pi / 180, 171 | child: Image.asset( 172 | options.elementAt(index).assetPath, 173 | width: size, 174 | ), 175 | ), 176 | ); 177 | }, 178 | ), 179 | ...List.generate( 180 | half, 181 | (index) { 182 | final dy = bottom + (size * 1.1 * index); 183 | return Positioned( 184 | right: dx, 185 | bottom: dy, 186 | child: Transform.rotate( 187 | angle: -15 * math.pi / 180, 188 | child: Image.asset( 189 | options.elementAt(index + half).assetPath, 190 | width: size, 191 | ), 192 | ), 193 | ); 194 | }, 195 | ), 196 | ], 197 | ); 198 | }, 199 | child: Transform.rotate( 200 | angle: math.pi, 201 | child: Image.asset( 202 | 'assets/images/jungle.png', 203 | width: double.infinity, 204 | color: Colors.primaries[8].withOpacity(0.3), 205 | ), 206 | ), 207 | ); 208 | }, 209 | ), 210 | ), 211 | ); 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /lib/src/ui/routes/app_routes.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:my_puzzle/src/ui/pages/game/game_view.dart'; 3 | import 'package:my_puzzle/src/ui/pages/privacy/privacy_view.dart'; 4 | import 'package:my_puzzle/src/ui/pages/splash/splash_view.dart'; 5 | import 'package:my_puzzle/src/ui/routes/routes.dart'; 6 | 7 | Map get appRoutes { 8 | return { 9 | Routes.splash: (_) => const SplashView(), 10 | Routes.game: (_) => const GameView(), 11 | Routes.privacy: (_) => const PrivacyView(), 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /lib/src/ui/routes/routes.dart: -------------------------------------------------------------------------------- 1 | class Routes { 2 | Routes._(); 3 | 4 | static const splash = '/'; 5 | static const game = '/game'; 6 | static const privacy = '/privacy'; 7 | } 8 | -------------------------------------------------------------------------------- /lib/src/ui/utils/colors.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart' show Color; 2 | 3 | const Map swatch = { 4 | 50: Color.fromRGBO(136, 14, 79, .1), 5 | 100: Color.fromRGBO(136, 14, 79, .2), 6 | 200: Color.fromRGBO(136, 14, 79, .3), 7 | 300: Color.fromRGBO(136, 14, 79, .4), 8 | 400: Color.fromRGBO(136, 14, 79, .5), 9 | 500: Color.fromRGBO(136, 14, 79, .6), 10 | 600: Color.fromRGBO(136, 14, 79, .7), 11 | 700: Color.fromRGBO(136, 14, 79, .8), 12 | 800: Color.fromRGBO(136, 14, 79, .9), 13 | 900: Color.fromRGBO(136, 14, 79, 1), 14 | }; 15 | 16 | const lightColor = Color(0xfff0f0f0); 17 | const lightColor2 = Color(0xfff9f9f9); 18 | const darkColor = Color(0xff002942); 19 | const acentColor = Color(0xff2196f3); 20 | -------------------------------------------------------------------------------- /lib/src/ui/utils/dark_mode_extension.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart' show BuildContext, ThemeData, Brightness, Theme; 2 | 3 | extension DarkModeExtension on BuildContext { 4 | bool get isDarkMode { 5 | final ThemeData theme = Theme.of(this); 6 | return theme.brightness == Brightness.dark; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /lib/src/ui/utils/platform.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | 3 | bool get isAndroid => defaultTargetPlatform == TargetPlatform.android; 4 | bool get isIOS => defaultTargetPlatform == TargetPlatform.iOS; 5 | bool get isWeb => kIsWeb; 6 | bool get isWebMobile => isWeb && (isIOS || isAndroid); 7 | -------------------------------------------------------------------------------- /lib/src/ui/utils/responsive.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'dart:math' as math; 3 | 4 | class Responsive { 5 | final Size size; 6 | double get width => size.width; 7 | double get height => size.height; 8 | final double diagonal; 9 | 10 | factory Responsive.of(BuildContext context) { 11 | final size = MediaQuery.of(context).size; 12 | final diagonal = math.sqrt( 13 | math.pow(size.width, 2) + math.pow(size.height, 2), 14 | ); 15 | return Responsive( 16 | size: size, 17 | diagonal: diagonal, 18 | ); 19 | } 20 | 21 | double wp(double percent) { 22 | return percent * width / 100; 23 | } 24 | 25 | double hp(double percent) { 26 | return percent * height / 100; 27 | } 28 | 29 | double dp(double percent) { 30 | return percent * diagonal / 100; 31 | } 32 | 33 | Responsive({ 34 | required this.size, 35 | required this.diagonal, 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /lib/src/ui/utils/time_parser.dart: -------------------------------------------------------------------------------- 1 | String parseTime(int time) { 2 | final duration = Duration( 3 | seconds: time, 4 | ); 5 | 6 | final minutes = duration.inMinutes.remainder(60).toString().padLeft(2, '0'); 7 | 8 | final seconds = duration.inSeconds.remainder(60).toString().padLeft(2, '0'); 9 | 10 | return "$minutes:$seconds"; 11 | } 12 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | archive: 5 | dependency: transitive 6 | description: 7 | name: archive 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "3.1.11" 11 | args: 12 | dependency: transitive 13 | description: 14 | name: args 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "2.3.0" 18 | async: 19 | dependency: transitive 20 | description: 21 | name: async 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "2.8.2" 25 | audio_session: 26 | dependency: "direct main" 27 | description: 28 | name: audio_session 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "0.1.6+1" 32 | boolean_selector: 33 | dependency: transitive 34 | description: 35 | name: boolean_selector 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "2.1.0" 39 | characters: 40 | dependency: transitive 41 | description: 42 | name: characters 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.2.0" 46 | charcode: 47 | dependency: transitive 48 | description: 49 | name: charcode 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.3.1" 53 | clock: 54 | dependency: transitive 55 | description: 56 | name: clock 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "1.1.0" 60 | collection: 61 | dependency: transitive 62 | description: 63 | name: collection 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "1.15.0" 67 | crypto: 68 | dependency: transitive 69 | description: 70 | name: crypto 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "3.0.1" 74 | equatable: 75 | dependency: "direct main" 76 | description: 77 | name: equatable 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "2.0.3" 81 | fake_async: 82 | dependency: transitive 83 | description: 84 | name: fake_async 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "1.2.0" 88 | ffi: 89 | dependency: transitive 90 | description: 91 | name: ffi 92 | url: "https://pub.dartlang.org" 93 | source: hosted 94 | version: "1.1.2" 95 | file: 96 | dependency: transitive 97 | description: 98 | name: file 99 | url: "https://pub.dartlang.org" 100 | source: hosted 101 | version: "6.1.2" 102 | flutter: 103 | dependency: "direct main" 104 | description: flutter 105 | source: sdk 106 | version: "0.0.0" 107 | flutter_launcher_icons: 108 | dependency: "direct dev" 109 | description: 110 | name: flutter_launcher_icons 111 | url: "https://pub.dartlang.org" 112 | source: hosted 113 | version: "0.9.2" 114 | flutter_lints: 115 | dependency: "direct dev" 116 | description: 117 | name: flutter_lints 118 | url: "https://pub.dartlang.org" 119 | source: hosted 120 | version: "1.0.4" 121 | flutter_localizations: 122 | dependency: "direct main" 123 | description: flutter 124 | source: sdk 125 | version: "0.0.0" 126 | flutter_test: 127 | dependency: "direct dev" 128 | description: flutter 129 | source: sdk 130 | version: "0.0.0" 131 | flutter_web_plugins: 132 | dependency: transitive 133 | description: flutter 134 | source: sdk 135 | version: "0.0.0" 136 | get_it: 137 | dependency: "direct main" 138 | description: 139 | name: get_it 140 | url: "https://pub.dartlang.org" 141 | source: hosted 142 | version: "7.2.0" 143 | google_fonts: 144 | dependency: "direct main" 145 | description: 146 | name: google_fonts 147 | url: "https://pub.dartlang.org" 148 | source: hosted 149 | version: "2.2.0" 150 | graphs: 151 | dependency: transitive 152 | description: 153 | name: graphs 154 | url: "https://pub.dartlang.org" 155 | source: hosted 156 | version: "2.1.0" 157 | http: 158 | dependency: transitive 159 | description: 160 | name: http 161 | url: "https://pub.dartlang.org" 162 | source: hosted 163 | version: "0.13.4" 164 | http_parser: 165 | dependency: transitive 166 | description: 167 | name: http_parser 168 | url: "https://pub.dartlang.org" 169 | source: hosted 170 | version: "4.0.0" 171 | image: 172 | dependency: "direct main" 173 | description: 174 | name: image 175 | url: "https://pub.dartlang.org" 176 | source: hosted 177 | version: "3.1.1" 178 | intl: 179 | dependency: "direct main" 180 | description: 181 | name: intl 182 | url: "https://pub.dartlang.org" 183 | source: hosted 184 | version: "0.17.0" 185 | js: 186 | dependency: transitive 187 | description: 188 | name: js 189 | url: "https://pub.dartlang.org" 190 | source: hosted 191 | version: "0.6.3" 192 | just_audio: 193 | dependency: "direct main" 194 | description: 195 | name: just_audio 196 | url: "https://pub.dartlang.org" 197 | source: hosted 198 | version: "0.9.18" 199 | just_audio_platform_interface: 200 | dependency: transitive 201 | description: 202 | name: just_audio_platform_interface 203 | url: "https://pub.dartlang.org" 204 | source: hosted 205 | version: "4.0.0" 206 | just_audio_web: 207 | dependency: transitive 208 | description: 209 | name: just_audio_web 210 | url: "https://pub.dartlang.org" 211 | source: hosted 212 | version: "0.4.3" 213 | lints: 214 | dependency: transitive 215 | description: 216 | name: lints 217 | url: "https://pub.dartlang.org" 218 | source: hosted 219 | version: "1.0.1" 220 | matcher: 221 | dependency: transitive 222 | description: 223 | name: matcher 224 | url: "https://pub.dartlang.org" 225 | source: hosted 226 | version: "0.12.11" 227 | material_color_utilities: 228 | dependency: transitive 229 | description: 230 | name: material_color_utilities 231 | url: "https://pub.dartlang.org" 232 | source: hosted 233 | version: "0.1.3" 234 | meta: 235 | dependency: transitive 236 | description: 237 | name: meta 238 | url: "https://pub.dartlang.org" 239 | source: hosted 240 | version: "1.7.0" 241 | nested: 242 | dependency: transitive 243 | description: 244 | name: nested 245 | url: "https://pub.dartlang.org" 246 | source: hosted 247 | version: "1.0.0" 248 | path: 249 | dependency: transitive 250 | description: 251 | name: path 252 | url: "https://pub.dartlang.org" 253 | source: hosted 254 | version: "1.8.0" 255 | path_provider: 256 | dependency: transitive 257 | description: 258 | name: path_provider 259 | url: "https://pub.dartlang.org" 260 | source: hosted 261 | version: "2.0.8" 262 | path_provider_android: 263 | dependency: transitive 264 | description: 265 | name: path_provider_android 266 | url: "https://pub.dartlang.org" 267 | source: hosted 268 | version: "2.0.11" 269 | path_provider_ios: 270 | dependency: transitive 271 | description: 272 | name: path_provider_ios 273 | url: "https://pub.dartlang.org" 274 | source: hosted 275 | version: "2.0.7" 276 | path_provider_linux: 277 | dependency: transitive 278 | description: 279 | name: path_provider_linux 280 | url: "https://pub.dartlang.org" 281 | source: hosted 282 | version: "2.1.5" 283 | path_provider_macos: 284 | dependency: transitive 285 | description: 286 | name: path_provider_macos 287 | url: "https://pub.dartlang.org" 288 | source: hosted 289 | version: "2.0.5" 290 | path_provider_platform_interface: 291 | dependency: transitive 292 | description: 293 | name: path_provider_platform_interface 294 | url: "https://pub.dartlang.org" 295 | source: hosted 296 | version: "2.0.3" 297 | path_provider_windows: 298 | dependency: transitive 299 | description: 300 | name: path_provider_windows 301 | url: "https://pub.dartlang.org" 302 | source: hosted 303 | version: "2.0.5" 304 | petitparser: 305 | dependency: transitive 306 | description: 307 | name: petitparser 308 | url: "https://pub.dartlang.org" 309 | source: hosted 310 | version: "4.4.0" 311 | platform: 312 | dependency: transitive 313 | description: 314 | name: platform 315 | url: "https://pub.dartlang.org" 316 | source: hosted 317 | version: "3.1.0" 318 | plugin_platform_interface: 319 | dependency: transitive 320 | description: 321 | name: plugin_platform_interface 322 | url: "https://pub.dartlang.org" 323 | source: hosted 324 | version: "2.1.2" 325 | process: 326 | dependency: transitive 327 | description: 328 | name: process 329 | url: "https://pub.dartlang.org" 330 | source: hosted 331 | version: "4.2.4" 332 | provider: 333 | dependency: "direct main" 334 | description: 335 | name: provider 336 | url: "https://pub.dartlang.org" 337 | source: hosted 338 | version: "6.0.2" 339 | rive: 340 | dependency: "direct main" 341 | description: 342 | name: rive 343 | url: "https://pub.dartlang.org" 344 | source: hosted 345 | version: "0.8.1" 346 | rxdart: 347 | dependency: transitive 348 | description: 349 | name: rxdart 350 | url: "https://pub.dartlang.org" 351 | source: hosted 352 | version: "0.27.3" 353 | shared_preferences: 354 | dependency: "direct main" 355 | description: 356 | name: shared_preferences 357 | url: "https://pub.dartlang.org" 358 | source: hosted 359 | version: "2.0.12" 360 | shared_preferences_android: 361 | dependency: transitive 362 | description: 363 | name: shared_preferences_android 364 | url: "https://pub.dartlang.org" 365 | source: hosted 366 | version: "2.0.10" 367 | shared_preferences_ios: 368 | dependency: transitive 369 | description: 370 | name: shared_preferences_ios 371 | url: "https://pub.dartlang.org" 372 | source: hosted 373 | version: "2.0.9" 374 | shared_preferences_linux: 375 | dependency: transitive 376 | description: 377 | name: shared_preferences_linux 378 | url: "https://pub.dartlang.org" 379 | source: hosted 380 | version: "2.0.4" 381 | shared_preferences_macos: 382 | dependency: transitive 383 | description: 384 | name: shared_preferences_macos 385 | url: "https://pub.dartlang.org" 386 | source: hosted 387 | version: "2.0.2" 388 | shared_preferences_platform_interface: 389 | dependency: transitive 390 | description: 391 | name: shared_preferences_platform_interface 392 | url: "https://pub.dartlang.org" 393 | source: hosted 394 | version: "2.0.0" 395 | shared_preferences_web: 396 | dependency: transitive 397 | description: 398 | name: shared_preferences_web 399 | url: "https://pub.dartlang.org" 400 | source: hosted 401 | version: "2.0.3" 402 | shared_preferences_windows: 403 | dependency: transitive 404 | description: 405 | name: shared_preferences_windows 406 | url: "https://pub.dartlang.org" 407 | source: hosted 408 | version: "2.0.4" 409 | sky_engine: 410 | dependency: transitive 411 | description: flutter 412 | source: sdk 413 | version: "0.0.99" 414 | smooth_page_indicator: 415 | dependency: "direct main" 416 | description: 417 | name: smooth_page_indicator 418 | url: "https://pub.dartlang.org" 419 | source: hosted 420 | version: "1.0.0+2" 421 | source_span: 422 | dependency: transitive 423 | description: 424 | name: source_span 425 | url: "https://pub.dartlang.org" 426 | source: hosted 427 | version: "1.8.1" 428 | stack_trace: 429 | dependency: transitive 430 | description: 431 | name: stack_trace 432 | url: "https://pub.dartlang.org" 433 | source: hosted 434 | version: "1.10.0" 435 | stream_channel: 436 | dependency: transitive 437 | description: 438 | name: stream_channel 439 | url: "https://pub.dartlang.org" 440 | source: hosted 441 | version: "2.1.0" 442 | string_scanner: 443 | dependency: transitive 444 | description: 445 | name: string_scanner 446 | url: "https://pub.dartlang.org" 447 | source: hosted 448 | version: "1.1.0" 449 | term_glyph: 450 | dependency: transitive 451 | description: 452 | name: term_glyph 453 | url: "https://pub.dartlang.org" 454 | source: hosted 455 | version: "1.2.0" 456 | test_api: 457 | dependency: transitive 458 | description: 459 | name: test_api 460 | url: "https://pub.dartlang.org" 461 | source: hosted 462 | version: "0.4.8" 463 | typed_data: 464 | dependency: transitive 465 | description: 466 | name: typed_data 467 | url: "https://pub.dartlang.org" 468 | source: hosted 469 | version: "1.3.0" 470 | url_strategy: 471 | dependency: "direct main" 472 | description: 473 | name: url_strategy 474 | url: "https://pub.dartlang.org" 475 | source: hosted 476 | version: "0.2.0" 477 | uuid: 478 | dependency: transitive 479 | description: 480 | name: uuid 481 | url: "https://pub.dartlang.org" 482 | source: hosted 483 | version: "3.0.5" 484 | vector_math: 485 | dependency: transitive 486 | description: 487 | name: vector_math 488 | url: "https://pub.dartlang.org" 489 | source: hosted 490 | version: "2.1.1" 491 | win32: 492 | dependency: transitive 493 | description: 494 | name: win32 495 | url: "https://pub.dartlang.org" 496 | source: hosted 497 | version: "2.3.10" 498 | xdg_directories: 499 | dependency: transitive 500 | description: 501 | name: xdg_directories 502 | url: "https://pub.dartlang.org" 503 | source: hosted 504 | version: "0.2.0" 505 | xml: 506 | dependency: transitive 507 | description: 508 | name: xml 509 | url: "https://pub.dartlang.org" 510 | source: hosted 511 | version: "5.3.1" 512 | yaml: 513 | dependency: transitive 514 | description: 515 | name: yaml 516 | url: "https://pub.dartlang.org" 517 | source: hosted 518 | version: "3.1.0" 519 | sdks: 520 | dart: ">=2.15.1 <3.0.0" 521 | flutter: ">=2.5.0" 522 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: my_puzzle 2 | description: A new Flutter project. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `flutter 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: ">=2.15.1 <3.0.0" 22 | 23 | # Dependencies specify other packages that your package needs in order to work. 24 | # To automatically upgrade your package dependencies to the latest versions 25 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 26 | # dependencies can be manually updated by changing the version numbers below to 27 | # the latest version available on pub.dev. To see which dependencies have newer 28 | # versions available, run `flutter pub outdated`. 29 | dependencies: 30 | flutter: 31 | sdk: flutter 32 | flutter_localizations: 33 | sdk: flutter 34 | 35 | intl: ^0.17.0 36 | provider: ^6.0.2 37 | equatable: ^2.0.3 38 | google_fonts: ^2.2.0 39 | shared_preferences: ^2.0.12 40 | get_it: ^7.2.0 41 | audio_session: ^0.1.6+1 42 | just_audio: ^0.9.18 43 | image: ^3.1.1 44 | smooth_page_indicator: ^1.0.0+2 45 | url_strategy: ^0.2.0 46 | rive: ^0.8.1 47 | 48 | dev_dependencies: 49 | flutter_test: 50 | sdk: flutter 51 | 52 | # The "flutter_lints" package below contains a set of recommended lints to 53 | # encourage good coding practices. The lint set provided by the package is 54 | # activated in the `analysis_options.yaml` file located at the root of your 55 | # package. See that file for information about deactivating specific lint 56 | # rules and activating additional ones. 57 | flutter_lints: ^1.0.0 58 | flutter_launcher_icons: "^0.9.2" 59 | 60 | # For information on the generic Dart part of this file, see the 61 | # following page: https://dart.dev/tools/pub/pubspec 62 | # The following section is specific to Flutter. 63 | flutter: 64 | 65 | # The following line ensures that the Material Icons font is 66 | # included with your application, so that you can use the icons in 67 | # the material Icons class. 68 | uses-material-design: true 69 | # To add assets to your application, add an assets section, like this: 70 | assets: 71 | - assets/images/jungle.png 72 | - assets/images/dash.png 73 | - assets/images/hero-dash.png 74 | - assets/images/relax-dash.png 75 | - assets/images/numeric-puzzle.png 76 | - assets/rive/winner.riv 77 | - assets/sounds/ 78 | - assets/animals/ 79 | # An image asset can refer to one or more resolution-specific "variants", see 80 | # https://flutter.dev/assets-and-images/#resolution-aware. 81 | # For details regarding adding assets from package dependencies, see 82 | # https://flutter.dev/assets-and-images/#from-packages 83 | # To add custom fonts to your application, add a fonts section here, 84 | # in this "flutter" section. Each entry in this list should have a 85 | # "family" key with the font family name, and a "fonts" key with a 86 | # list giving the asset and other descriptors for the font. For 87 | # example: 88 | fonts: 89 | - family: PuzzleIcons 90 | fonts: 91 | - asset: assets/icons/PuzzleIcons.ttf 92 | 93 | 94 | flutter_intl: 95 | enabled: true 96 | arb_dir: lib/src/l10n 97 | 98 | 99 | flutter_icons: 100 | android: "ic_launcher" 101 | ios: true 102 | image_path: "assets/icons/icon.png" -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/web/icons/Icon-512.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darwin-morocho/flutter-puzzle-hack/7b9635ce08f4e3982d8eebdbc664c62de6f538b9/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | my_puzzle 33 | 34 | 35 | 36 | 39 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my_puzzle", 3 | "short_name": "my_puzzle", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } 36 | --------------------------------------------------------------------------------