commit 4f84c66942217d7fabad3d56e466f1c8d5cb66c8 Author: mohsen zamani Date: Sun Feb 19 18:12:53 2023 +0330 first commit diff --git a/.firebaserc b/.firebaserc new file mode 100644 index 0000000..17df186 --- /dev/null +++ b/.firebaserc @@ -0,0 +1,5 @@ +{ + "projects": { + "default": "flutterpuzzle-eaf89" + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8ae1987 --- /dev/null +++ b/.gitignore @@ -0,0 +1,49 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release +lib/firebase_options.dart +.firebase +.vscode/settings.json diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..fd70cab --- /dev/null +++ b/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 77d935af4db863f6abd0b9c31c7e6df2a13de57b + channel: stable + +project_type: app diff --git a/Auto_solver_README.md b/Auto_solver_README.md new file mode 100644 index 0000000..8ddbe50 --- /dev/null +++ b/Auto_solver_README.md @@ -0,0 +1,65 @@ +# Auto solver implementation with A-star + +The board is represented as a 1D array initially, which is converted to a 2D matrix(list of list of int). + +Lets understand the implementation with a 3 * 3 slide puzzle. However, this will work for any nXn slide puzzle. + +``` +[1 2 3 4 5 6 7 8 ] + +Let's use the A star algorithm to solve the 8 puzzle problem. + +Let the initial state be + +[] + +Let the goal state be + +[] + +The search tree for the initial state will look like this + +At each step, we move our empty block either left or down. We need to make sure we do not reach the original state through these moves, as they can lead to infinite loops. + +There are two entities we need to keep into mind + +G is the number of steps required to reach the goal state from the initial state. Estimated number of steps from current state to goal state. + +H is Manhattan distance. It is the estimated distance to goal. + +It can be calculated by adding the difference in the row and column for all the blocks of the initial state from the goal state. + +Identify the path with the minimum g+h. + + +These are the variables in `puzzle_solver.dart` : + +* queue - Priority queue +* visited - set +* goal_states - list +* root - object of Node class +* board: board +* previous: null, +* heuristic: manhattan(board, goalStates[currGoal]) +* depth: int + +>Each node can have maximum of 4 children, the graph fill further expand in a similar fashion with each child pointing to the parent using pointer. + +>The g score here, which is the cost of the move will increase by 1 at each depth since each tile sliding to the blank space represents one move and in the end we need to get the optimal moves for the puzzle instance, this basically mean that we have to add 1 to the g score of the parent before storing it in the child nodes. + +>Heuristics returns the number of tiles that are not in their final position + +>Manhattan Distance of a tile is the distance or the number of slides/tiles away it is from it’s goal state.Thus, for a certain state the Manhattan distance will be the sum of the Manhattan distances of all the tiles except the blank tile. + +Functions : + +* rowColGoalStates() - Returns the goal states for the slide puzzle + +queue is initialised with the first node object, the initial board configuration. + +We loop till the queue isnt empty. + +Inside the loop, we pop an element from the top. + +If it possible to reach the goal state from that configuration + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..63590da --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Soumi Bardhan, Souvik Biswas + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..8c2cc24 --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ +# Flutter Puzzle + +[![Codemagic build status](https://api.codemagic.io/apps/61f7d22dafa5dd12bdc7c1bf/61f7d22dafa5dd12bdc7c1be/status_badge.svg)](https://codemagic.io/apps/61f7d22dafa5dd12bdc7c1bf/61f7d22dafa5dd12bdc7c1be/latest_build) + +> Web App link: https://flutterpuzzle.codemagic.app + +![](screenshots/photo_mode.gif) + +Playing a game or solving a puzzle is always fun when you share with friends. We have created a puzzle that consists of three main modes: + +* **Normal mode:** The classic slide puzzle configuration. +* **Photo mode:** Ability to generate puzzles from any image uploaded. +* **Multiplayer mode:** Play with others in a competitive head-to-head multiplayer mode. + +The puzzle also has an auto solver built-in, it uses the A* algorithm along with some pretty neat tricks to solve the puzzle in the lowest possible moves. + +A full explanation of how the A* algorithm works is [here](https://youtu.be/nwtOWpSKGHc). + +The puzzle uses **Rive** to display an animated Dash beside the puzzle. + +**Riverpod** is used for state management inside the puzzle. The app has pretty challenging state management as there are three modes and the puzzle state is persisted across both the photo mode and the multiplayer mode. + +We also used a color palette generator package that can create an entire color palette from just an image, it is used in photo mode to change the theme of the screen according to the image selected. + +## Screenshots + +**Normal mode:** + +![](screenshots/puzzle_normal.png) + +**Photo mode:** + +![](screenshots/puzzle_photo.png) + +**Multiplayer mode:** + +![](screenshots/puzzle_multiplayer_1.png) + +![](screenshots/puzzle_multiplayer.png) + + +## License + +Copyright (c) 2022 Soumi Bardhan, Souvik Biswas + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..61b6c4d --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..6f56801 --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/android/app/build.gradle b/android/app/build.gradle new file mode 100644 index 0000000..0c45e28 --- /dev/null +++ b/android/app/build.gradle @@ -0,0 +1,68 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion flutter.compileSdkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.souvikbiswas.my_flutter_puzzle" + minSdkVersion flutter.minSdkVersion + targetSdkVersion flutter.targetSdkVersion + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..da38ab7 --- /dev/null +++ b/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..f6ab0f9 --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + diff --git a/android/app/src/main/kotlin/com/souvikbiswas/my_flutter_puzzle/MainActivity.kt b/android/app/src/main/kotlin/com/souvikbiswas/my_flutter_puzzle/MainActivity.kt new file mode 100644 index 0000000..4f69645 --- /dev/null +++ b/android/app/src/main/kotlin/com/souvikbiswas/my_flutter_puzzle/MainActivity.kt @@ -0,0 +1,6 @@ +package com.souvikbiswas.my_flutter_puzzle + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..db77bb4 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..17987b7 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..09d4391 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..d5f1c8d Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..4d6372e Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..3db14bb --- /dev/null +++ b/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..d460d1e --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..da38ab7 --- /dev/null +++ b/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000..24047dc --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,31 @@ +buildscript { + ext.kotlin_version = '1.3.50' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:4.1.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..94adc3a --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..293b9e0 --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 23 08:50:38 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip \ No newline at end of file diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 0000000..44e62bc --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/assets/images/default_image.png b/assets/images/default_image.png new file mode 100644 index 0000000..2e67519 Binary files /dev/null and b/assets/images/default_image.png differ diff --git a/assets/images/puzzle_side_image.png b/assets/images/puzzle_side_image.png new file mode 100644 index 0000000..22943e3 Binary files /dev/null and b/assets/images/puzzle_side_image.png differ diff --git a/assets/rive/dash.riv b/assets/rive/dash.riv new file mode 100644 index 0000000..dedbe61 Binary files /dev/null and b/assets/rive/dash.riv differ diff --git a/codemagic.yaml b/codemagic.yaml new file mode 100644 index 0000000..454f719 --- /dev/null +++ b/codemagic.yaml @@ -0,0 +1,31 @@ +workflows: + web-workflow: + name: Web Workflow + instance_type: mac_mini + max_build_duration: 60 + environment: + flutter: stable + xcode: latest + cocoapods: default + triggering: + events: + - push + scripts: + - name: Install dependencies + script: flutter packages pub get + - name: Enable web + script: flutter config --enable-web + - name: Testing + script: flutter test + - name: Build web + script: + | + flutter build web --release + cd build/web + 7z a -r ../web.zip ./* + artifacts: + - build/web.zip + publishing: + email: + recipients: + - sbis1999@gmail.com \ No newline at end of file diff --git a/firebase.json b/firebase.json new file mode 100644 index 0000000..6603732 --- /dev/null +++ b/firebase.json @@ -0,0 +1,16 @@ +{ + "hosting": { + "public": "build/web", + "ignore": [ + "firebase.json", + "**/.*", + "**/node_modules/**" + ], + "rewrites": [ + { + "source": "**", + "destination": "/index.html" + } + ] + } +} diff --git a/fonts/GoogleSans-Bold.ttf b/fonts/GoogleSans-Bold.ttf new file mode 100644 index 0000000..a3d73c8 Binary files /dev/null and b/fonts/GoogleSans-Bold.ttf differ diff --git a/fonts/GoogleSans-Medium.ttf b/fonts/GoogleSans-Medium.ttf new file mode 100644 index 0000000..282e88b Binary files /dev/null and b/fonts/GoogleSans-Medium.ttf differ diff --git a/fonts/GoogleSans-Regular.ttf b/fonts/GoogleSans-Regular.ttf new file mode 100644 index 0000000..5bb6e03 Binary files /dev/null and b/fonts/GoogleSans-Regular.ttf differ diff --git a/ios/.gitignore b/ios/.gitignore new file mode 100644 index 0000000..7a7f987 --- /dev/null +++ b/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..8d4492f --- /dev/null +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 9.0 + + diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..ec97fc6 --- /dev/null +++ b/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..c4855bf --- /dev/null +++ b/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/ios/Podfile b/ios/Podfile new file mode 100644 index 0000000..1e8c3c9 --- /dev/null +++ b/ios/Podfile @@ -0,0 +1,41 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..332de7c --- /dev/null +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,481 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.souvikbiswas.myFlutterPuzzle; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.souvikbiswas.myFlutterPuzzle; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.souvikbiswas.myFlutterPuzzle; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..c87d15a --- /dev/null +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000..70693e4 --- /dev/null +++ b/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d36b1fa --- /dev/null +++ b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000..dc9ada4 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000..28c6bf0 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000..2ccbfd9 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000..f091b6b Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000..4cde121 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000..d0ef06e Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 0000000..dcdc230 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 0000000..2ccbfd9 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000..c8f9ed8 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000..a6d6b86 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000..a6d6b86 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000..75b2d16 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000..c4df70d Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 0000000..6a84f41 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000..d0e1f58 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000..0bedcf2 --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +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. \ No newline at end of file diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f2e259c --- /dev/null +++ b/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist new file mode 100644 index 0000000..acc0b03 --- /dev/null +++ b/ios/Runner/Info.plist @@ -0,0 +1,47 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + My Flutter Puzzle + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + my_flutter_puzzle + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/lib/application/notifiers/anonymous_auth_notifier.dart b/lib/application/notifiers/anonymous_auth_notifier.dart new file mode 100644 index 0000000..8833f7d --- /dev/null +++ b/lib/application/notifiers/anonymous_auth_notifier.dart @@ -0,0 +1,33 @@ +// import 'package:flutter_riverpod/flutter_riverpod.dart'; +// import 'package:my_flutter_puzzle/application/states/anonymous_auth_state.dart'; +// import 'package:my_flutter_puzzle/models/user_info.dart'; +// import 'package:my_flutter_puzzle/utils/authentication_client.dart'; +// import 'package:my_flutter_puzzle/utils/database_client.dart'; + +// class AnonymousAuthNotifier extends StateNotifier { +// final AuthenticationClient _authentication; +// final DatabaseClient _databaseClient; + +// AnonymousAuthNotifier(this._authentication, this._databaseClient) +// : super(const AnonymousAuthState()); + +// triggerAnonymousLogin({required String name}) async { +// state = const AnonymousAuthState.processing(); + +// try { +// final user = await _authentication.signInAnonymously(name: name); +// state = AnonymousAuthState.done(user); +// state = const AnonymousAuthState.storingInfo(); +// final userData = UserData( +// uid: user.uid, +// name: user.displayName!, +// username: '${user.displayName!.split(' ')[0].toLowerCase()}@${user.uid}', +// timestamp: DateTime.now().millisecondsSinceEpoch, +// ); +// await _databaseClient.addUser(userInfo: userData); +// state = AnonymousAuthState.storageDone(userData); +// } catch (e) { +// state = AnonymousAuthState.error(message: e.toString()); +// } +// } +// } diff --git a/lib/application/notifiers/image_splitter_notifier.dart b/lib/application/notifiers/image_splitter_notifier.dart new file mode 100644 index 0000000..4d128a2 --- /dev/null +++ b/lib/application/notifiers/image_splitter_notifier.dart @@ -0,0 +1,56 @@ +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:my_flutter_puzzle/application/states/image_splitter_state.dart'; +import 'package:my_flutter_puzzle/res/strings.dart'; +import 'package:my_flutter_puzzle/utils/image_splitter.dart'; + +class ImageSplitterNotifier extends StateNotifier { + final ImageSplitter _splitter; + + ImageSplitterNotifier(this._splitter) : super(const ImageSplitterState()); + + getInitialImages({required int puzzleSize}) async { + state = const ImageSplitterState.generating(); + + try { + final data = await rootBundle.load(defaultImagePath); + final imageBytes = data.buffer.asUint8List(); + final _splitter = ImageSplitter(); + + final palette = + await _splitter.getImagePalette(const AssetImage(defaultImagePath)); + final images = await _splitter.runSplitterIsolate(imageBytes, puzzleSize); + + + state = ImageSplitterState.complete( + Image.asset(defaultImagePath), images, palette); + } catch (e) { + state = ImageSplitterState.error(message: e.toString()); + } + } + + generateImages({required ImagePicker picker, required int puzzleSize}) async { + state = const ImageSplitterState.generating(); + + try { + final imageBytesPalette = await _splitter.getImage(picker: picker); + + if (imageBytesPalette != null) { + final image = imageBytesPalette.item1; + final imageBytes = imageBytesPalette.item2; + final palette = imageBytesPalette.item3; + log('Image tuple retrieved'); + final images = + await _splitter.runSplitterIsolate(imageBytes, puzzleSize); + state = ImageSplitterState.complete(image, images, palette); + } + } catch (e) { + debugPrint('Error: $e'); + state = ImageSplitterState.error(message: e.toString()); + } + } +} diff --git a/lib/application/notifiers/login_register_notifier.dart b/lib/application/notifiers/login_register_notifier.dart new file mode 100644 index 0000000..9d1a4b9 --- /dev/null +++ b/lib/application/notifiers/login_register_notifier.dart @@ -0,0 +1,13 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +class IsLoginNotifier extends StateNotifier { + IsLoginNotifier() : super(true); + + selectLogin() { + state = true; + } + + selectRegister() { + state = false; + } +} diff --git a/lib/application/notifiers/puzzle_notifier.dart b/lib/application/notifiers/puzzle_notifier.dart new file mode 100644 index 0000000..29c6d05 --- /dev/null +++ b/lib/application/notifiers/puzzle_notifier.dart @@ -0,0 +1,320 @@ +import 'dart:async'; +import 'dart:developer'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:my_flutter_puzzle/application/states/puzzle_state.dart'; +import 'package:my_flutter_puzzle/models/puzzle_data.dart'; + +import '../../utils/puzzle_solver.dart'; + +class PuzzleNotifier extends StateNotifier { + final PuzzleSolverClient _solverClient; + + PuzzleNotifier(this._solverClient) : super(const PuzzleState()); + + // List>? _board2D; + // List? myList; + // int _moves = 0; + // final _puzzleSize = 3; + // final int _animationSpeedInMilliseconds = 300; + // bool _isComputing = false; + // bool _isAutoSolving = false; + // bool _isSolved = false; + + // Map? _offsetMap; + final List _solvedList = []; + + generateSolvedList() { + var _puzzleSize = _solverClient.size; + for (int i = 1; i < _puzzleSize * _puzzleSize; i++) { + _solvedList.add(i); + } + _solvedList.add(0); + } + + generateInitialPuzzle() { + List solvedList = []; + var _puzzleSize = _solverClient.size; + for (int i = 1; i < _puzzleSize * _puzzleSize; i++) { + solvedList.add(i); + } + solvedList.add(0); + final initialOffsetMap = createOffset(solvedList); + + final initialPuzzleData = PuzzleData( + board2D: [], + board1D: solvedList, + offsetMap: initialOffsetMap, + moves: 0, + tiles: 0, + puzzleSize: _puzzleSize, + ); + + return initialPuzzleData; + } + + restartPuzzle() async { + final initialPuzzle = generateInitialPuzzle(); + state = PuzzleState.scrambling(initialPuzzle); + await Future.delayed(const Duration(seconds: 2)); + initializePuzzle(initialPuzzleData: initialPuzzle); + } + + initializePuzzle({required PuzzleData initialPuzzleData}) { + state = const PuzzleState.initializing(); + + _solvedList.clear(); + _solverClient.setSize = initialPuzzleData.puzzleSize; + + generateSolvedList(); + + int start = 0; + const period = Duration(milliseconds: 1200); + PuzzleData currentPuzzleData = initialPuzzleData; + currentPuzzleData = scrambleBoard(currentPuzzleData); + + Timer.periodic(period, (Timer timer) async { + currentPuzzleData = scrambleBoard(currentPuzzleData); + start++; + if (start == 2) { + timer.cancel(); + await Future.delayed(const Duration(milliseconds: 1000)); + state = PuzzleState.current(currentPuzzleData); + } + }); + } + + Map createOffset(List board) { + Map offsetMap = {}; + int j = 0; + + log('BOARD: $board'); + + for (int i = 0; i < board.length; i++) { + var _puzzleSize = _solverClient.size; + final xMod = i % _puzzleSize; + double x = xMod / (_puzzleSize - 1); + + if (x % i == 0 && i != 0) j++; + int yMod = j % _puzzleSize; + double y = yMod / (_puzzleSize - 1); + + offsetMap.addEntries([ + MapEntry( + board[i], + FractionalOffset(x, y), + ) + ]); + } + + log('INITIAL OFFSET MAP: $offsetMap'); + + return offsetMap; + } + + onClick({required int index, required PuzzleData prev}) { + log('-----------------------'); + log('Tapped index: $index'); + + var board1D = prev.board1D; + var offsetMap = prev.offsetMap; + var _puzzleSize = prev.puzzleSize; + var _moves = prev.moves; + + int emptyTilePosIndex = board1D.indexOf(0); + int emptyTilePosRow = emptyTilePosIndex ~/ _puzzleSize; + int emptyTilePosCol = emptyTilePosIndex % _puzzleSize; + + int currentTileRow = index ~/ _puzzleSize; + int currentTileCol = index % _puzzleSize; + + //current element moves up + if ((currentTileRow - 1 == emptyTilePosRow) && + (currentTileCol == emptyTilePosCol)) { + board1D[emptyTilePosIndex] = board1D[index]; + board1D[index] = 0; + _moves++; + } + + //current element moves down + else if ((currentTileRow + 1 == emptyTilePosRow) && + (currentTileCol == emptyTilePosCol)) { + board1D[emptyTilePosIndex] = board1D[index]; + board1D[index] = 0; + _moves++; + } + + //current element moves left + else if ((currentTileRow == emptyTilePosRow) && + (currentTileCol + 1 == emptyTilePosCol)) { + board1D[emptyTilePosIndex] = board1D[index]; + board1D[index] = 0; + _moves++; + } + + //current element moves right + else if ((currentTileRow == emptyTilePosRow) && + (currentTileCol - 1 == emptyTilePosCol)) { + board1D[emptyTilePosIndex] = board1D[index]; + board1D[index] = 0; + _moves++; + } else { + if (currentTileCol == emptyTilePosCol) { + int low; + int high; + + // multiple elements move up + if (emptyTilePosRow < currentTileRow) { + low = emptyTilePosRow; + high = currentTileRow; + + int i = low; + while (i < high) { + board1D[(i * _puzzleSize) + emptyTilePosCol] = + board1D[(((i + 1) * _puzzleSize) + emptyTilePosCol)]; + + i += 1; + } + + board1D[(high * _puzzleSize) + emptyTilePosCol] = 0; + _moves++; + } + + //multiple elements move down + else { + low = emptyTilePosRow; + high = currentTileRow; + + int i = low; + while (i > high) { + board1D[(i * _puzzleSize) + emptyTilePosCol] = + board1D[(((i - 1) * _puzzleSize) + emptyTilePosCol)]; + + i -= 1; + } + + board1D[(high * _puzzleSize) + emptyTilePosCol] = 0; + _moves++; + } + } + + // multiple elements move left or right + if (currentTileRow == emptyTilePosRow) { + int low; + int high; + + // multiple elements move left + if (emptyTilePosCol < currentTileCol) { + low = emptyTilePosCol; + high = currentTileCol; + + int i = low; + while (i < high) { + board1D[(emptyTilePosRow * _puzzleSize) + i] = + board1D[(emptyTilePosRow * _puzzleSize) + (i + 1)]; + + i += 1; + } + + board1D[high + (emptyTilePosRow * _puzzleSize)] = 0; + _moves++; + } + + //multiple elements move right + else { + low = emptyTilePosCol; + high = currentTileCol; + + int i = low; + while (i > high) { + board1D[(i + (emptyTilePosRow * _puzzleSize))] = + board1D[(i - 1) + (emptyTilePosRow * _puzzleSize)]; + + i -= 1; + } + + board1D[high + (emptyTilePosRow * _puzzleSize)] = 0; + _moves++; + } + } + } + + updateOffset(board1D, _puzzleSize, offsetMap); + var tiles = calculateCorrectTiles(board: board1D); + + PuzzleData updatedData = PuzzleData( + board2D: prev.board2D, + board1D: board1D, + offsetMap: offsetMap, + moves: _moves, + tiles: tiles, + puzzleSize: _puzzleSize, + ); + + state = PuzzleState.current(updatedData); + + if (listEquals(board1D, _solvedList)) { + state = PuzzleState.solved(updatedData); + } + + log('List: $board1D'); + log('-----------------------'); + } + + int calculateCorrectTiles({required List board}) { + int correctTiles = 0; + + for (int i = 0; i < _solverClient.size * _solverClient.size; i++) { + if (board[i] != _solvedList[i] && board[i] != 0) { + correctTiles++; + } + } + + return correctTiles; + } + + void updateOffset( + List board1D, + int size, + Map offsetMap, + ) { + int j = 0; + + for (int i = 0; i < board1D.length; i++) { + final xMod = i % size; + double x = xMod / (size - 1); + + if (x % i == 0 && i != 0) j++; + int yMod = j % size; + double y = yMod / (size - 1); + + offsetMap[board1D[i]] = FractionalOffset(x, y); + } + log('OFFSET MAP: $offsetMap'); + log('BOARD: $board1D'); + } + + scrambleBoard(PuzzleData puzzleData) { + final generated2DBoard = _solverClient.createRandomBoard(); + final generated1DBoard = _solverClient.convertTo1D(generated2DBoard); + + updateOffset(generated1DBoard, puzzleData.puzzleSize, puzzleData.offsetMap); + + var tiles = calculateCorrectTiles(board: generated1DBoard); + + final newPuzzle = PuzzleData( + board2D: generated2DBoard, + board1D: generated1DBoard, + offsetMap: puzzleData.offsetMap, + moves: 0, + tiles: tiles, + puzzleSize: puzzleData.puzzleSize, + ); + + state = PuzzleState.scrambling(newPuzzle); + + return newPuzzle; + } +} diff --git a/lib/application/notifiers/puzzle_type_notifier.dart b/lib/application/notifiers/puzzle_type_notifier.dart new file mode 100644 index 0000000..7ac1738 --- /dev/null +++ b/lib/application/notifiers/puzzle_type_notifier.dart @@ -0,0 +1,23 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +enum PuzzleType { + normal, + photo, + multiplayer, +} + +class PuzzleTypeNotifier extends StateNotifier { + PuzzleTypeNotifier() : super(PuzzleType.normal); + + changeToNormal() { + state = PuzzleType.normal; + } + + changeToPhoto() { + state = PuzzleType.photo; + } + + changeToMultiplayer() { + state = PuzzleType.multiplayer; + } +} diff --git a/lib/application/notifiers/timer_notifier.dart b/lib/application/notifiers/timer_notifier.dart new file mode 100644 index 0000000..308a50c --- /dev/null +++ b/lib/application/notifiers/timer_notifier.dart @@ -0,0 +1,38 @@ +import 'dart:async'; + +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +class TimerNotifier extends StateNotifier { + TimerNotifier() : super('00:00:00'); + + Timer? _timer; + final _stopwatch = Stopwatch(); + + @override + void dispose() { + _stopwatch.stop(); + _timer?.cancel(); + super.dispose(); + } + + startTimer() { + _stopwatch.start(); + _timer = Timer.periodic(const Duration(seconds: 1), (timer) { + state = getDurationString(_stopwatch.elapsed); + }); + } + + stopTimer() { + _stopwatch.stop(); + _stopwatch.reset(); + state = getDurationString(_stopwatch.elapsed); + _timer?.cancel(); + } + + String getDurationString(Duration duration) { + String twoDigits(int n) => n.toString().padLeft(2, "0"); + String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60)); + String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60)); + return "${twoDigits(duration.inHours)}:$twoDigitMinutes:$twoDigitSeconds"; + } +} diff --git a/lib/application/states/add_user_database_state.dart b/lib/application/states/add_user_database_state.dart new file mode 100644 index 0000000..66e02ac --- /dev/null +++ b/lib/application/states/add_user_database_state.dart @@ -0,0 +1,12 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:my_flutter_puzzle/models/user_info.dart'; + +part 'add_user_database_state.freezed.dart'; + +@freezed +class AddUserDatabaseState with _$AddUserDatabaseState { + const factory AddUserDatabaseState() = AddUserDatabaseIdle; + const factory AddUserDatabaseState.processing() = AddUserDatabaseProcessing; + const factory AddUserDatabaseState.done(EUserData user) = AddUserDatabaseDone; + const factory AddUserDatabaseState.error({String? message}) = AddUserDatabaseError; +} diff --git a/lib/application/states/add_user_database_state.freezed.dart b/lib/application/states/add_user_database_state.freezed.dart new file mode 100644 index 0000000..0aaf354 --- /dev/null +++ b/lib/application/states/add_user_database_state.freezed.dart @@ -0,0 +1,644 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target + +part of 'add_user_database_state.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +/// @nodoc +class _$AddUserDatabaseStateTearOff { + const _$AddUserDatabaseStateTearOff(); + + AddUserDatabaseIdle call() { + return const AddUserDatabaseIdle(); + } + + AddUserDatabaseProcessing processing() { + return const AddUserDatabaseProcessing(); + } + + AddUserDatabaseDone done(EUserData user) { + return AddUserDatabaseDone( + user, + ); + } + + AddUserDatabaseError error({String? message}) { + return AddUserDatabaseError( + message: message, + ); + } +} + +/// @nodoc +const $AddUserDatabaseState = _$AddUserDatabaseStateTearOff(); + +/// @nodoc +mixin _$AddUserDatabaseState { + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() processing, + required TResult Function(EUserData user) done, + required TResult Function(String? message) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(EUserData user)? done, + TResult Function(String? message)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(EUserData user)? done, + TResult Function(String? message)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map( + TResult Function(AddUserDatabaseIdle value) $default, { + required TResult Function(AddUserDatabaseProcessing value) processing, + required TResult Function(AddUserDatabaseDone value) done, + required TResult Function(AddUserDatabaseError value) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(AddUserDatabaseIdle value)? $default, { + TResult Function(AddUserDatabaseProcessing value)? processing, + TResult Function(AddUserDatabaseDone value)? done, + TResult Function(AddUserDatabaseError value)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap( + TResult Function(AddUserDatabaseIdle value)? $default, { + TResult Function(AddUserDatabaseProcessing value)? processing, + TResult Function(AddUserDatabaseDone value)? done, + TResult Function(AddUserDatabaseError value)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $AddUserDatabaseStateCopyWith<$Res> { + factory $AddUserDatabaseStateCopyWith(AddUserDatabaseState value, + $Res Function(AddUserDatabaseState) then) = + _$AddUserDatabaseStateCopyWithImpl<$Res>; +} + +/// @nodoc +class _$AddUserDatabaseStateCopyWithImpl<$Res> + implements $AddUserDatabaseStateCopyWith<$Res> { + _$AddUserDatabaseStateCopyWithImpl(this._value, this._then); + + final AddUserDatabaseState _value; + // ignore: unused_field + final $Res Function(AddUserDatabaseState) _then; +} + +/// @nodoc +abstract class $AddUserDatabaseIdleCopyWith<$Res> { + factory $AddUserDatabaseIdleCopyWith( + AddUserDatabaseIdle value, $Res Function(AddUserDatabaseIdle) then) = + _$AddUserDatabaseIdleCopyWithImpl<$Res>; +} + +/// @nodoc +class _$AddUserDatabaseIdleCopyWithImpl<$Res> + extends _$AddUserDatabaseStateCopyWithImpl<$Res> + implements $AddUserDatabaseIdleCopyWith<$Res> { + _$AddUserDatabaseIdleCopyWithImpl( + AddUserDatabaseIdle _value, $Res Function(AddUserDatabaseIdle) _then) + : super(_value, (v) => _then(v as AddUserDatabaseIdle)); + + @override + AddUserDatabaseIdle get _value => super._value as AddUserDatabaseIdle; +} + +/// @nodoc + +class _$AddUserDatabaseIdle implements AddUserDatabaseIdle { + const _$AddUserDatabaseIdle(); + + @override + String toString() { + return 'AddUserDatabaseState()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is AddUserDatabaseIdle); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() processing, + required TResult Function(EUserData user) done, + required TResult Function(String? message) error, + }) { + return $default(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(EUserData user)? done, + TResult Function(String? message)? error, + }) { + return $default?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(EUserData user)? done, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if ($default != null) { + return $default(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(AddUserDatabaseIdle value) $default, { + required TResult Function(AddUserDatabaseProcessing value) processing, + required TResult Function(AddUserDatabaseDone value) done, + required TResult Function(AddUserDatabaseError value) error, + }) { + return $default(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(AddUserDatabaseIdle value)? $default, { + TResult Function(AddUserDatabaseProcessing value)? processing, + TResult Function(AddUserDatabaseDone value)? done, + TResult Function(AddUserDatabaseError value)? error, + }) { + return $default?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(AddUserDatabaseIdle value)? $default, { + TResult Function(AddUserDatabaseProcessing value)? processing, + TResult Function(AddUserDatabaseDone value)? done, + TResult Function(AddUserDatabaseError value)? error, + required TResult orElse(), + }) { + if ($default != null) { + return $default(this); + } + return orElse(); + } +} + +abstract class AddUserDatabaseIdle implements AddUserDatabaseState { + const factory AddUserDatabaseIdle() = _$AddUserDatabaseIdle; +} + +/// @nodoc +abstract class $AddUserDatabaseProcessingCopyWith<$Res> { + factory $AddUserDatabaseProcessingCopyWith(AddUserDatabaseProcessing value, + $Res Function(AddUserDatabaseProcessing) then) = + _$AddUserDatabaseProcessingCopyWithImpl<$Res>; +} + +/// @nodoc +class _$AddUserDatabaseProcessingCopyWithImpl<$Res> + extends _$AddUserDatabaseStateCopyWithImpl<$Res> + implements $AddUserDatabaseProcessingCopyWith<$Res> { + _$AddUserDatabaseProcessingCopyWithImpl(AddUserDatabaseProcessing _value, + $Res Function(AddUserDatabaseProcessing) _then) + : super(_value, (v) => _then(v as AddUserDatabaseProcessing)); + + @override + AddUserDatabaseProcessing get _value => + super._value as AddUserDatabaseProcessing; +} + +/// @nodoc + +class _$AddUserDatabaseProcessing implements AddUserDatabaseProcessing { + const _$AddUserDatabaseProcessing(); + + @override + String toString() { + return 'AddUserDatabaseState.processing()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is AddUserDatabaseProcessing); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() processing, + required TResult Function(EUserData user) done, + required TResult Function(String? message) error, + }) { + return processing(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(EUserData user)? done, + TResult Function(String? message)? error, + }) { + return processing?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(EUserData user)? done, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (processing != null) { + return processing(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(AddUserDatabaseIdle value) $default, { + required TResult Function(AddUserDatabaseProcessing value) processing, + required TResult Function(AddUserDatabaseDone value) done, + required TResult Function(AddUserDatabaseError value) error, + }) { + return processing(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(AddUserDatabaseIdle value)? $default, { + TResult Function(AddUserDatabaseProcessing value)? processing, + TResult Function(AddUserDatabaseDone value)? done, + TResult Function(AddUserDatabaseError value)? error, + }) { + return processing?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(AddUserDatabaseIdle value)? $default, { + TResult Function(AddUserDatabaseProcessing value)? processing, + TResult Function(AddUserDatabaseDone value)? done, + TResult Function(AddUserDatabaseError value)? error, + required TResult orElse(), + }) { + if (processing != null) { + return processing(this); + } + return orElse(); + } +} + +abstract class AddUserDatabaseProcessing implements AddUserDatabaseState { + const factory AddUserDatabaseProcessing() = _$AddUserDatabaseProcessing; +} + +/// @nodoc +abstract class $AddUserDatabaseDoneCopyWith<$Res> { + factory $AddUserDatabaseDoneCopyWith( + AddUserDatabaseDone value, $Res Function(AddUserDatabaseDone) then) = + _$AddUserDatabaseDoneCopyWithImpl<$Res>; + $Res call({EUserData user}); +} + +/// @nodoc +class _$AddUserDatabaseDoneCopyWithImpl<$Res> + extends _$AddUserDatabaseStateCopyWithImpl<$Res> + implements $AddUserDatabaseDoneCopyWith<$Res> { + _$AddUserDatabaseDoneCopyWithImpl( + AddUserDatabaseDone _value, $Res Function(AddUserDatabaseDone) _then) + : super(_value, (v) => _then(v as AddUserDatabaseDone)); + + @override + AddUserDatabaseDone get _value => super._value as AddUserDatabaseDone; + + @override + $Res call({ + Object? user = freezed, + }) { + return _then(AddUserDatabaseDone( + user == freezed + ? _value.user + : user // ignore: cast_nullable_to_non_nullable + as EUserData, + )); + } +} + +/// @nodoc + +class _$AddUserDatabaseDone implements AddUserDatabaseDone { + const _$AddUserDatabaseDone(this.user); + + @override + final EUserData user; + + @override + String toString() { + return 'AddUserDatabaseState.done(user: $user)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is AddUserDatabaseDone && + const DeepCollectionEquality().equals(other.user, user)); + } + + @override + int get hashCode => + Object.hash(runtimeType, const DeepCollectionEquality().hash(user)); + + @JsonKey(ignore: true) + @override + $AddUserDatabaseDoneCopyWith get copyWith => + _$AddUserDatabaseDoneCopyWithImpl(this, _$identity); + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() processing, + required TResult Function(EUserData user) done, + required TResult Function(String? message) error, + }) { + return done(user); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(EUserData user)? done, + TResult Function(String? message)? error, + }) { + return done?.call(user); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(EUserData user)? done, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (done != null) { + return done(user); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(AddUserDatabaseIdle value) $default, { + required TResult Function(AddUserDatabaseProcessing value) processing, + required TResult Function(AddUserDatabaseDone value) done, + required TResult Function(AddUserDatabaseError value) error, + }) { + return done(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(AddUserDatabaseIdle value)? $default, { + TResult Function(AddUserDatabaseProcessing value)? processing, + TResult Function(AddUserDatabaseDone value)? done, + TResult Function(AddUserDatabaseError value)? error, + }) { + return done?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(AddUserDatabaseIdle value)? $default, { + TResult Function(AddUserDatabaseProcessing value)? processing, + TResult Function(AddUserDatabaseDone value)? done, + TResult Function(AddUserDatabaseError value)? error, + required TResult orElse(), + }) { + if (done != null) { + return done(this); + } + return orElse(); + } +} + +abstract class AddUserDatabaseDone implements AddUserDatabaseState { + const factory AddUserDatabaseDone(EUserData user) = _$AddUserDatabaseDone; + + EUserData get user; + @JsonKey(ignore: true) + $AddUserDatabaseDoneCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $AddUserDatabaseErrorCopyWith<$Res> { + factory $AddUserDatabaseErrorCopyWith(AddUserDatabaseError value, + $Res Function(AddUserDatabaseError) then) = + _$AddUserDatabaseErrorCopyWithImpl<$Res>; + $Res call({String? message}); +} + +/// @nodoc +class _$AddUserDatabaseErrorCopyWithImpl<$Res> + extends _$AddUserDatabaseStateCopyWithImpl<$Res> + implements $AddUserDatabaseErrorCopyWith<$Res> { + _$AddUserDatabaseErrorCopyWithImpl( + AddUserDatabaseError _value, $Res Function(AddUserDatabaseError) _then) + : super(_value, (v) => _then(v as AddUserDatabaseError)); + + @override + AddUserDatabaseError get _value => super._value as AddUserDatabaseError; + + @override + $Res call({ + Object? message = freezed, + }) { + return _then(AddUserDatabaseError( + message: message == freezed + ? _value.message + : message // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc + +class _$AddUserDatabaseError implements AddUserDatabaseError { + const _$AddUserDatabaseError({this.message}); + + @override + final String? message; + + @override + String toString() { + return 'AddUserDatabaseState.error(message: $message)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is AddUserDatabaseError && + const DeepCollectionEquality().equals(other.message, message)); + } + + @override + int get hashCode => + Object.hash(runtimeType, const DeepCollectionEquality().hash(message)); + + @JsonKey(ignore: true) + @override + $AddUserDatabaseErrorCopyWith get copyWith => + _$AddUserDatabaseErrorCopyWithImpl( + this, _$identity); + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() processing, + required TResult Function(EUserData user) done, + required TResult Function(String? message) error, + }) { + return error(message); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(EUserData user)? done, + TResult Function(String? message)? error, + }) { + return error?.call(message); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(EUserData user)? done, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(message); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(AddUserDatabaseIdle value) $default, { + required TResult Function(AddUserDatabaseProcessing value) processing, + required TResult Function(AddUserDatabaseDone value) done, + required TResult Function(AddUserDatabaseError value) error, + }) { + return error(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(AddUserDatabaseIdle value)? $default, { + TResult Function(AddUserDatabaseProcessing value)? processing, + TResult Function(AddUserDatabaseDone value)? done, + TResult Function(AddUserDatabaseError value)? error, + }) { + return error?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(AddUserDatabaseIdle value)? $default, { + TResult Function(AddUserDatabaseProcessing value)? processing, + TResult Function(AddUserDatabaseDone value)? done, + TResult Function(AddUserDatabaseError value)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(this); + } + return orElse(); + } +} + +abstract class AddUserDatabaseError implements AddUserDatabaseState { + const factory AddUserDatabaseError({String? message}) = + _$AddUserDatabaseError; + + String? get message; + @JsonKey(ignore: true) + $AddUserDatabaseErrorCopyWith get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/application/states/email_auth_state.dart b/lib/application/states/email_auth_state.dart new file mode 100644 index 0000000..f8acb86 --- /dev/null +++ b/lib/application/states/email_auth_state.dart @@ -0,0 +1,14 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:my_flutter_puzzle/models/user_info.dart'; + +part 'email_auth_state.freezed.dart'; + +@freezed +class EmailAuthState with _$EmailAuthState { + const factory EmailAuthState() = EmailAuthIdle; + const factory EmailAuthState.processing() = EmailAuthProcessing; + const factory EmailAuthState.done(EUserData user) = EmailAuthDone; + const factory EmailAuthState.storingInfo() = UserStorageProcessing; + const factory EmailAuthState.storageDone(EUserData userData) = UserStorageDone; + const factory EmailAuthState.error({String? message}) = EmailAuthError; +} diff --git a/lib/application/states/email_auth_state.freezed.dart b/lib/application/states/email_auth_state.freezed.dart new file mode 100644 index 0000000..75a309d --- /dev/null +++ b/lib/application/states/email_auth_state.freezed.dart @@ -0,0 +1,997 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target + +part of 'email_auth_state.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +/// @nodoc +class _$EmailAuthStateTearOff { + const _$EmailAuthStateTearOff(); + + EmailAuthIdle call() { + return const EmailAuthIdle(); + } + + EmailAuthProcessing processing() { + return const EmailAuthProcessing(); + } + + EmailAuthDone done(EUserData user) { + return EmailAuthDone( + user, + ); + } + + UserStorageProcessing storingInfo() { + return const UserStorageProcessing(); + } + + UserStorageDone storageDone(EUserData userData) { + return UserStorageDone( + userData, + ); + } + + EmailAuthError error({String? message}) { + return EmailAuthError( + message: message, + ); + } +} + +/// @nodoc +const $EmailAuthState = _$EmailAuthStateTearOff(); + +/// @nodoc +mixin _$EmailAuthState { + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() processing, + required TResult Function(EUserData user) done, + required TResult Function() storingInfo, + required TResult Function(EUserData userData) storageDone, + required TResult Function(String? message) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(EUserData user)? done, + TResult Function()? storingInfo, + TResult Function(EUserData userData)? storageDone, + TResult Function(String? message)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(EUserData user)? done, + TResult Function()? storingInfo, + TResult Function(EUserData userData)? storageDone, + TResult Function(String? message)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map( + TResult Function(EmailAuthIdle value) $default, { + required TResult Function(EmailAuthProcessing value) processing, + required TResult Function(EmailAuthDone value) done, + required TResult Function(UserStorageProcessing value) storingInfo, + required TResult Function(UserStorageDone value) storageDone, + required TResult Function(EmailAuthError value) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(EmailAuthIdle value)? $default, { + TResult Function(EmailAuthProcessing value)? processing, + TResult Function(EmailAuthDone value)? done, + TResult Function(UserStorageProcessing value)? storingInfo, + TResult Function(UserStorageDone value)? storageDone, + TResult Function(EmailAuthError value)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap( + TResult Function(EmailAuthIdle value)? $default, { + TResult Function(EmailAuthProcessing value)? processing, + TResult Function(EmailAuthDone value)? done, + TResult Function(UserStorageProcessing value)? storingInfo, + TResult Function(UserStorageDone value)? storageDone, + TResult Function(EmailAuthError value)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $EmailAuthStateCopyWith<$Res> { + factory $EmailAuthStateCopyWith( + EmailAuthState value, $Res Function(EmailAuthState) then) = + _$EmailAuthStateCopyWithImpl<$Res>; +} + +/// @nodoc +class _$EmailAuthStateCopyWithImpl<$Res> + implements $EmailAuthStateCopyWith<$Res> { + _$EmailAuthStateCopyWithImpl(this._value, this._then); + + final EmailAuthState _value; + // ignore: unused_field + final $Res Function(EmailAuthState) _then; +} + +/// @nodoc +abstract class $EmailAuthIdleCopyWith<$Res> { + factory $EmailAuthIdleCopyWith( + EmailAuthIdle value, $Res Function(EmailAuthIdle) then) = + _$EmailAuthIdleCopyWithImpl<$Res>; +} + +/// @nodoc +class _$EmailAuthIdleCopyWithImpl<$Res> + extends _$EmailAuthStateCopyWithImpl<$Res> + implements $EmailAuthIdleCopyWith<$Res> { + _$EmailAuthIdleCopyWithImpl( + EmailAuthIdle _value, $Res Function(EmailAuthIdle) _then) + : super(_value, (v) => _then(v as EmailAuthIdle)); + + @override + EmailAuthIdle get _value => super._value as EmailAuthIdle; +} + +/// @nodoc + +class _$EmailAuthIdle implements EmailAuthIdle { + const _$EmailAuthIdle(); + + @override + String toString() { + return 'EmailAuthState()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is EmailAuthIdle); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() processing, + required TResult Function(EUserData user) done, + required TResult Function() storingInfo, + required TResult Function(EUserData userData) storageDone, + required TResult Function(String? message) error, + }) { + return $default(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(EUserData user)? done, + TResult Function()? storingInfo, + TResult Function(EUserData userData)? storageDone, + TResult Function(String? message)? error, + }) { + return $default?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(EUserData user)? done, + TResult Function()? storingInfo, + TResult Function(EUserData userData)? storageDone, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if ($default != null) { + return $default(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(EmailAuthIdle value) $default, { + required TResult Function(EmailAuthProcessing value) processing, + required TResult Function(EmailAuthDone value) done, + required TResult Function(UserStorageProcessing value) storingInfo, + required TResult Function(UserStorageDone value) storageDone, + required TResult Function(EmailAuthError value) error, + }) { + return $default(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(EmailAuthIdle value)? $default, { + TResult Function(EmailAuthProcessing value)? processing, + TResult Function(EmailAuthDone value)? done, + TResult Function(UserStorageProcessing value)? storingInfo, + TResult Function(UserStorageDone value)? storageDone, + TResult Function(EmailAuthError value)? error, + }) { + return $default?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(EmailAuthIdle value)? $default, { + TResult Function(EmailAuthProcessing value)? processing, + TResult Function(EmailAuthDone value)? done, + TResult Function(UserStorageProcessing value)? storingInfo, + TResult Function(UserStorageDone value)? storageDone, + TResult Function(EmailAuthError value)? error, + required TResult orElse(), + }) { + if ($default != null) { + return $default(this); + } + return orElse(); + } +} + +abstract class EmailAuthIdle implements EmailAuthState { + const factory EmailAuthIdle() = _$EmailAuthIdle; +} + +/// @nodoc +abstract class $EmailAuthProcessingCopyWith<$Res> { + factory $EmailAuthProcessingCopyWith( + EmailAuthProcessing value, $Res Function(EmailAuthProcessing) then) = + _$EmailAuthProcessingCopyWithImpl<$Res>; +} + +/// @nodoc +class _$EmailAuthProcessingCopyWithImpl<$Res> + extends _$EmailAuthStateCopyWithImpl<$Res> + implements $EmailAuthProcessingCopyWith<$Res> { + _$EmailAuthProcessingCopyWithImpl( + EmailAuthProcessing _value, $Res Function(EmailAuthProcessing) _then) + : super(_value, (v) => _then(v as EmailAuthProcessing)); + + @override + EmailAuthProcessing get _value => super._value as EmailAuthProcessing; +} + +/// @nodoc + +class _$EmailAuthProcessing implements EmailAuthProcessing { + const _$EmailAuthProcessing(); + + @override + String toString() { + return 'EmailAuthState.processing()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is EmailAuthProcessing); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() processing, + required TResult Function(EUserData user) done, + required TResult Function() storingInfo, + required TResult Function(EUserData userData) storageDone, + required TResult Function(String? message) error, + }) { + return processing(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(EUserData user)? done, + TResult Function()? storingInfo, + TResult Function(EUserData userData)? storageDone, + TResult Function(String? message)? error, + }) { + return processing?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(EUserData user)? done, + TResult Function()? storingInfo, + TResult Function(EUserData userData)? storageDone, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (processing != null) { + return processing(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(EmailAuthIdle value) $default, { + required TResult Function(EmailAuthProcessing value) processing, + required TResult Function(EmailAuthDone value) done, + required TResult Function(UserStorageProcessing value) storingInfo, + required TResult Function(UserStorageDone value) storageDone, + required TResult Function(EmailAuthError value) error, + }) { + return processing(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(EmailAuthIdle value)? $default, { + TResult Function(EmailAuthProcessing value)? processing, + TResult Function(EmailAuthDone value)? done, + TResult Function(UserStorageProcessing value)? storingInfo, + TResult Function(UserStorageDone value)? storageDone, + TResult Function(EmailAuthError value)? error, + }) { + return processing?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(EmailAuthIdle value)? $default, { + TResult Function(EmailAuthProcessing value)? processing, + TResult Function(EmailAuthDone value)? done, + TResult Function(UserStorageProcessing value)? storingInfo, + TResult Function(UserStorageDone value)? storageDone, + TResult Function(EmailAuthError value)? error, + required TResult orElse(), + }) { + if (processing != null) { + return processing(this); + } + return orElse(); + } +} + +abstract class EmailAuthProcessing implements EmailAuthState { + const factory EmailAuthProcessing() = _$EmailAuthProcessing; +} + +/// @nodoc +abstract class $EmailAuthDoneCopyWith<$Res> { + factory $EmailAuthDoneCopyWith( + EmailAuthDone value, $Res Function(EmailAuthDone) then) = + _$EmailAuthDoneCopyWithImpl<$Res>; + $Res call({EUserData user}); +} + +/// @nodoc +class _$EmailAuthDoneCopyWithImpl<$Res> + extends _$EmailAuthStateCopyWithImpl<$Res> + implements $EmailAuthDoneCopyWith<$Res> { + _$EmailAuthDoneCopyWithImpl( + EmailAuthDone _value, $Res Function(EmailAuthDone) _then) + : super(_value, (v) => _then(v as EmailAuthDone)); + + @override + EmailAuthDone get _value => super._value as EmailAuthDone; + + @override + $Res call({ + Object? user = freezed, + }) { + return _then(EmailAuthDone( + user == freezed + ? _value.user + : user // ignore: cast_nullable_to_non_nullable + as EUserData, + )); + } +} + +/// @nodoc + +class _$EmailAuthDone implements EmailAuthDone { + const _$EmailAuthDone(this.user); + + @override + final EUserData user; + + @override + String toString() { + return 'EmailAuthState.done(user: $user)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is EmailAuthDone && + const DeepCollectionEquality().equals(other.user, user)); + } + + @override + int get hashCode => + Object.hash(runtimeType, const DeepCollectionEquality().hash(user)); + + @JsonKey(ignore: true) + @override + $EmailAuthDoneCopyWith get copyWith => + _$EmailAuthDoneCopyWithImpl(this, _$identity); + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() processing, + required TResult Function(EUserData user) done, + required TResult Function() storingInfo, + required TResult Function(EUserData userData) storageDone, + required TResult Function(String? message) error, + }) { + return done(user); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(EUserData user)? done, + TResult Function()? storingInfo, + TResult Function(EUserData userData)? storageDone, + TResult Function(String? message)? error, + }) { + return done?.call(user); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(EUserData user)? done, + TResult Function()? storingInfo, + TResult Function(EUserData userData)? storageDone, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (done != null) { + return done(user); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(EmailAuthIdle value) $default, { + required TResult Function(EmailAuthProcessing value) processing, + required TResult Function(EmailAuthDone value) done, + required TResult Function(UserStorageProcessing value) storingInfo, + required TResult Function(UserStorageDone value) storageDone, + required TResult Function(EmailAuthError value) error, + }) { + return done(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(EmailAuthIdle value)? $default, { + TResult Function(EmailAuthProcessing value)? processing, + TResult Function(EmailAuthDone value)? done, + TResult Function(UserStorageProcessing value)? storingInfo, + TResult Function(UserStorageDone value)? storageDone, + TResult Function(EmailAuthError value)? error, + }) { + return done?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(EmailAuthIdle value)? $default, { + TResult Function(EmailAuthProcessing value)? processing, + TResult Function(EmailAuthDone value)? done, + TResult Function(UserStorageProcessing value)? storingInfo, + TResult Function(UserStorageDone value)? storageDone, + TResult Function(EmailAuthError value)? error, + required TResult orElse(), + }) { + if (done != null) { + return done(this); + } + return orElse(); + } +} + +abstract class EmailAuthDone implements EmailAuthState { + const factory EmailAuthDone(EUserData user) = _$EmailAuthDone; + + EUserData get user; + @JsonKey(ignore: true) + $EmailAuthDoneCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $UserStorageProcessingCopyWith<$Res> { + factory $UserStorageProcessingCopyWith(UserStorageProcessing value, + $Res Function(UserStorageProcessing) then) = + _$UserStorageProcessingCopyWithImpl<$Res>; +} + +/// @nodoc +class _$UserStorageProcessingCopyWithImpl<$Res> + extends _$EmailAuthStateCopyWithImpl<$Res> + implements $UserStorageProcessingCopyWith<$Res> { + _$UserStorageProcessingCopyWithImpl( + UserStorageProcessing _value, $Res Function(UserStorageProcessing) _then) + : super(_value, (v) => _then(v as UserStorageProcessing)); + + @override + UserStorageProcessing get _value => super._value as UserStorageProcessing; +} + +/// @nodoc + +class _$UserStorageProcessing implements UserStorageProcessing { + const _$UserStorageProcessing(); + + @override + String toString() { + return 'EmailAuthState.storingInfo()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is UserStorageProcessing); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() processing, + required TResult Function(EUserData user) done, + required TResult Function() storingInfo, + required TResult Function(EUserData userData) storageDone, + required TResult Function(String? message) error, + }) { + return storingInfo(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(EUserData user)? done, + TResult Function()? storingInfo, + TResult Function(EUserData userData)? storageDone, + TResult Function(String? message)? error, + }) { + return storingInfo?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(EUserData user)? done, + TResult Function()? storingInfo, + TResult Function(EUserData userData)? storageDone, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (storingInfo != null) { + return storingInfo(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(EmailAuthIdle value) $default, { + required TResult Function(EmailAuthProcessing value) processing, + required TResult Function(EmailAuthDone value) done, + required TResult Function(UserStorageProcessing value) storingInfo, + required TResult Function(UserStorageDone value) storageDone, + required TResult Function(EmailAuthError value) error, + }) { + return storingInfo(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(EmailAuthIdle value)? $default, { + TResult Function(EmailAuthProcessing value)? processing, + TResult Function(EmailAuthDone value)? done, + TResult Function(UserStorageProcessing value)? storingInfo, + TResult Function(UserStorageDone value)? storageDone, + TResult Function(EmailAuthError value)? error, + }) { + return storingInfo?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(EmailAuthIdle value)? $default, { + TResult Function(EmailAuthProcessing value)? processing, + TResult Function(EmailAuthDone value)? done, + TResult Function(UserStorageProcessing value)? storingInfo, + TResult Function(UserStorageDone value)? storageDone, + TResult Function(EmailAuthError value)? error, + required TResult orElse(), + }) { + if (storingInfo != null) { + return storingInfo(this); + } + return orElse(); + } +} + +abstract class UserStorageProcessing implements EmailAuthState { + const factory UserStorageProcessing() = _$UserStorageProcessing; +} + +/// @nodoc +abstract class $UserStorageDoneCopyWith<$Res> { + factory $UserStorageDoneCopyWith( + UserStorageDone value, $Res Function(UserStorageDone) then) = + _$UserStorageDoneCopyWithImpl<$Res>; + $Res call({EUserData userData}); +} + +/// @nodoc +class _$UserStorageDoneCopyWithImpl<$Res> + extends _$EmailAuthStateCopyWithImpl<$Res> + implements $UserStorageDoneCopyWith<$Res> { + _$UserStorageDoneCopyWithImpl( + UserStorageDone _value, $Res Function(UserStorageDone) _then) + : super(_value, (v) => _then(v as UserStorageDone)); + + @override + UserStorageDone get _value => super._value as UserStorageDone; + + @override + $Res call({ + Object? userData = freezed, + }) { + return _then(UserStorageDone( + userData == freezed + ? _value.userData + : userData // ignore: cast_nullable_to_non_nullable + as EUserData, + )); + } +} + +/// @nodoc + +class _$UserStorageDone implements UserStorageDone { + const _$UserStorageDone(this.userData); + + @override + final EUserData userData; + + @override + String toString() { + return 'EmailAuthState.storageDone(userData: $userData)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is UserStorageDone && + const DeepCollectionEquality().equals(other.userData, userData)); + } + + @override + int get hashCode => + Object.hash(runtimeType, const DeepCollectionEquality().hash(userData)); + + @JsonKey(ignore: true) + @override + $UserStorageDoneCopyWith get copyWith => + _$UserStorageDoneCopyWithImpl(this, _$identity); + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() processing, + required TResult Function(EUserData user) done, + required TResult Function() storingInfo, + required TResult Function(EUserData userData) storageDone, + required TResult Function(String? message) error, + }) { + return storageDone(userData); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(EUserData user)? done, + TResult Function()? storingInfo, + TResult Function(EUserData userData)? storageDone, + TResult Function(String? message)? error, + }) { + return storageDone?.call(userData); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(EUserData user)? done, + TResult Function()? storingInfo, + TResult Function(EUserData userData)? storageDone, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (storageDone != null) { + return storageDone(userData); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(EmailAuthIdle value) $default, { + required TResult Function(EmailAuthProcessing value) processing, + required TResult Function(EmailAuthDone value) done, + required TResult Function(UserStorageProcessing value) storingInfo, + required TResult Function(UserStorageDone value) storageDone, + required TResult Function(EmailAuthError value) error, + }) { + return storageDone(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(EmailAuthIdle value)? $default, { + TResult Function(EmailAuthProcessing value)? processing, + TResult Function(EmailAuthDone value)? done, + TResult Function(UserStorageProcessing value)? storingInfo, + TResult Function(UserStorageDone value)? storageDone, + TResult Function(EmailAuthError value)? error, + }) { + return storageDone?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(EmailAuthIdle value)? $default, { + TResult Function(EmailAuthProcessing value)? processing, + TResult Function(EmailAuthDone value)? done, + TResult Function(UserStorageProcessing value)? storingInfo, + TResult Function(UserStorageDone value)? storageDone, + TResult Function(EmailAuthError value)? error, + required TResult orElse(), + }) { + if (storageDone != null) { + return storageDone(this); + } + return orElse(); + } +} + +abstract class UserStorageDone implements EmailAuthState { + const factory UserStorageDone(EUserData userData) = _$UserStorageDone; + + EUserData get userData; + @JsonKey(ignore: true) + $UserStorageDoneCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $EmailAuthErrorCopyWith<$Res> { + factory $EmailAuthErrorCopyWith( + EmailAuthError value, $Res Function(EmailAuthError) then) = + _$EmailAuthErrorCopyWithImpl<$Res>; + $Res call({String? message}); +} + +/// @nodoc +class _$EmailAuthErrorCopyWithImpl<$Res> + extends _$EmailAuthStateCopyWithImpl<$Res> + implements $EmailAuthErrorCopyWith<$Res> { + _$EmailAuthErrorCopyWithImpl( + EmailAuthError _value, $Res Function(EmailAuthError) _then) + : super(_value, (v) => _then(v as EmailAuthError)); + + @override + EmailAuthError get _value => super._value as EmailAuthError; + + @override + $Res call({ + Object? message = freezed, + }) { + return _then(EmailAuthError( + message: message == freezed + ? _value.message + : message // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc + +class _$EmailAuthError implements EmailAuthError { + const _$EmailAuthError({this.message}); + + @override + final String? message; + + @override + String toString() { + return 'EmailAuthState.error(message: $message)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is EmailAuthError && + const DeepCollectionEquality().equals(other.message, message)); + } + + @override + int get hashCode => + Object.hash(runtimeType, const DeepCollectionEquality().hash(message)); + + @JsonKey(ignore: true) + @override + $EmailAuthErrorCopyWith get copyWith => + _$EmailAuthErrorCopyWithImpl(this, _$identity); + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() processing, + required TResult Function(EUserData user) done, + required TResult Function() storingInfo, + required TResult Function(EUserData userData) storageDone, + required TResult Function(String? message) error, + }) { + return error(message); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(EUserData user)? done, + TResult Function()? storingInfo, + TResult Function(EUserData userData)? storageDone, + TResult Function(String? message)? error, + }) { + return error?.call(message); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(EUserData user)? done, + TResult Function()? storingInfo, + TResult Function(EUserData userData)? storageDone, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(message); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(EmailAuthIdle value) $default, { + required TResult Function(EmailAuthProcessing value) processing, + required TResult Function(EmailAuthDone value) done, + required TResult Function(UserStorageProcessing value) storingInfo, + required TResult Function(UserStorageDone value) storageDone, + required TResult Function(EmailAuthError value) error, + }) { + return error(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(EmailAuthIdle value)? $default, { + TResult Function(EmailAuthProcessing value)? processing, + TResult Function(EmailAuthDone value)? done, + TResult Function(UserStorageProcessing value)? storingInfo, + TResult Function(UserStorageDone value)? storageDone, + TResult Function(EmailAuthError value)? error, + }) { + return error?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(EmailAuthIdle value)? $default, { + TResult Function(EmailAuthProcessing value)? processing, + TResult Function(EmailAuthDone value)? done, + TResult Function(UserStorageProcessing value)? storingInfo, + TResult Function(UserStorageDone value)? storageDone, + TResult Function(EmailAuthError value)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(this); + } + return orElse(); + } +} + +abstract class EmailAuthError implements EmailAuthState { + const factory EmailAuthError({String? message}) = _$EmailAuthError; + + String? get message; + @JsonKey(ignore: true) + $EmailAuthErrorCopyWith get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/application/states/image_splitter_state.dart b/lib/application/states/image_splitter_state.dart new file mode 100644 index 0000000..4fac3ef --- /dev/null +++ b/lib/application/states/image_splitter_state.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:palette_generator/palette_generator.dart'; + +part 'image_splitter_state.freezed.dart'; + +@freezed +class ImageSplitterState with _$ImageSplitterState { + const factory ImageSplitterState() = ImageSplitterIdle; + + const factory ImageSplitterState.generating() = ImageSplitterGenerating; + + const factory ImageSplitterState.complete(Image image, List images, PaletteGenerator palette) = + ImageSplitterComplete; + + const factory ImageSplitterState.error({String? message}) = ImageSplitterError; +} diff --git a/lib/application/states/image_splitter_state.freezed.dart b/lib/application/states/image_splitter_state.freezed.dart new file mode 100644 index 0000000..837309c --- /dev/null +++ b/lib/application/states/image_splitter_state.freezed.dart @@ -0,0 +1,687 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target + +part of 'image_splitter_state.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +/// @nodoc +class _$ImageSplitterStateTearOff { + const _$ImageSplitterStateTearOff(); + + ImageSplitterIdle call() { + return const ImageSplitterIdle(); + } + + ImageSplitterGenerating generating() { + return const ImageSplitterGenerating(); + } + + ImageSplitterComplete complete( + Image image, List images, PaletteGenerator palette) { + return ImageSplitterComplete( + image, + images, + palette, + ); + } + + ImageSplitterError error({String? message}) { + return ImageSplitterError( + message: message, + ); + } +} + +/// @nodoc +const $ImageSplitterState = _$ImageSplitterStateTearOff(); + +/// @nodoc +mixin _$ImageSplitterState { + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() generating, + required TResult Function( + Image image, List images, PaletteGenerator palette) + complete, + required TResult Function(String? message) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? generating, + TResult Function(Image image, List images, PaletteGenerator palette)? + complete, + TResult Function(String? message)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? generating, + TResult Function(Image image, List images, PaletteGenerator palette)? + complete, + TResult Function(String? message)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map( + TResult Function(ImageSplitterIdle value) $default, { + required TResult Function(ImageSplitterGenerating value) generating, + required TResult Function(ImageSplitterComplete value) complete, + required TResult Function(ImageSplitterError value) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(ImageSplitterIdle value)? $default, { + TResult Function(ImageSplitterGenerating value)? generating, + TResult Function(ImageSplitterComplete value)? complete, + TResult Function(ImageSplitterError value)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap( + TResult Function(ImageSplitterIdle value)? $default, { + TResult Function(ImageSplitterGenerating value)? generating, + TResult Function(ImageSplitterComplete value)? complete, + TResult Function(ImageSplitterError value)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $ImageSplitterStateCopyWith<$Res> { + factory $ImageSplitterStateCopyWith( + ImageSplitterState value, $Res Function(ImageSplitterState) then) = + _$ImageSplitterStateCopyWithImpl<$Res>; +} + +/// @nodoc +class _$ImageSplitterStateCopyWithImpl<$Res> + implements $ImageSplitterStateCopyWith<$Res> { + _$ImageSplitterStateCopyWithImpl(this._value, this._then); + + final ImageSplitterState _value; + // ignore: unused_field + final $Res Function(ImageSplitterState) _then; +} + +/// @nodoc +abstract class $ImageSplitterIdleCopyWith<$Res> { + factory $ImageSplitterIdleCopyWith( + ImageSplitterIdle value, $Res Function(ImageSplitterIdle) then) = + _$ImageSplitterIdleCopyWithImpl<$Res>; +} + +/// @nodoc +class _$ImageSplitterIdleCopyWithImpl<$Res> + extends _$ImageSplitterStateCopyWithImpl<$Res> + implements $ImageSplitterIdleCopyWith<$Res> { + _$ImageSplitterIdleCopyWithImpl( + ImageSplitterIdle _value, $Res Function(ImageSplitterIdle) _then) + : super(_value, (v) => _then(v as ImageSplitterIdle)); + + @override + ImageSplitterIdle get _value => super._value as ImageSplitterIdle; +} + +/// @nodoc + +class _$ImageSplitterIdle implements ImageSplitterIdle { + const _$ImageSplitterIdle(); + + @override + String toString() { + return 'ImageSplitterState()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is ImageSplitterIdle); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() generating, + required TResult Function( + Image image, List images, PaletteGenerator palette) + complete, + required TResult Function(String? message) error, + }) { + return $default(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? generating, + TResult Function(Image image, List images, PaletteGenerator palette)? + complete, + TResult Function(String? message)? error, + }) { + return $default?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? generating, + TResult Function(Image image, List images, PaletteGenerator palette)? + complete, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if ($default != null) { + return $default(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(ImageSplitterIdle value) $default, { + required TResult Function(ImageSplitterGenerating value) generating, + required TResult Function(ImageSplitterComplete value) complete, + required TResult Function(ImageSplitterError value) error, + }) { + return $default(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(ImageSplitterIdle value)? $default, { + TResult Function(ImageSplitterGenerating value)? generating, + TResult Function(ImageSplitterComplete value)? complete, + TResult Function(ImageSplitterError value)? error, + }) { + return $default?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(ImageSplitterIdle value)? $default, { + TResult Function(ImageSplitterGenerating value)? generating, + TResult Function(ImageSplitterComplete value)? complete, + TResult Function(ImageSplitterError value)? error, + required TResult orElse(), + }) { + if ($default != null) { + return $default(this); + } + return orElse(); + } +} + +abstract class ImageSplitterIdle implements ImageSplitterState { + const factory ImageSplitterIdle() = _$ImageSplitterIdle; +} + +/// @nodoc +abstract class $ImageSplitterGeneratingCopyWith<$Res> { + factory $ImageSplitterGeneratingCopyWith(ImageSplitterGenerating value, + $Res Function(ImageSplitterGenerating) then) = + _$ImageSplitterGeneratingCopyWithImpl<$Res>; +} + +/// @nodoc +class _$ImageSplitterGeneratingCopyWithImpl<$Res> + extends _$ImageSplitterStateCopyWithImpl<$Res> + implements $ImageSplitterGeneratingCopyWith<$Res> { + _$ImageSplitterGeneratingCopyWithImpl(ImageSplitterGenerating _value, + $Res Function(ImageSplitterGenerating) _then) + : super(_value, (v) => _then(v as ImageSplitterGenerating)); + + @override + ImageSplitterGenerating get _value => super._value as ImageSplitterGenerating; +} + +/// @nodoc + +class _$ImageSplitterGenerating implements ImageSplitterGenerating { + const _$ImageSplitterGenerating(); + + @override + String toString() { + return 'ImageSplitterState.generating()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is ImageSplitterGenerating); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() generating, + required TResult Function( + Image image, List images, PaletteGenerator palette) + complete, + required TResult Function(String? message) error, + }) { + return generating(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? generating, + TResult Function(Image image, List images, PaletteGenerator palette)? + complete, + TResult Function(String? message)? error, + }) { + return generating?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? generating, + TResult Function(Image image, List images, PaletteGenerator palette)? + complete, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (generating != null) { + return generating(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(ImageSplitterIdle value) $default, { + required TResult Function(ImageSplitterGenerating value) generating, + required TResult Function(ImageSplitterComplete value) complete, + required TResult Function(ImageSplitterError value) error, + }) { + return generating(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(ImageSplitterIdle value)? $default, { + TResult Function(ImageSplitterGenerating value)? generating, + TResult Function(ImageSplitterComplete value)? complete, + TResult Function(ImageSplitterError value)? error, + }) { + return generating?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(ImageSplitterIdle value)? $default, { + TResult Function(ImageSplitterGenerating value)? generating, + TResult Function(ImageSplitterComplete value)? complete, + TResult Function(ImageSplitterError value)? error, + required TResult orElse(), + }) { + if (generating != null) { + return generating(this); + } + return orElse(); + } +} + +abstract class ImageSplitterGenerating implements ImageSplitterState { + const factory ImageSplitterGenerating() = _$ImageSplitterGenerating; +} + +/// @nodoc +abstract class $ImageSplitterCompleteCopyWith<$Res> { + factory $ImageSplitterCompleteCopyWith(ImageSplitterComplete value, + $Res Function(ImageSplitterComplete) then) = + _$ImageSplitterCompleteCopyWithImpl<$Res>; + $Res call({Image image, List images, PaletteGenerator palette}); +} + +/// @nodoc +class _$ImageSplitterCompleteCopyWithImpl<$Res> + extends _$ImageSplitterStateCopyWithImpl<$Res> + implements $ImageSplitterCompleteCopyWith<$Res> { + _$ImageSplitterCompleteCopyWithImpl( + ImageSplitterComplete _value, $Res Function(ImageSplitterComplete) _then) + : super(_value, (v) => _then(v as ImageSplitterComplete)); + + @override + ImageSplitterComplete get _value => super._value as ImageSplitterComplete; + + @override + $Res call({ + Object? image = freezed, + Object? images = freezed, + Object? palette = freezed, + }) { + return _then(ImageSplitterComplete( + image == freezed + ? _value.image + : image // ignore: cast_nullable_to_non_nullable + as Image, + images == freezed + ? _value.images + : images // ignore: cast_nullable_to_non_nullable + as List, + palette == freezed + ? _value.palette + : palette // ignore: cast_nullable_to_non_nullable + as PaletteGenerator, + )); + } +} + +/// @nodoc + +class _$ImageSplitterComplete implements ImageSplitterComplete { + const _$ImageSplitterComplete(this.image, this.images, this.palette); + + @override + final Image image; + @override + final List images; + @override + final PaletteGenerator palette; + + @override + String toString() { + return 'ImageSplitterState.complete(image: $image, images: $images, palette: $palette)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is ImageSplitterComplete && + const DeepCollectionEquality().equals(other.image, image) && + const DeepCollectionEquality().equals(other.images, images) && + const DeepCollectionEquality().equals(other.palette, palette)); + } + + @override + int get hashCode => Object.hash( + runtimeType, + const DeepCollectionEquality().hash(image), + const DeepCollectionEquality().hash(images), + const DeepCollectionEquality().hash(palette)); + + @JsonKey(ignore: true) + @override + $ImageSplitterCompleteCopyWith get copyWith => + _$ImageSplitterCompleteCopyWithImpl( + this, _$identity); + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() generating, + required TResult Function( + Image image, List images, PaletteGenerator palette) + complete, + required TResult Function(String? message) error, + }) { + return complete(image, images, palette); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? generating, + TResult Function(Image image, List images, PaletteGenerator palette)? + complete, + TResult Function(String? message)? error, + }) { + return complete?.call(image, images, palette); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? generating, + TResult Function(Image image, List images, PaletteGenerator palette)? + complete, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (complete != null) { + return complete(image, images, palette); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(ImageSplitterIdle value) $default, { + required TResult Function(ImageSplitterGenerating value) generating, + required TResult Function(ImageSplitterComplete value) complete, + required TResult Function(ImageSplitterError value) error, + }) { + return complete(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(ImageSplitterIdle value)? $default, { + TResult Function(ImageSplitterGenerating value)? generating, + TResult Function(ImageSplitterComplete value)? complete, + TResult Function(ImageSplitterError value)? error, + }) { + return complete?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(ImageSplitterIdle value)? $default, { + TResult Function(ImageSplitterGenerating value)? generating, + TResult Function(ImageSplitterComplete value)? complete, + TResult Function(ImageSplitterError value)? error, + required TResult orElse(), + }) { + if (complete != null) { + return complete(this); + } + return orElse(); + } +} + +abstract class ImageSplitterComplete implements ImageSplitterState { + const factory ImageSplitterComplete( + Image image, List images, PaletteGenerator palette) = + _$ImageSplitterComplete; + + Image get image; + List get images; + PaletteGenerator get palette; + @JsonKey(ignore: true) + $ImageSplitterCompleteCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $ImageSplitterErrorCopyWith<$Res> { + factory $ImageSplitterErrorCopyWith( + ImageSplitterError value, $Res Function(ImageSplitterError) then) = + _$ImageSplitterErrorCopyWithImpl<$Res>; + $Res call({String? message}); +} + +/// @nodoc +class _$ImageSplitterErrorCopyWithImpl<$Res> + extends _$ImageSplitterStateCopyWithImpl<$Res> + implements $ImageSplitterErrorCopyWith<$Res> { + _$ImageSplitterErrorCopyWithImpl( + ImageSplitterError _value, $Res Function(ImageSplitterError) _then) + : super(_value, (v) => _then(v as ImageSplitterError)); + + @override + ImageSplitterError get _value => super._value as ImageSplitterError; + + @override + $Res call({ + Object? message = freezed, + }) { + return _then(ImageSplitterError( + message: message == freezed + ? _value.message + : message // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc + +class _$ImageSplitterError implements ImageSplitterError { + const _$ImageSplitterError({this.message}); + + @override + final String? message; + + @override + String toString() { + return 'ImageSplitterState.error(message: $message)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is ImageSplitterError && + const DeepCollectionEquality().equals(other.message, message)); + } + + @override + int get hashCode => + Object.hash(runtimeType, const DeepCollectionEquality().hash(message)); + + @JsonKey(ignore: true) + @override + $ImageSplitterErrorCopyWith get copyWith => + _$ImageSplitterErrorCopyWithImpl(this, _$identity); + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() generating, + required TResult Function( + Image image, List images, PaletteGenerator palette) + complete, + required TResult Function(String? message) error, + }) { + return error(message); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? generating, + TResult Function(Image image, List images, PaletteGenerator palette)? + complete, + TResult Function(String? message)? error, + }) { + return error?.call(message); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? generating, + TResult Function(Image image, List images, PaletteGenerator palette)? + complete, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(message); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(ImageSplitterIdle value) $default, { + required TResult Function(ImageSplitterGenerating value) generating, + required TResult Function(ImageSplitterComplete value) complete, + required TResult Function(ImageSplitterError value) error, + }) { + return error(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(ImageSplitterIdle value)? $default, { + TResult Function(ImageSplitterGenerating value)? generating, + TResult Function(ImageSplitterComplete value)? complete, + TResult Function(ImageSplitterError value)? error, + }) { + return error?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(ImageSplitterIdle value)? $default, { + TResult Function(ImageSplitterGenerating value)? generating, + TResult Function(ImageSplitterComplete value)? complete, + TResult Function(ImageSplitterError value)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(this); + } + return orElse(); + } +} + +abstract class ImageSplitterError implements ImageSplitterState { + const factory ImageSplitterError({String? message}) = _$ImageSplitterError; + + String? get message; + @JsonKey(ignore: true) + $ImageSplitterErrorCopyWith get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/application/states/multi_puzzle_state.dart b/lib/application/states/multi_puzzle_state.dart new file mode 100644 index 0000000..900c01a --- /dev/null +++ b/lib/application/states/multi_puzzle_state.dart @@ -0,0 +1,13 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:my_flutter_puzzle/models/puzzle_data.dart'; + +part 'multi_puzzle_state.freezed.dart'; + +@freezed +class MultiPuzzleState with _$MultiPuzzleState { + const factory MultiPuzzleState() = MultiPuzzleIdle; + const factory MultiPuzzleState.initializing() = MultiPuzzleInitializing; + const factory MultiPuzzleState.current(PuzzleData puzzleData) = MultiPuzzleCurrent; + const factory MultiPuzzleState.solved(PuzzleData puzzleData) = MultiPuzzleSolved; + const factory MultiPuzzleState.error({String? message}) = MultiPuzzleError; +} \ No newline at end of file diff --git a/lib/application/states/multi_puzzle_state.freezed.dart b/lib/application/states/multi_puzzle_state.freezed.dart new file mode 100644 index 0000000..f3fea70 --- /dev/null +++ b/lib/application/states/multi_puzzle_state.freezed.dart @@ -0,0 +1,831 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target + +part of 'multi_puzzle_state.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +/// @nodoc +class _$MultiPuzzleStateTearOff { + const _$MultiPuzzleStateTearOff(); + + MultiPuzzleIdle call() { + return const MultiPuzzleIdle(); + } + + MultiPuzzleInitializing initializing() { + return const MultiPuzzleInitializing(); + } + + MultiPuzzleCurrent current(PuzzleData puzzleData) { + return MultiPuzzleCurrent( + puzzleData, + ); + } + + MultiPuzzleSolved solved(PuzzleData puzzleData) { + return MultiPuzzleSolved( + puzzleData, + ); + } + + MultiPuzzleError error({String? message}) { + return MultiPuzzleError( + message: message, + ); + } +} + +/// @nodoc +const $MultiPuzzleState = _$MultiPuzzleStateTearOff(); + +/// @nodoc +mixin _$MultiPuzzleState { + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() initializing, + required TResult Function(PuzzleData puzzleData) current, + required TResult Function(PuzzleData puzzleData) solved, + required TResult Function(String? message) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map( + TResult Function(MultiPuzzleIdle value) $default, { + required TResult Function(MultiPuzzleInitializing value) initializing, + required TResult Function(MultiPuzzleCurrent value) current, + required TResult Function(MultiPuzzleSolved value) solved, + required TResult Function(MultiPuzzleError value) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(MultiPuzzleIdle value)? $default, { + TResult Function(MultiPuzzleInitializing value)? initializing, + TResult Function(MultiPuzzleCurrent value)? current, + TResult Function(MultiPuzzleSolved value)? solved, + TResult Function(MultiPuzzleError value)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap( + TResult Function(MultiPuzzleIdle value)? $default, { + TResult Function(MultiPuzzleInitializing value)? initializing, + TResult Function(MultiPuzzleCurrent value)? current, + TResult Function(MultiPuzzleSolved value)? solved, + TResult Function(MultiPuzzleError value)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $MultiPuzzleStateCopyWith<$Res> { + factory $MultiPuzzleStateCopyWith( + MultiPuzzleState value, $Res Function(MultiPuzzleState) then) = + _$MultiPuzzleStateCopyWithImpl<$Res>; +} + +/// @nodoc +class _$MultiPuzzleStateCopyWithImpl<$Res> + implements $MultiPuzzleStateCopyWith<$Res> { + _$MultiPuzzleStateCopyWithImpl(this._value, this._then); + + final MultiPuzzleState _value; + // ignore: unused_field + final $Res Function(MultiPuzzleState) _then; +} + +/// @nodoc +abstract class $MultiPuzzleIdleCopyWith<$Res> { + factory $MultiPuzzleIdleCopyWith( + MultiPuzzleIdle value, $Res Function(MultiPuzzleIdle) then) = + _$MultiPuzzleIdleCopyWithImpl<$Res>; +} + +/// @nodoc +class _$MultiPuzzleIdleCopyWithImpl<$Res> + extends _$MultiPuzzleStateCopyWithImpl<$Res> + implements $MultiPuzzleIdleCopyWith<$Res> { + _$MultiPuzzleIdleCopyWithImpl( + MultiPuzzleIdle _value, $Res Function(MultiPuzzleIdle) _then) + : super(_value, (v) => _then(v as MultiPuzzleIdle)); + + @override + MultiPuzzleIdle get _value => super._value as MultiPuzzleIdle; +} + +/// @nodoc + +class _$MultiPuzzleIdle implements MultiPuzzleIdle { + const _$MultiPuzzleIdle(); + + @override + String toString() { + return 'MultiPuzzleState()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is MultiPuzzleIdle); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() initializing, + required TResult Function(PuzzleData puzzleData) current, + required TResult Function(PuzzleData puzzleData) solved, + required TResult Function(String? message) error, + }) { + return $default(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + }) { + return $default?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if ($default != null) { + return $default(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(MultiPuzzleIdle value) $default, { + required TResult Function(MultiPuzzleInitializing value) initializing, + required TResult Function(MultiPuzzleCurrent value) current, + required TResult Function(MultiPuzzleSolved value) solved, + required TResult Function(MultiPuzzleError value) error, + }) { + return $default(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(MultiPuzzleIdle value)? $default, { + TResult Function(MultiPuzzleInitializing value)? initializing, + TResult Function(MultiPuzzleCurrent value)? current, + TResult Function(MultiPuzzleSolved value)? solved, + TResult Function(MultiPuzzleError value)? error, + }) { + return $default?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(MultiPuzzleIdle value)? $default, { + TResult Function(MultiPuzzleInitializing value)? initializing, + TResult Function(MultiPuzzleCurrent value)? current, + TResult Function(MultiPuzzleSolved value)? solved, + TResult Function(MultiPuzzleError value)? error, + required TResult orElse(), + }) { + if ($default != null) { + return $default(this); + } + return orElse(); + } +} + +abstract class MultiPuzzleIdle implements MultiPuzzleState { + const factory MultiPuzzleIdle() = _$MultiPuzzleIdle; +} + +/// @nodoc +abstract class $MultiPuzzleInitializingCopyWith<$Res> { + factory $MultiPuzzleInitializingCopyWith(MultiPuzzleInitializing value, + $Res Function(MultiPuzzleInitializing) then) = + _$MultiPuzzleInitializingCopyWithImpl<$Res>; +} + +/// @nodoc +class _$MultiPuzzleInitializingCopyWithImpl<$Res> + extends _$MultiPuzzleStateCopyWithImpl<$Res> + implements $MultiPuzzleInitializingCopyWith<$Res> { + _$MultiPuzzleInitializingCopyWithImpl(MultiPuzzleInitializing _value, + $Res Function(MultiPuzzleInitializing) _then) + : super(_value, (v) => _then(v as MultiPuzzleInitializing)); + + @override + MultiPuzzleInitializing get _value => super._value as MultiPuzzleInitializing; +} + +/// @nodoc + +class _$MultiPuzzleInitializing implements MultiPuzzleInitializing { + const _$MultiPuzzleInitializing(); + + @override + String toString() { + return 'MultiPuzzleState.initializing()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is MultiPuzzleInitializing); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() initializing, + required TResult Function(PuzzleData puzzleData) current, + required TResult Function(PuzzleData puzzleData) solved, + required TResult Function(String? message) error, + }) { + return initializing(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + }) { + return initializing?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (initializing != null) { + return initializing(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(MultiPuzzleIdle value) $default, { + required TResult Function(MultiPuzzleInitializing value) initializing, + required TResult Function(MultiPuzzleCurrent value) current, + required TResult Function(MultiPuzzleSolved value) solved, + required TResult Function(MultiPuzzleError value) error, + }) { + return initializing(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(MultiPuzzleIdle value)? $default, { + TResult Function(MultiPuzzleInitializing value)? initializing, + TResult Function(MultiPuzzleCurrent value)? current, + TResult Function(MultiPuzzleSolved value)? solved, + TResult Function(MultiPuzzleError value)? error, + }) { + return initializing?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(MultiPuzzleIdle value)? $default, { + TResult Function(MultiPuzzleInitializing value)? initializing, + TResult Function(MultiPuzzleCurrent value)? current, + TResult Function(MultiPuzzleSolved value)? solved, + TResult Function(MultiPuzzleError value)? error, + required TResult orElse(), + }) { + if (initializing != null) { + return initializing(this); + } + return orElse(); + } +} + +abstract class MultiPuzzleInitializing implements MultiPuzzleState { + const factory MultiPuzzleInitializing() = _$MultiPuzzleInitializing; +} + +/// @nodoc +abstract class $MultiPuzzleCurrentCopyWith<$Res> { + factory $MultiPuzzleCurrentCopyWith( + MultiPuzzleCurrent value, $Res Function(MultiPuzzleCurrent) then) = + _$MultiPuzzleCurrentCopyWithImpl<$Res>; + $Res call({PuzzleData puzzleData}); +} + +/// @nodoc +class _$MultiPuzzleCurrentCopyWithImpl<$Res> + extends _$MultiPuzzleStateCopyWithImpl<$Res> + implements $MultiPuzzleCurrentCopyWith<$Res> { + _$MultiPuzzleCurrentCopyWithImpl( + MultiPuzzleCurrent _value, $Res Function(MultiPuzzleCurrent) _then) + : super(_value, (v) => _then(v as MultiPuzzleCurrent)); + + @override + MultiPuzzleCurrent get _value => super._value as MultiPuzzleCurrent; + + @override + $Res call({ + Object? puzzleData = freezed, + }) { + return _then(MultiPuzzleCurrent( + puzzleData == freezed + ? _value.puzzleData + : puzzleData // ignore: cast_nullable_to_non_nullable + as PuzzleData, + )); + } +} + +/// @nodoc + +class _$MultiPuzzleCurrent implements MultiPuzzleCurrent { + const _$MultiPuzzleCurrent(this.puzzleData); + + @override + final PuzzleData puzzleData; + + @override + String toString() { + return 'MultiPuzzleState.current(puzzleData: $puzzleData)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is MultiPuzzleCurrent && + const DeepCollectionEquality() + .equals(other.puzzleData, puzzleData)); + } + + @override + int get hashCode => + Object.hash(runtimeType, const DeepCollectionEquality().hash(puzzleData)); + + @JsonKey(ignore: true) + @override + $MultiPuzzleCurrentCopyWith get copyWith => + _$MultiPuzzleCurrentCopyWithImpl(this, _$identity); + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() initializing, + required TResult Function(PuzzleData puzzleData) current, + required TResult Function(PuzzleData puzzleData) solved, + required TResult Function(String? message) error, + }) { + return current(puzzleData); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + }) { + return current?.call(puzzleData); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (current != null) { + return current(puzzleData); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(MultiPuzzleIdle value) $default, { + required TResult Function(MultiPuzzleInitializing value) initializing, + required TResult Function(MultiPuzzleCurrent value) current, + required TResult Function(MultiPuzzleSolved value) solved, + required TResult Function(MultiPuzzleError value) error, + }) { + return current(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(MultiPuzzleIdle value)? $default, { + TResult Function(MultiPuzzleInitializing value)? initializing, + TResult Function(MultiPuzzleCurrent value)? current, + TResult Function(MultiPuzzleSolved value)? solved, + TResult Function(MultiPuzzleError value)? error, + }) { + return current?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(MultiPuzzleIdle value)? $default, { + TResult Function(MultiPuzzleInitializing value)? initializing, + TResult Function(MultiPuzzleCurrent value)? current, + TResult Function(MultiPuzzleSolved value)? solved, + TResult Function(MultiPuzzleError value)? error, + required TResult orElse(), + }) { + if (current != null) { + return current(this); + } + return orElse(); + } +} + +abstract class MultiPuzzleCurrent implements MultiPuzzleState { + const factory MultiPuzzleCurrent(PuzzleData puzzleData) = + _$MultiPuzzleCurrent; + + PuzzleData get puzzleData; + @JsonKey(ignore: true) + $MultiPuzzleCurrentCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $MultiPuzzleSolvedCopyWith<$Res> { + factory $MultiPuzzleSolvedCopyWith( + MultiPuzzleSolved value, $Res Function(MultiPuzzleSolved) then) = + _$MultiPuzzleSolvedCopyWithImpl<$Res>; + $Res call({PuzzleData puzzleData}); +} + +/// @nodoc +class _$MultiPuzzleSolvedCopyWithImpl<$Res> + extends _$MultiPuzzleStateCopyWithImpl<$Res> + implements $MultiPuzzleSolvedCopyWith<$Res> { + _$MultiPuzzleSolvedCopyWithImpl( + MultiPuzzleSolved _value, $Res Function(MultiPuzzleSolved) _then) + : super(_value, (v) => _then(v as MultiPuzzleSolved)); + + @override + MultiPuzzleSolved get _value => super._value as MultiPuzzleSolved; + + @override + $Res call({ + Object? puzzleData = freezed, + }) { + return _then(MultiPuzzleSolved( + puzzleData == freezed + ? _value.puzzleData + : puzzleData // ignore: cast_nullable_to_non_nullable + as PuzzleData, + )); + } +} + +/// @nodoc + +class _$MultiPuzzleSolved implements MultiPuzzleSolved { + const _$MultiPuzzleSolved(this.puzzleData); + + @override + final PuzzleData puzzleData; + + @override + String toString() { + return 'MultiPuzzleState.solved(puzzleData: $puzzleData)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is MultiPuzzleSolved && + const DeepCollectionEquality() + .equals(other.puzzleData, puzzleData)); + } + + @override + int get hashCode => + Object.hash(runtimeType, const DeepCollectionEquality().hash(puzzleData)); + + @JsonKey(ignore: true) + @override + $MultiPuzzleSolvedCopyWith get copyWith => + _$MultiPuzzleSolvedCopyWithImpl(this, _$identity); + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() initializing, + required TResult Function(PuzzleData puzzleData) current, + required TResult Function(PuzzleData puzzleData) solved, + required TResult Function(String? message) error, + }) { + return solved(puzzleData); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + }) { + return solved?.call(puzzleData); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (solved != null) { + return solved(puzzleData); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(MultiPuzzleIdle value) $default, { + required TResult Function(MultiPuzzleInitializing value) initializing, + required TResult Function(MultiPuzzleCurrent value) current, + required TResult Function(MultiPuzzleSolved value) solved, + required TResult Function(MultiPuzzleError value) error, + }) { + return solved(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(MultiPuzzleIdle value)? $default, { + TResult Function(MultiPuzzleInitializing value)? initializing, + TResult Function(MultiPuzzleCurrent value)? current, + TResult Function(MultiPuzzleSolved value)? solved, + TResult Function(MultiPuzzleError value)? error, + }) { + return solved?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(MultiPuzzleIdle value)? $default, { + TResult Function(MultiPuzzleInitializing value)? initializing, + TResult Function(MultiPuzzleCurrent value)? current, + TResult Function(MultiPuzzleSolved value)? solved, + TResult Function(MultiPuzzleError value)? error, + required TResult orElse(), + }) { + if (solved != null) { + return solved(this); + } + return orElse(); + } +} + +abstract class MultiPuzzleSolved implements MultiPuzzleState { + const factory MultiPuzzleSolved(PuzzleData puzzleData) = _$MultiPuzzleSolved; + + PuzzleData get puzzleData; + @JsonKey(ignore: true) + $MultiPuzzleSolvedCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $MultiPuzzleErrorCopyWith<$Res> { + factory $MultiPuzzleErrorCopyWith( + MultiPuzzleError value, $Res Function(MultiPuzzleError) then) = + _$MultiPuzzleErrorCopyWithImpl<$Res>; + $Res call({String? message}); +} + +/// @nodoc +class _$MultiPuzzleErrorCopyWithImpl<$Res> + extends _$MultiPuzzleStateCopyWithImpl<$Res> + implements $MultiPuzzleErrorCopyWith<$Res> { + _$MultiPuzzleErrorCopyWithImpl( + MultiPuzzleError _value, $Res Function(MultiPuzzleError) _then) + : super(_value, (v) => _then(v as MultiPuzzleError)); + + @override + MultiPuzzleError get _value => super._value as MultiPuzzleError; + + @override + $Res call({ + Object? message = freezed, + }) { + return _then(MultiPuzzleError( + message: message == freezed + ? _value.message + : message // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc + +class _$MultiPuzzleError implements MultiPuzzleError { + const _$MultiPuzzleError({this.message}); + + @override + final String? message; + + @override + String toString() { + return 'MultiPuzzleState.error(message: $message)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is MultiPuzzleError && + const DeepCollectionEquality().equals(other.message, message)); + } + + @override + int get hashCode => + Object.hash(runtimeType, const DeepCollectionEquality().hash(message)); + + @JsonKey(ignore: true) + @override + $MultiPuzzleErrorCopyWith get copyWith => + _$MultiPuzzleErrorCopyWithImpl(this, _$identity); + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() initializing, + required TResult Function(PuzzleData puzzleData) current, + required TResult Function(PuzzleData puzzleData) solved, + required TResult Function(String? message) error, + }) { + return error(message); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + }) { + return error?.call(message); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(message); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(MultiPuzzleIdle value) $default, { + required TResult Function(MultiPuzzleInitializing value) initializing, + required TResult Function(MultiPuzzleCurrent value) current, + required TResult Function(MultiPuzzleSolved value) solved, + required TResult Function(MultiPuzzleError value) error, + }) { + return error(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(MultiPuzzleIdle value)? $default, { + TResult Function(MultiPuzzleInitializing value)? initializing, + TResult Function(MultiPuzzleCurrent value)? current, + TResult Function(MultiPuzzleSolved value)? solved, + TResult Function(MultiPuzzleError value)? error, + }) { + return error?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(MultiPuzzleIdle value)? $default, { + TResult Function(MultiPuzzleInitializing value)? initializing, + TResult Function(MultiPuzzleCurrent value)? current, + TResult Function(MultiPuzzleSolved value)? solved, + TResult Function(MultiPuzzleError value)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(this); + } + return orElse(); + } +} + +abstract class MultiPuzzleError implements MultiPuzzleState { + const factory MultiPuzzleError({String? message}) = _$MultiPuzzleError; + + String? get message; + @JsonKey(ignore: true) + $MultiPuzzleErrorCopyWith get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/application/states/player_matching_state.dart b/lib/application/states/player_matching_state.dart new file mode 100644 index 0000000..1e09464 --- /dev/null +++ b/lib/application/states/player_matching_state.dart @@ -0,0 +1,12 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'player_matching_state.freezed.dart'; + +@freezed +class PlayerMatchingState with _$PlayerMatchingState { + const factory PlayerMatchingState() = PlayerMatchingIdle; + const factory PlayerMatchingState.processing() = PlayerMatchingProcessing; + const factory PlayerMatchingState.isMatched(String id) = PlayerMatched; + const factory PlayerMatchingState.isQueued() = PlayerQueue; + const factory PlayerMatchingState.error({String? message}) = PlayerMatchingError; +} diff --git a/lib/application/states/player_matching_state.freezed.dart b/lib/application/states/player_matching_state.freezed.dart new file mode 100644 index 0000000..f0420e7 --- /dev/null +++ b/lib/application/states/player_matching_state.freezed.dart @@ -0,0 +1,798 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target + +part of 'player_matching_state.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +/// @nodoc +class _$PlayerMatchingStateTearOff { + const _$PlayerMatchingStateTearOff(); + + PlayerMatchingIdle call() { + return const PlayerMatchingIdle(); + } + + PlayerMatchingProcessing processing() { + return const PlayerMatchingProcessing(); + } + + PlayerMatched isMatched(String id) { + return PlayerMatched( + id, + ); + } + + PlayerQueue isQueued() { + return const PlayerQueue(); + } + + PlayerMatchingError error({String? message}) { + return PlayerMatchingError( + message: message, + ); + } +} + +/// @nodoc +const $PlayerMatchingState = _$PlayerMatchingStateTearOff(); + +/// @nodoc +mixin _$PlayerMatchingState { + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() processing, + required TResult Function(String id) isMatched, + required TResult Function() isQueued, + required TResult Function(String? message) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(String id)? isMatched, + TResult Function()? isQueued, + TResult Function(String? message)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(String id)? isMatched, + TResult Function()? isQueued, + TResult Function(String? message)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map( + TResult Function(PlayerMatchingIdle value) $default, { + required TResult Function(PlayerMatchingProcessing value) processing, + required TResult Function(PlayerMatched value) isMatched, + required TResult Function(PlayerQueue value) isQueued, + required TResult Function(PlayerMatchingError value) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(PlayerMatchingIdle value)? $default, { + TResult Function(PlayerMatchingProcessing value)? processing, + TResult Function(PlayerMatched value)? isMatched, + TResult Function(PlayerQueue value)? isQueued, + TResult Function(PlayerMatchingError value)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap( + TResult Function(PlayerMatchingIdle value)? $default, { + TResult Function(PlayerMatchingProcessing value)? processing, + TResult Function(PlayerMatched value)? isMatched, + TResult Function(PlayerQueue value)? isQueued, + TResult Function(PlayerMatchingError value)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $PlayerMatchingStateCopyWith<$Res> { + factory $PlayerMatchingStateCopyWith( + PlayerMatchingState value, $Res Function(PlayerMatchingState) then) = + _$PlayerMatchingStateCopyWithImpl<$Res>; +} + +/// @nodoc +class _$PlayerMatchingStateCopyWithImpl<$Res> + implements $PlayerMatchingStateCopyWith<$Res> { + _$PlayerMatchingStateCopyWithImpl(this._value, this._then); + + final PlayerMatchingState _value; + // ignore: unused_field + final $Res Function(PlayerMatchingState) _then; +} + +/// @nodoc +abstract class $PlayerMatchingIdleCopyWith<$Res> { + factory $PlayerMatchingIdleCopyWith( + PlayerMatchingIdle value, $Res Function(PlayerMatchingIdle) then) = + _$PlayerMatchingIdleCopyWithImpl<$Res>; +} + +/// @nodoc +class _$PlayerMatchingIdleCopyWithImpl<$Res> + extends _$PlayerMatchingStateCopyWithImpl<$Res> + implements $PlayerMatchingIdleCopyWith<$Res> { + _$PlayerMatchingIdleCopyWithImpl( + PlayerMatchingIdle _value, $Res Function(PlayerMatchingIdle) _then) + : super(_value, (v) => _then(v as PlayerMatchingIdle)); + + @override + PlayerMatchingIdle get _value => super._value as PlayerMatchingIdle; +} + +/// @nodoc + +class _$PlayerMatchingIdle implements PlayerMatchingIdle { + const _$PlayerMatchingIdle(); + + @override + String toString() { + return 'PlayerMatchingState()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is PlayerMatchingIdle); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() processing, + required TResult Function(String id) isMatched, + required TResult Function() isQueued, + required TResult Function(String? message) error, + }) { + return $default(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(String id)? isMatched, + TResult Function()? isQueued, + TResult Function(String? message)? error, + }) { + return $default?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(String id)? isMatched, + TResult Function()? isQueued, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if ($default != null) { + return $default(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(PlayerMatchingIdle value) $default, { + required TResult Function(PlayerMatchingProcessing value) processing, + required TResult Function(PlayerMatched value) isMatched, + required TResult Function(PlayerQueue value) isQueued, + required TResult Function(PlayerMatchingError value) error, + }) { + return $default(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(PlayerMatchingIdle value)? $default, { + TResult Function(PlayerMatchingProcessing value)? processing, + TResult Function(PlayerMatched value)? isMatched, + TResult Function(PlayerQueue value)? isQueued, + TResult Function(PlayerMatchingError value)? error, + }) { + return $default?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(PlayerMatchingIdle value)? $default, { + TResult Function(PlayerMatchingProcessing value)? processing, + TResult Function(PlayerMatched value)? isMatched, + TResult Function(PlayerQueue value)? isQueued, + TResult Function(PlayerMatchingError value)? error, + required TResult orElse(), + }) { + if ($default != null) { + return $default(this); + } + return orElse(); + } +} + +abstract class PlayerMatchingIdle implements PlayerMatchingState { + const factory PlayerMatchingIdle() = _$PlayerMatchingIdle; +} + +/// @nodoc +abstract class $PlayerMatchingProcessingCopyWith<$Res> { + factory $PlayerMatchingProcessingCopyWith(PlayerMatchingProcessing value, + $Res Function(PlayerMatchingProcessing) then) = + _$PlayerMatchingProcessingCopyWithImpl<$Res>; +} + +/// @nodoc +class _$PlayerMatchingProcessingCopyWithImpl<$Res> + extends _$PlayerMatchingStateCopyWithImpl<$Res> + implements $PlayerMatchingProcessingCopyWith<$Res> { + _$PlayerMatchingProcessingCopyWithImpl(PlayerMatchingProcessing _value, + $Res Function(PlayerMatchingProcessing) _then) + : super(_value, (v) => _then(v as PlayerMatchingProcessing)); + + @override + PlayerMatchingProcessing get _value => + super._value as PlayerMatchingProcessing; +} + +/// @nodoc + +class _$PlayerMatchingProcessing implements PlayerMatchingProcessing { + const _$PlayerMatchingProcessing(); + + @override + String toString() { + return 'PlayerMatchingState.processing()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is PlayerMatchingProcessing); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() processing, + required TResult Function(String id) isMatched, + required TResult Function() isQueued, + required TResult Function(String? message) error, + }) { + return processing(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(String id)? isMatched, + TResult Function()? isQueued, + TResult Function(String? message)? error, + }) { + return processing?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(String id)? isMatched, + TResult Function()? isQueued, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (processing != null) { + return processing(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(PlayerMatchingIdle value) $default, { + required TResult Function(PlayerMatchingProcessing value) processing, + required TResult Function(PlayerMatched value) isMatched, + required TResult Function(PlayerQueue value) isQueued, + required TResult Function(PlayerMatchingError value) error, + }) { + return processing(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(PlayerMatchingIdle value)? $default, { + TResult Function(PlayerMatchingProcessing value)? processing, + TResult Function(PlayerMatched value)? isMatched, + TResult Function(PlayerQueue value)? isQueued, + TResult Function(PlayerMatchingError value)? error, + }) { + return processing?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(PlayerMatchingIdle value)? $default, { + TResult Function(PlayerMatchingProcessing value)? processing, + TResult Function(PlayerMatched value)? isMatched, + TResult Function(PlayerQueue value)? isQueued, + TResult Function(PlayerMatchingError value)? error, + required TResult orElse(), + }) { + if (processing != null) { + return processing(this); + } + return orElse(); + } +} + +abstract class PlayerMatchingProcessing implements PlayerMatchingState { + const factory PlayerMatchingProcessing() = _$PlayerMatchingProcessing; +} + +/// @nodoc +abstract class $PlayerMatchedCopyWith<$Res> { + factory $PlayerMatchedCopyWith( + PlayerMatched value, $Res Function(PlayerMatched) then) = + _$PlayerMatchedCopyWithImpl<$Res>; + $Res call({String id}); +} + +/// @nodoc +class _$PlayerMatchedCopyWithImpl<$Res> + extends _$PlayerMatchingStateCopyWithImpl<$Res> + implements $PlayerMatchedCopyWith<$Res> { + _$PlayerMatchedCopyWithImpl( + PlayerMatched _value, $Res Function(PlayerMatched) _then) + : super(_value, (v) => _then(v as PlayerMatched)); + + @override + PlayerMatched get _value => super._value as PlayerMatched; + + @override + $Res call({ + Object? id = freezed, + }) { + return _then(PlayerMatched( + id == freezed + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc + +class _$PlayerMatched implements PlayerMatched { + const _$PlayerMatched(this.id); + + @override + final String id; + + @override + String toString() { + return 'PlayerMatchingState.isMatched(id: $id)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is PlayerMatched && + const DeepCollectionEquality().equals(other.id, id)); + } + + @override + int get hashCode => + Object.hash(runtimeType, const DeepCollectionEquality().hash(id)); + + @JsonKey(ignore: true) + @override + $PlayerMatchedCopyWith get copyWith => + _$PlayerMatchedCopyWithImpl(this, _$identity); + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() processing, + required TResult Function(String id) isMatched, + required TResult Function() isQueued, + required TResult Function(String? message) error, + }) { + return isMatched(id); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(String id)? isMatched, + TResult Function()? isQueued, + TResult Function(String? message)? error, + }) { + return isMatched?.call(id); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(String id)? isMatched, + TResult Function()? isQueued, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (isMatched != null) { + return isMatched(id); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(PlayerMatchingIdle value) $default, { + required TResult Function(PlayerMatchingProcessing value) processing, + required TResult Function(PlayerMatched value) isMatched, + required TResult Function(PlayerQueue value) isQueued, + required TResult Function(PlayerMatchingError value) error, + }) { + return isMatched(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(PlayerMatchingIdle value)? $default, { + TResult Function(PlayerMatchingProcessing value)? processing, + TResult Function(PlayerMatched value)? isMatched, + TResult Function(PlayerQueue value)? isQueued, + TResult Function(PlayerMatchingError value)? error, + }) { + return isMatched?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(PlayerMatchingIdle value)? $default, { + TResult Function(PlayerMatchingProcessing value)? processing, + TResult Function(PlayerMatched value)? isMatched, + TResult Function(PlayerQueue value)? isQueued, + TResult Function(PlayerMatchingError value)? error, + required TResult orElse(), + }) { + if (isMatched != null) { + return isMatched(this); + } + return orElse(); + } +} + +abstract class PlayerMatched implements PlayerMatchingState { + const factory PlayerMatched(String id) = _$PlayerMatched; + + String get id; + @JsonKey(ignore: true) + $PlayerMatchedCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $PlayerQueueCopyWith<$Res> { + factory $PlayerQueueCopyWith( + PlayerQueue value, $Res Function(PlayerQueue) then) = + _$PlayerQueueCopyWithImpl<$Res>; +} + +/// @nodoc +class _$PlayerQueueCopyWithImpl<$Res> + extends _$PlayerMatchingStateCopyWithImpl<$Res> + implements $PlayerQueueCopyWith<$Res> { + _$PlayerQueueCopyWithImpl( + PlayerQueue _value, $Res Function(PlayerQueue) _then) + : super(_value, (v) => _then(v as PlayerQueue)); + + @override + PlayerQueue get _value => super._value as PlayerQueue; +} + +/// @nodoc + +class _$PlayerQueue implements PlayerQueue { + const _$PlayerQueue(); + + @override + String toString() { + return 'PlayerMatchingState.isQueued()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is PlayerQueue); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() processing, + required TResult Function(String id) isMatched, + required TResult Function() isQueued, + required TResult Function(String? message) error, + }) { + return isQueued(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(String id)? isMatched, + TResult Function()? isQueued, + TResult Function(String? message)? error, + }) { + return isQueued?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(String id)? isMatched, + TResult Function()? isQueued, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (isQueued != null) { + return isQueued(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(PlayerMatchingIdle value) $default, { + required TResult Function(PlayerMatchingProcessing value) processing, + required TResult Function(PlayerMatched value) isMatched, + required TResult Function(PlayerQueue value) isQueued, + required TResult Function(PlayerMatchingError value) error, + }) { + return isQueued(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(PlayerMatchingIdle value)? $default, { + TResult Function(PlayerMatchingProcessing value)? processing, + TResult Function(PlayerMatched value)? isMatched, + TResult Function(PlayerQueue value)? isQueued, + TResult Function(PlayerMatchingError value)? error, + }) { + return isQueued?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(PlayerMatchingIdle value)? $default, { + TResult Function(PlayerMatchingProcessing value)? processing, + TResult Function(PlayerMatched value)? isMatched, + TResult Function(PlayerQueue value)? isQueued, + TResult Function(PlayerMatchingError value)? error, + required TResult orElse(), + }) { + if (isQueued != null) { + return isQueued(this); + } + return orElse(); + } +} + +abstract class PlayerQueue implements PlayerMatchingState { + const factory PlayerQueue() = _$PlayerQueue; +} + +/// @nodoc +abstract class $PlayerMatchingErrorCopyWith<$Res> { + factory $PlayerMatchingErrorCopyWith( + PlayerMatchingError value, $Res Function(PlayerMatchingError) then) = + _$PlayerMatchingErrorCopyWithImpl<$Res>; + $Res call({String? message}); +} + +/// @nodoc +class _$PlayerMatchingErrorCopyWithImpl<$Res> + extends _$PlayerMatchingStateCopyWithImpl<$Res> + implements $PlayerMatchingErrorCopyWith<$Res> { + _$PlayerMatchingErrorCopyWithImpl( + PlayerMatchingError _value, $Res Function(PlayerMatchingError) _then) + : super(_value, (v) => _then(v as PlayerMatchingError)); + + @override + PlayerMatchingError get _value => super._value as PlayerMatchingError; + + @override + $Res call({ + Object? message = freezed, + }) { + return _then(PlayerMatchingError( + message: message == freezed + ? _value.message + : message // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc + +class _$PlayerMatchingError implements PlayerMatchingError { + const _$PlayerMatchingError({this.message}); + + @override + final String? message; + + @override + String toString() { + return 'PlayerMatchingState.error(message: $message)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is PlayerMatchingError && + const DeepCollectionEquality().equals(other.message, message)); + } + + @override + int get hashCode => + Object.hash(runtimeType, const DeepCollectionEquality().hash(message)); + + @JsonKey(ignore: true) + @override + $PlayerMatchingErrorCopyWith get copyWith => + _$PlayerMatchingErrorCopyWithImpl(this, _$identity); + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() processing, + required TResult Function(String id) isMatched, + required TResult Function() isQueued, + required TResult Function(String? message) error, + }) { + return error(message); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(String id)? isMatched, + TResult Function()? isQueued, + TResult Function(String? message)? error, + }) { + return error?.call(message); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? processing, + TResult Function(String id)? isMatched, + TResult Function()? isQueued, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(message); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(PlayerMatchingIdle value) $default, { + required TResult Function(PlayerMatchingProcessing value) processing, + required TResult Function(PlayerMatched value) isMatched, + required TResult Function(PlayerQueue value) isQueued, + required TResult Function(PlayerMatchingError value) error, + }) { + return error(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(PlayerMatchingIdle value)? $default, { + TResult Function(PlayerMatchingProcessing value)? processing, + TResult Function(PlayerMatched value)? isMatched, + TResult Function(PlayerQueue value)? isQueued, + TResult Function(PlayerMatchingError value)? error, + }) { + return error?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(PlayerMatchingIdle value)? $default, { + TResult Function(PlayerMatchingProcessing value)? processing, + TResult Function(PlayerMatched value)? isMatched, + TResult Function(PlayerQueue value)? isQueued, + TResult Function(PlayerMatchingError value)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(this); + } + return orElse(); + } +} + +abstract class PlayerMatchingError implements PlayerMatchingState { + const factory PlayerMatchingError({String? message}) = _$PlayerMatchingError; + + String? get message; + @JsonKey(ignore: true) + $PlayerMatchingErrorCopyWith get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/application/states/puzzle_state.dart b/lib/application/states/puzzle_state.dart new file mode 100644 index 0000000..590b2b5 --- /dev/null +++ b/lib/application/states/puzzle_state.dart @@ -0,0 +1,16 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:my_flutter_puzzle/models/puzzle_data.dart'; + +part 'puzzle_state.freezed.dart'; + +@freezed +class PuzzleState with _$PuzzleState { + const factory PuzzleState() = PuzzleIdle; + const factory PuzzleState.initializing() = PuzzleInitializing; + const factory PuzzleState.scrambling(PuzzleData puzzleData) = PuzzleScrambling; + const factory PuzzleState.current(PuzzleData puzzleData) = PuzzleCurrent; + const factory PuzzleState.computingSolution(PuzzleData puzzleData) = PuzzleComputingSolution; + const factory PuzzleState.autoSolving(PuzzleData puzzleData) = PuzzleAutoSolving; + const factory PuzzleState.solved(PuzzleData puzzleData) = PuzzleSolved; + const factory PuzzleState.error({String? message}) = PuzzleError; +} \ No newline at end of file diff --git a/lib/application/states/puzzle_state.freezed.dart b/lib/application/states/puzzle_state.freezed.dart new file mode 100644 index 0000000..add9b6e --- /dev/null +++ b/lib/application/states/puzzle_state.freezed.dart @@ -0,0 +1,1465 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target + +part of 'puzzle_state.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +/// @nodoc +class _$PuzzleStateTearOff { + const _$PuzzleStateTearOff(); + + PuzzleIdle call() { + return const PuzzleIdle(); + } + + PuzzleInitializing initializing() { + return const PuzzleInitializing(); + } + + PuzzleScrambling scrambling(PuzzleData puzzleData) { + return PuzzleScrambling( + puzzleData, + ); + } + + PuzzleCurrent current(PuzzleData puzzleData) { + return PuzzleCurrent( + puzzleData, + ); + } + + PuzzleComputingSolution computingSolution(PuzzleData puzzleData) { + return PuzzleComputingSolution( + puzzleData, + ); + } + + PuzzleAutoSolving autoSolving(PuzzleData puzzleData) { + return PuzzleAutoSolving( + puzzleData, + ); + } + + PuzzleSolved solved(PuzzleData puzzleData) { + return PuzzleSolved( + puzzleData, + ); + } + + PuzzleError error({String? message}) { + return PuzzleError( + message: message, + ); + } +} + +/// @nodoc +const $PuzzleState = _$PuzzleStateTearOff(); + +/// @nodoc +mixin _$PuzzleState { + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() initializing, + required TResult Function(PuzzleData puzzleData) scrambling, + required TResult Function(PuzzleData puzzleData) current, + required TResult Function(PuzzleData puzzleData) computingSolution, + required TResult Function(PuzzleData puzzleData) autoSolving, + required TResult Function(PuzzleData puzzleData) solved, + required TResult Function(String? message) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? scrambling, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? computingSolution, + TResult Function(PuzzleData puzzleData)? autoSolving, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? scrambling, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? computingSolution, + TResult Function(PuzzleData puzzleData)? autoSolving, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map( + TResult Function(PuzzleIdle value) $default, { + required TResult Function(PuzzleInitializing value) initializing, + required TResult Function(PuzzleScrambling value) scrambling, + required TResult Function(PuzzleCurrent value) current, + required TResult Function(PuzzleComputingSolution value) computingSolution, + required TResult Function(PuzzleAutoSolving value) autoSolving, + required TResult Function(PuzzleSolved value) solved, + required TResult Function(PuzzleError value) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(PuzzleIdle value)? $default, { + TResult Function(PuzzleInitializing value)? initializing, + TResult Function(PuzzleScrambling value)? scrambling, + TResult Function(PuzzleCurrent value)? current, + TResult Function(PuzzleComputingSolution value)? computingSolution, + TResult Function(PuzzleAutoSolving value)? autoSolving, + TResult Function(PuzzleSolved value)? solved, + TResult Function(PuzzleError value)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap( + TResult Function(PuzzleIdle value)? $default, { + TResult Function(PuzzleInitializing value)? initializing, + TResult Function(PuzzleScrambling value)? scrambling, + TResult Function(PuzzleCurrent value)? current, + TResult Function(PuzzleComputingSolution value)? computingSolution, + TResult Function(PuzzleAutoSolving value)? autoSolving, + TResult Function(PuzzleSolved value)? solved, + TResult Function(PuzzleError value)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $PuzzleStateCopyWith<$Res> { + factory $PuzzleStateCopyWith( + PuzzleState value, $Res Function(PuzzleState) then) = + _$PuzzleStateCopyWithImpl<$Res>; +} + +/// @nodoc +class _$PuzzleStateCopyWithImpl<$Res> implements $PuzzleStateCopyWith<$Res> { + _$PuzzleStateCopyWithImpl(this._value, this._then); + + final PuzzleState _value; + // ignore: unused_field + final $Res Function(PuzzleState) _then; +} + +/// @nodoc +abstract class $PuzzleIdleCopyWith<$Res> { + factory $PuzzleIdleCopyWith( + PuzzleIdle value, $Res Function(PuzzleIdle) then) = + _$PuzzleIdleCopyWithImpl<$Res>; +} + +/// @nodoc +class _$PuzzleIdleCopyWithImpl<$Res> extends _$PuzzleStateCopyWithImpl<$Res> + implements $PuzzleIdleCopyWith<$Res> { + _$PuzzleIdleCopyWithImpl(PuzzleIdle _value, $Res Function(PuzzleIdle) _then) + : super(_value, (v) => _then(v as PuzzleIdle)); + + @override + PuzzleIdle get _value => super._value as PuzzleIdle; +} + +/// @nodoc + +class _$PuzzleIdle implements PuzzleIdle { + const _$PuzzleIdle(); + + @override + String toString() { + return 'PuzzleState()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is PuzzleIdle); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() initializing, + required TResult Function(PuzzleData puzzleData) scrambling, + required TResult Function(PuzzleData puzzleData) current, + required TResult Function(PuzzleData puzzleData) computingSolution, + required TResult Function(PuzzleData puzzleData) autoSolving, + required TResult Function(PuzzleData puzzleData) solved, + required TResult Function(String? message) error, + }) { + return $default(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? scrambling, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? computingSolution, + TResult Function(PuzzleData puzzleData)? autoSolving, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + }) { + return $default?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? scrambling, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? computingSolution, + TResult Function(PuzzleData puzzleData)? autoSolving, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if ($default != null) { + return $default(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(PuzzleIdle value) $default, { + required TResult Function(PuzzleInitializing value) initializing, + required TResult Function(PuzzleScrambling value) scrambling, + required TResult Function(PuzzleCurrent value) current, + required TResult Function(PuzzleComputingSolution value) computingSolution, + required TResult Function(PuzzleAutoSolving value) autoSolving, + required TResult Function(PuzzleSolved value) solved, + required TResult Function(PuzzleError value) error, + }) { + return $default(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(PuzzleIdle value)? $default, { + TResult Function(PuzzleInitializing value)? initializing, + TResult Function(PuzzleScrambling value)? scrambling, + TResult Function(PuzzleCurrent value)? current, + TResult Function(PuzzleComputingSolution value)? computingSolution, + TResult Function(PuzzleAutoSolving value)? autoSolving, + TResult Function(PuzzleSolved value)? solved, + TResult Function(PuzzleError value)? error, + }) { + return $default?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(PuzzleIdle value)? $default, { + TResult Function(PuzzleInitializing value)? initializing, + TResult Function(PuzzleScrambling value)? scrambling, + TResult Function(PuzzleCurrent value)? current, + TResult Function(PuzzleComputingSolution value)? computingSolution, + TResult Function(PuzzleAutoSolving value)? autoSolving, + TResult Function(PuzzleSolved value)? solved, + TResult Function(PuzzleError value)? error, + required TResult orElse(), + }) { + if ($default != null) { + return $default(this); + } + return orElse(); + } +} + +abstract class PuzzleIdle implements PuzzleState { + const factory PuzzleIdle() = _$PuzzleIdle; +} + +/// @nodoc +abstract class $PuzzleInitializingCopyWith<$Res> { + factory $PuzzleInitializingCopyWith( + PuzzleInitializing value, $Res Function(PuzzleInitializing) then) = + _$PuzzleInitializingCopyWithImpl<$Res>; +} + +/// @nodoc +class _$PuzzleInitializingCopyWithImpl<$Res> + extends _$PuzzleStateCopyWithImpl<$Res> + implements $PuzzleInitializingCopyWith<$Res> { + _$PuzzleInitializingCopyWithImpl( + PuzzleInitializing _value, $Res Function(PuzzleInitializing) _then) + : super(_value, (v) => _then(v as PuzzleInitializing)); + + @override + PuzzleInitializing get _value => super._value as PuzzleInitializing; +} + +/// @nodoc + +class _$PuzzleInitializing implements PuzzleInitializing { + const _$PuzzleInitializing(); + + @override + String toString() { + return 'PuzzleState.initializing()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is PuzzleInitializing); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() initializing, + required TResult Function(PuzzleData puzzleData) scrambling, + required TResult Function(PuzzleData puzzleData) current, + required TResult Function(PuzzleData puzzleData) computingSolution, + required TResult Function(PuzzleData puzzleData) autoSolving, + required TResult Function(PuzzleData puzzleData) solved, + required TResult Function(String? message) error, + }) { + return initializing(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? scrambling, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? computingSolution, + TResult Function(PuzzleData puzzleData)? autoSolving, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + }) { + return initializing?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? scrambling, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? computingSolution, + TResult Function(PuzzleData puzzleData)? autoSolving, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (initializing != null) { + return initializing(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(PuzzleIdle value) $default, { + required TResult Function(PuzzleInitializing value) initializing, + required TResult Function(PuzzleScrambling value) scrambling, + required TResult Function(PuzzleCurrent value) current, + required TResult Function(PuzzleComputingSolution value) computingSolution, + required TResult Function(PuzzleAutoSolving value) autoSolving, + required TResult Function(PuzzleSolved value) solved, + required TResult Function(PuzzleError value) error, + }) { + return initializing(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(PuzzleIdle value)? $default, { + TResult Function(PuzzleInitializing value)? initializing, + TResult Function(PuzzleScrambling value)? scrambling, + TResult Function(PuzzleCurrent value)? current, + TResult Function(PuzzleComputingSolution value)? computingSolution, + TResult Function(PuzzleAutoSolving value)? autoSolving, + TResult Function(PuzzleSolved value)? solved, + TResult Function(PuzzleError value)? error, + }) { + return initializing?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(PuzzleIdle value)? $default, { + TResult Function(PuzzleInitializing value)? initializing, + TResult Function(PuzzleScrambling value)? scrambling, + TResult Function(PuzzleCurrent value)? current, + TResult Function(PuzzleComputingSolution value)? computingSolution, + TResult Function(PuzzleAutoSolving value)? autoSolving, + TResult Function(PuzzleSolved value)? solved, + TResult Function(PuzzleError value)? error, + required TResult orElse(), + }) { + if (initializing != null) { + return initializing(this); + } + return orElse(); + } +} + +abstract class PuzzleInitializing implements PuzzleState { + const factory PuzzleInitializing() = _$PuzzleInitializing; +} + +/// @nodoc +abstract class $PuzzleScramblingCopyWith<$Res> { + factory $PuzzleScramblingCopyWith( + PuzzleScrambling value, $Res Function(PuzzleScrambling) then) = + _$PuzzleScramblingCopyWithImpl<$Res>; + $Res call({PuzzleData puzzleData}); +} + +/// @nodoc +class _$PuzzleScramblingCopyWithImpl<$Res> + extends _$PuzzleStateCopyWithImpl<$Res> + implements $PuzzleScramblingCopyWith<$Res> { + _$PuzzleScramblingCopyWithImpl( + PuzzleScrambling _value, $Res Function(PuzzleScrambling) _then) + : super(_value, (v) => _then(v as PuzzleScrambling)); + + @override + PuzzleScrambling get _value => super._value as PuzzleScrambling; + + @override + $Res call({ + Object? puzzleData = freezed, + }) { + return _then(PuzzleScrambling( + puzzleData == freezed + ? _value.puzzleData + : puzzleData // ignore: cast_nullable_to_non_nullable + as PuzzleData, + )); + } +} + +/// @nodoc + +class _$PuzzleScrambling implements PuzzleScrambling { + const _$PuzzleScrambling(this.puzzleData); + + @override + final PuzzleData puzzleData; + + @override + String toString() { + return 'PuzzleState.scrambling(puzzleData: $puzzleData)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is PuzzleScrambling && + const DeepCollectionEquality() + .equals(other.puzzleData, puzzleData)); + } + + @override + int get hashCode => + Object.hash(runtimeType, const DeepCollectionEquality().hash(puzzleData)); + + @JsonKey(ignore: true) + @override + $PuzzleScramblingCopyWith get copyWith => + _$PuzzleScramblingCopyWithImpl(this, _$identity); + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() initializing, + required TResult Function(PuzzleData puzzleData) scrambling, + required TResult Function(PuzzleData puzzleData) current, + required TResult Function(PuzzleData puzzleData) computingSolution, + required TResult Function(PuzzleData puzzleData) autoSolving, + required TResult Function(PuzzleData puzzleData) solved, + required TResult Function(String? message) error, + }) { + return scrambling(puzzleData); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? scrambling, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? computingSolution, + TResult Function(PuzzleData puzzleData)? autoSolving, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + }) { + return scrambling?.call(puzzleData); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? scrambling, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? computingSolution, + TResult Function(PuzzleData puzzleData)? autoSolving, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (scrambling != null) { + return scrambling(puzzleData); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(PuzzleIdle value) $default, { + required TResult Function(PuzzleInitializing value) initializing, + required TResult Function(PuzzleScrambling value) scrambling, + required TResult Function(PuzzleCurrent value) current, + required TResult Function(PuzzleComputingSolution value) computingSolution, + required TResult Function(PuzzleAutoSolving value) autoSolving, + required TResult Function(PuzzleSolved value) solved, + required TResult Function(PuzzleError value) error, + }) { + return scrambling(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(PuzzleIdle value)? $default, { + TResult Function(PuzzleInitializing value)? initializing, + TResult Function(PuzzleScrambling value)? scrambling, + TResult Function(PuzzleCurrent value)? current, + TResult Function(PuzzleComputingSolution value)? computingSolution, + TResult Function(PuzzleAutoSolving value)? autoSolving, + TResult Function(PuzzleSolved value)? solved, + TResult Function(PuzzleError value)? error, + }) { + return scrambling?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(PuzzleIdle value)? $default, { + TResult Function(PuzzleInitializing value)? initializing, + TResult Function(PuzzleScrambling value)? scrambling, + TResult Function(PuzzleCurrent value)? current, + TResult Function(PuzzleComputingSolution value)? computingSolution, + TResult Function(PuzzleAutoSolving value)? autoSolving, + TResult Function(PuzzleSolved value)? solved, + TResult Function(PuzzleError value)? error, + required TResult orElse(), + }) { + if (scrambling != null) { + return scrambling(this); + } + return orElse(); + } +} + +abstract class PuzzleScrambling implements PuzzleState { + const factory PuzzleScrambling(PuzzleData puzzleData) = _$PuzzleScrambling; + + PuzzleData get puzzleData; + @JsonKey(ignore: true) + $PuzzleScramblingCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $PuzzleCurrentCopyWith<$Res> { + factory $PuzzleCurrentCopyWith( + PuzzleCurrent value, $Res Function(PuzzleCurrent) then) = + _$PuzzleCurrentCopyWithImpl<$Res>; + $Res call({PuzzleData puzzleData}); +} + +/// @nodoc +class _$PuzzleCurrentCopyWithImpl<$Res> extends _$PuzzleStateCopyWithImpl<$Res> + implements $PuzzleCurrentCopyWith<$Res> { + _$PuzzleCurrentCopyWithImpl( + PuzzleCurrent _value, $Res Function(PuzzleCurrent) _then) + : super(_value, (v) => _then(v as PuzzleCurrent)); + + @override + PuzzleCurrent get _value => super._value as PuzzleCurrent; + + @override + $Res call({ + Object? puzzleData = freezed, + }) { + return _then(PuzzleCurrent( + puzzleData == freezed + ? _value.puzzleData + : puzzleData // ignore: cast_nullable_to_non_nullable + as PuzzleData, + )); + } +} + +/// @nodoc + +class _$PuzzleCurrent implements PuzzleCurrent { + const _$PuzzleCurrent(this.puzzleData); + + @override + final PuzzleData puzzleData; + + @override + String toString() { + return 'PuzzleState.current(puzzleData: $puzzleData)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is PuzzleCurrent && + const DeepCollectionEquality() + .equals(other.puzzleData, puzzleData)); + } + + @override + int get hashCode => + Object.hash(runtimeType, const DeepCollectionEquality().hash(puzzleData)); + + @JsonKey(ignore: true) + @override + $PuzzleCurrentCopyWith get copyWith => + _$PuzzleCurrentCopyWithImpl(this, _$identity); + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() initializing, + required TResult Function(PuzzleData puzzleData) scrambling, + required TResult Function(PuzzleData puzzleData) current, + required TResult Function(PuzzleData puzzleData) computingSolution, + required TResult Function(PuzzleData puzzleData) autoSolving, + required TResult Function(PuzzleData puzzleData) solved, + required TResult Function(String? message) error, + }) { + return current(puzzleData); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? scrambling, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? computingSolution, + TResult Function(PuzzleData puzzleData)? autoSolving, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + }) { + return current?.call(puzzleData); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? scrambling, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? computingSolution, + TResult Function(PuzzleData puzzleData)? autoSolving, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (current != null) { + return current(puzzleData); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(PuzzleIdle value) $default, { + required TResult Function(PuzzleInitializing value) initializing, + required TResult Function(PuzzleScrambling value) scrambling, + required TResult Function(PuzzleCurrent value) current, + required TResult Function(PuzzleComputingSolution value) computingSolution, + required TResult Function(PuzzleAutoSolving value) autoSolving, + required TResult Function(PuzzleSolved value) solved, + required TResult Function(PuzzleError value) error, + }) { + return current(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(PuzzleIdle value)? $default, { + TResult Function(PuzzleInitializing value)? initializing, + TResult Function(PuzzleScrambling value)? scrambling, + TResult Function(PuzzleCurrent value)? current, + TResult Function(PuzzleComputingSolution value)? computingSolution, + TResult Function(PuzzleAutoSolving value)? autoSolving, + TResult Function(PuzzleSolved value)? solved, + TResult Function(PuzzleError value)? error, + }) { + return current?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(PuzzleIdle value)? $default, { + TResult Function(PuzzleInitializing value)? initializing, + TResult Function(PuzzleScrambling value)? scrambling, + TResult Function(PuzzleCurrent value)? current, + TResult Function(PuzzleComputingSolution value)? computingSolution, + TResult Function(PuzzleAutoSolving value)? autoSolving, + TResult Function(PuzzleSolved value)? solved, + TResult Function(PuzzleError value)? error, + required TResult orElse(), + }) { + if (current != null) { + return current(this); + } + return orElse(); + } +} + +abstract class PuzzleCurrent implements PuzzleState { + const factory PuzzleCurrent(PuzzleData puzzleData) = _$PuzzleCurrent; + + PuzzleData get puzzleData; + @JsonKey(ignore: true) + $PuzzleCurrentCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $PuzzleComputingSolutionCopyWith<$Res> { + factory $PuzzleComputingSolutionCopyWith(PuzzleComputingSolution value, + $Res Function(PuzzleComputingSolution) then) = + _$PuzzleComputingSolutionCopyWithImpl<$Res>; + $Res call({PuzzleData puzzleData}); +} + +/// @nodoc +class _$PuzzleComputingSolutionCopyWithImpl<$Res> + extends _$PuzzleStateCopyWithImpl<$Res> + implements $PuzzleComputingSolutionCopyWith<$Res> { + _$PuzzleComputingSolutionCopyWithImpl(PuzzleComputingSolution _value, + $Res Function(PuzzleComputingSolution) _then) + : super(_value, (v) => _then(v as PuzzleComputingSolution)); + + @override + PuzzleComputingSolution get _value => super._value as PuzzleComputingSolution; + + @override + $Res call({ + Object? puzzleData = freezed, + }) { + return _then(PuzzleComputingSolution( + puzzleData == freezed + ? _value.puzzleData + : puzzleData // ignore: cast_nullable_to_non_nullable + as PuzzleData, + )); + } +} + +/// @nodoc + +class _$PuzzleComputingSolution implements PuzzleComputingSolution { + const _$PuzzleComputingSolution(this.puzzleData); + + @override + final PuzzleData puzzleData; + + @override + String toString() { + return 'PuzzleState.computingSolution(puzzleData: $puzzleData)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is PuzzleComputingSolution && + const DeepCollectionEquality() + .equals(other.puzzleData, puzzleData)); + } + + @override + int get hashCode => + Object.hash(runtimeType, const DeepCollectionEquality().hash(puzzleData)); + + @JsonKey(ignore: true) + @override + $PuzzleComputingSolutionCopyWith get copyWith => + _$PuzzleComputingSolutionCopyWithImpl( + this, _$identity); + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() initializing, + required TResult Function(PuzzleData puzzleData) scrambling, + required TResult Function(PuzzleData puzzleData) current, + required TResult Function(PuzzleData puzzleData) computingSolution, + required TResult Function(PuzzleData puzzleData) autoSolving, + required TResult Function(PuzzleData puzzleData) solved, + required TResult Function(String? message) error, + }) { + return computingSolution(puzzleData); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? scrambling, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? computingSolution, + TResult Function(PuzzleData puzzleData)? autoSolving, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + }) { + return computingSolution?.call(puzzleData); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? scrambling, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? computingSolution, + TResult Function(PuzzleData puzzleData)? autoSolving, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (computingSolution != null) { + return computingSolution(puzzleData); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(PuzzleIdle value) $default, { + required TResult Function(PuzzleInitializing value) initializing, + required TResult Function(PuzzleScrambling value) scrambling, + required TResult Function(PuzzleCurrent value) current, + required TResult Function(PuzzleComputingSolution value) computingSolution, + required TResult Function(PuzzleAutoSolving value) autoSolving, + required TResult Function(PuzzleSolved value) solved, + required TResult Function(PuzzleError value) error, + }) { + return computingSolution(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(PuzzleIdle value)? $default, { + TResult Function(PuzzleInitializing value)? initializing, + TResult Function(PuzzleScrambling value)? scrambling, + TResult Function(PuzzleCurrent value)? current, + TResult Function(PuzzleComputingSolution value)? computingSolution, + TResult Function(PuzzleAutoSolving value)? autoSolving, + TResult Function(PuzzleSolved value)? solved, + TResult Function(PuzzleError value)? error, + }) { + return computingSolution?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(PuzzleIdle value)? $default, { + TResult Function(PuzzleInitializing value)? initializing, + TResult Function(PuzzleScrambling value)? scrambling, + TResult Function(PuzzleCurrent value)? current, + TResult Function(PuzzleComputingSolution value)? computingSolution, + TResult Function(PuzzleAutoSolving value)? autoSolving, + TResult Function(PuzzleSolved value)? solved, + TResult Function(PuzzleError value)? error, + required TResult orElse(), + }) { + if (computingSolution != null) { + return computingSolution(this); + } + return orElse(); + } +} + +abstract class PuzzleComputingSolution implements PuzzleState { + const factory PuzzleComputingSolution(PuzzleData puzzleData) = + _$PuzzleComputingSolution; + + PuzzleData get puzzleData; + @JsonKey(ignore: true) + $PuzzleComputingSolutionCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $PuzzleAutoSolvingCopyWith<$Res> { + factory $PuzzleAutoSolvingCopyWith( + PuzzleAutoSolving value, $Res Function(PuzzleAutoSolving) then) = + _$PuzzleAutoSolvingCopyWithImpl<$Res>; + $Res call({PuzzleData puzzleData}); +} + +/// @nodoc +class _$PuzzleAutoSolvingCopyWithImpl<$Res> + extends _$PuzzleStateCopyWithImpl<$Res> + implements $PuzzleAutoSolvingCopyWith<$Res> { + _$PuzzleAutoSolvingCopyWithImpl( + PuzzleAutoSolving _value, $Res Function(PuzzleAutoSolving) _then) + : super(_value, (v) => _then(v as PuzzleAutoSolving)); + + @override + PuzzleAutoSolving get _value => super._value as PuzzleAutoSolving; + + @override + $Res call({ + Object? puzzleData = freezed, + }) { + return _then(PuzzleAutoSolving( + puzzleData == freezed + ? _value.puzzleData + : puzzleData // ignore: cast_nullable_to_non_nullable + as PuzzleData, + )); + } +} + +/// @nodoc + +class _$PuzzleAutoSolving implements PuzzleAutoSolving { + const _$PuzzleAutoSolving(this.puzzleData); + + @override + final PuzzleData puzzleData; + + @override + String toString() { + return 'PuzzleState.autoSolving(puzzleData: $puzzleData)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is PuzzleAutoSolving && + const DeepCollectionEquality() + .equals(other.puzzleData, puzzleData)); + } + + @override + int get hashCode => + Object.hash(runtimeType, const DeepCollectionEquality().hash(puzzleData)); + + @JsonKey(ignore: true) + @override + $PuzzleAutoSolvingCopyWith get copyWith => + _$PuzzleAutoSolvingCopyWithImpl(this, _$identity); + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() initializing, + required TResult Function(PuzzleData puzzleData) scrambling, + required TResult Function(PuzzleData puzzleData) current, + required TResult Function(PuzzleData puzzleData) computingSolution, + required TResult Function(PuzzleData puzzleData) autoSolving, + required TResult Function(PuzzleData puzzleData) solved, + required TResult Function(String? message) error, + }) { + return autoSolving(puzzleData); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? scrambling, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? computingSolution, + TResult Function(PuzzleData puzzleData)? autoSolving, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + }) { + return autoSolving?.call(puzzleData); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? scrambling, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? computingSolution, + TResult Function(PuzzleData puzzleData)? autoSolving, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (autoSolving != null) { + return autoSolving(puzzleData); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(PuzzleIdle value) $default, { + required TResult Function(PuzzleInitializing value) initializing, + required TResult Function(PuzzleScrambling value) scrambling, + required TResult Function(PuzzleCurrent value) current, + required TResult Function(PuzzleComputingSolution value) computingSolution, + required TResult Function(PuzzleAutoSolving value) autoSolving, + required TResult Function(PuzzleSolved value) solved, + required TResult Function(PuzzleError value) error, + }) { + return autoSolving(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(PuzzleIdle value)? $default, { + TResult Function(PuzzleInitializing value)? initializing, + TResult Function(PuzzleScrambling value)? scrambling, + TResult Function(PuzzleCurrent value)? current, + TResult Function(PuzzleComputingSolution value)? computingSolution, + TResult Function(PuzzleAutoSolving value)? autoSolving, + TResult Function(PuzzleSolved value)? solved, + TResult Function(PuzzleError value)? error, + }) { + return autoSolving?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(PuzzleIdle value)? $default, { + TResult Function(PuzzleInitializing value)? initializing, + TResult Function(PuzzleScrambling value)? scrambling, + TResult Function(PuzzleCurrent value)? current, + TResult Function(PuzzleComputingSolution value)? computingSolution, + TResult Function(PuzzleAutoSolving value)? autoSolving, + TResult Function(PuzzleSolved value)? solved, + TResult Function(PuzzleError value)? error, + required TResult orElse(), + }) { + if (autoSolving != null) { + return autoSolving(this); + } + return orElse(); + } +} + +abstract class PuzzleAutoSolving implements PuzzleState { + const factory PuzzleAutoSolving(PuzzleData puzzleData) = _$PuzzleAutoSolving; + + PuzzleData get puzzleData; + @JsonKey(ignore: true) + $PuzzleAutoSolvingCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $PuzzleSolvedCopyWith<$Res> { + factory $PuzzleSolvedCopyWith( + PuzzleSolved value, $Res Function(PuzzleSolved) then) = + _$PuzzleSolvedCopyWithImpl<$Res>; + $Res call({PuzzleData puzzleData}); +} + +/// @nodoc +class _$PuzzleSolvedCopyWithImpl<$Res> extends _$PuzzleStateCopyWithImpl<$Res> + implements $PuzzleSolvedCopyWith<$Res> { + _$PuzzleSolvedCopyWithImpl( + PuzzleSolved _value, $Res Function(PuzzleSolved) _then) + : super(_value, (v) => _then(v as PuzzleSolved)); + + @override + PuzzleSolved get _value => super._value as PuzzleSolved; + + @override + $Res call({ + Object? puzzleData = freezed, + }) { + return _then(PuzzleSolved( + puzzleData == freezed + ? _value.puzzleData + : puzzleData // ignore: cast_nullable_to_non_nullable + as PuzzleData, + )); + } +} + +/// @nodoc + +class _$PuzzleSolved implements PuzzleSolved { + const _$PuzzleSolved(this.puzzleData); + + @override + final PuzzleData puzzleData; + + @override + String toString() { + return 'PuzzleState.solved(puzzleData: $puzzleData)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is PuzzleSolved && + const DeepCollectionEquality() + .equals(other.puzzleData, puzzleData)); + } + + @override + int get hashCode => + Object.hash(runtimeType, const DeepCollectionEquality().hash(puzzleData)); + + @JsonKey(ignore: true) + @override + $PuzzleSolvedCopyWith get copyWith => + _$PuzzleSolvedCopyWithImpl(this, _$identity); + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() initializing, + required TResult Function(PuzzleData puzzleData) scrambling, + required TResult Function(PuzzleData puzzleData) current, + required TResult Function(PuzzleData puzzleData) computingSolution, + required TResult Function(PuzzleData puzzleData) autoSolving, + required TResult Function(PuzzleData puzzleData) solved, + required TResult Function(String? message) error, + }) { + return solved(puzzleData); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? scrambling, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? computingSolution, + TResult Function(PuzzleData puzzleData)? autoSolving, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + }) { + return solved?.call(puzzleData); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? scrambling, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? computingSolution, + TResult Function(PuzzleData puzzleData)? autoSolving, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (solved != null) { + return solved(puzzleData); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(PuzzleIdle value) $default, { + required TResult Function(PuzzleInitializing value) initializing, + required TResult Function(PuzzleScrambling value) scrambling, + required TResult Function(PuzzleCurrent value) current, + required TResult Function(PuzzleComputingSolution value) computingSolution, + required TResult Function(PuzzleAutoSolving value) autoSolving, + required TResult Function(PuzzleSolved value) solved, + required TResult Function(PuzzleError value) error, + }) { + return solved(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(PuzzleIdle value)? $default, { + TResult Function(PuzzleInitializing value)? initializing, + TResult Function(PuzzleScrambling value)? scrambling, + TResult Function(PuzzleCurrent value)? current, + TResult Function(PuzzleComputingSolution value)? computingSolution, + TResult Function(PuzzleAutoSolving value)? autoSolving, + TResult Function(PuzzleSolved value)? solved, + TResult Function(PuzzleError value)? error, + }) { + return solved?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(PuzzleIdle value)? $default, { + TResult Function(PuzzleInitializing value)? initializing, + TResult Function(PuzzleScrambling value)? scrambling, + TResult Function(PuzzleCurrent value)? current, + TResult Function(PuzzleComputingSolution value)? computingSolution, + TResult Function(PuzzleAutoSolving value)? autoSolving, + TResult Function(PuzzleSolved value)? solved, + TResult Function(PuzzleError value)? error, + required TResult orElse(), + }) { + if (solved != null) { + return solved(this); + } + return orElse(); + } +} + +abstract class PuzzleSolved implements PuzzleState { + const factory PuzzleSolved(PuzzleData puzzleData) = _$PuzzleSolved; + + PuzzleData get puzzleData; + @JsonKey(ignore: true) + $PuzzleSolvedCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $PuzzleErrorCopyWith<$Res> { + factory $PuzzleErrorCopyWith( + PuzzleError value, $Res Function(PuzzleError) then) = + _$PuzzleErrorCopyWithImpl<$Res>; + $Res call({String? message}); +} + +/// @nodoc +class _$PuzzleErrorCopyWithImpl<$Res> extends _$PuzzleStateCopyWithImpl<$Res> + implements $PuzzleErrorCopyWith<$Res> { + _$PuzzleErrorCopyWithImpl( + PuzzleError _value, $Res Function(PuzzleError) _then) + : super(_value, (v) => _then(v as PuzzleError)); + + @override + PuzzleError get _value => super._value as PuzzleError; + + @override + $Res call({ + Object? message = freezed, + }) { + return _then(PuzzleError( + message: message == freezed + ? _value.message + : message // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc + +class _$PuzzleError implements PuzzleError { + const _$PuzzleError({this.message}); + + @override + final String? message; + + @override + String toString() { + return 'PuzzleState.error(message: $message)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is PuzzleError && + const DeepCollectionEquality().equals(other.message, message)); + } + + @override + int get hashCode => + Object.hash(runtimeType, const DeepCollectionEquality().hash(message)); + + @JsonKey(ignore: true) + @override + $PuzzleErrorCopyWith get copyWith => + _$PuzzleErrorCopyWithImpl(this, _$identity); + + @override + @optionalTypeArgs + TResult when( + TResult Function() $default, { + required TResult Function() initializing, + required TResult Function(PuzzleData puzzleData) scrambling, + required TResult Function(PuzzleData puzzleData) current, + required TResult Function(PuzzleData puzzleData) computingSolution, + required TResult Function(PuzzleData puzzleData) autoSolving, + required TResult Function(PuzzleData puzzleData) solved, + required TResult Function(String? message) error, + }) { + return error(message); + } + + @override + @optionalTypeArgs + TResult? whenOrNull( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? scrambling, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? computingSolution, + TResult Function(PuzzleData puzzleData)? autoSolving, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + }) { + return error?.call(message); + } + + @override + @optionalTypeArgs + TResult maybeWhen( + TResult Function()? $default, { + TResult Function()? initializing, + TResult Function(PuzzleData puzzleData)? scrambling, + TResult Function(PuzzleData puzzleData)? current, + TResult Function(PuzzleData puzzleData)? computingSolution, + TResult Function(PuzzleData puzzleData)? autoSolving, + TResult Function(PuzzleData puzzleData)? solved, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(message); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map( + TResult Function(PuzzleIdle value) $default, { + required TResult Function(PuzzleInitializing value) initializing, + required TResult Function(PuzzleScrambling value) scrambling, + required TResult Function(PuzzleCurrent value) current, + required TResult Function(PuzzleComputingSolution value) computingSolution, + required TResult Function(PuzzleAutoSolving value) autoSolving, + required TResult Function(PuzzleSolved value) solved, + required TResult Function(PuzzleError value) error, + }) { + return error(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull( + TResult Function(PuzzleIdle value)? $default, { + TResult Function(PuzzleInitializing value)? initializing, + TResult Function(PuzzleScrambling value)? scrambling, + TResult Function(PuzzleCurrent value)? current, + TResult Function(PuzzleComputingSolution value)? computingSolution, + TResult Function(PuzzleAutoSolving value)? autoSolving, + TResult Function(PuzzleSolved value)? solved, + TResult Function(PuzzleError value)? error, + }) { + return error?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap( + TResult Function(PuzzleIdle value)? $default, { + TResult Function(PuzzleInitializing value)? initializing, + TResult Function(PuzzleScrambling value)? scrambling, + TResult Function(PuzzleCurrent value)? current, + TResult Function(PuzzleComputingSolution value)? computingSolution, + TResult Function(PuzzleAutoSolving value)? autoSolving, + TResult Function(PuzzleSolved value)? solved, + TResult Function(PuzzleError value)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(this); + } + return orElse(); + } +} + +abstract class PuzzleError implements PuzzleState { + const factory PuzzleError({String? message}) = _$PuzzleError; + + String? get message; + @JsonKey(ignore: true) + $PuzzleErrorCopyWith get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..d030d4f --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:my_flutter_puzzle/res/palette.dart'; +import 'package:my_flutter_puzzle/screens/level_list/screen/level_list_screen.dart'; +import 'package:my_flutter_puzzle/utils/color_brightness.dart'; +import 'package:url_strategy/url_strategy.dart'; + +void main() async { + setPathUrlStrategy(); + + WidgetsFlutterBinding.ensureInitialized(); + + runApp(const ProviderScope(child: MyApp())); +} + +class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Puzzle', + debugShowCheckedModeBanner: false, + theme: ThemeData( + primarySwatch: Colors.blue, + fontFamily: 'GoogleSans', + backgroundColor: Palette.blue.darken(0.3), + colorScheme: ColorScheme( + brightness: Theme.of(context).brightness, + primary: Palette.blue, + onPrimary: Colors.white, + secondary: Palette.blue.withOpacity(0.6), + onSecondary: Palette.blue.withOpacity(0.3), + error: Theme.of(context).colorScheme.error, + onError: Theme.of(context).colorScheme.onError, + background: Palette.blue.darken(0.3), + onBackground: Colors.white, + surface: Palette.crimson, + onSurface: Colors.white38, + ), + ), + home: const LevelListScreen(), + ); + } +} diff --git a/lib/models/puzzle_data.dart b/lib/models/puzzle_data.dart new file mode 100644 index 0000000..07296f1 --- /dev/null +++ b/lib/models/puzzle_data.dart @@ -0,0 +1,43 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; + +class PuzzleData { + final List> board2D; + final List board1D; + final int moves; + final int tiles; + final int puzzleSize; + final Map offsetMap; + + PuzzleData({ + required this.board2D, + required this.board1D, + required this.offsetMap, + required this.moves, + required this.tiles, + required this.puzzleSize, + }); + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is PuzzleData && + listEquals(other.board2D, board2D) && + listEquals(other.board1D, board1D) && + other.moves == moves && + other.tiles == tiles && + other.puzzleSize == puzzleSize && + mapEquals(other.offsetMap, offsetMap); + } + + @override + int get hashCode { + return board2D.hashCode ^ + board1D.hashCode ^ + moves.hashCode ^ + tiles.hashCode ^ + puzzleSize.hashCode ^ + offsetMap.hashCode; + } +} diff --git a/lib/models/user_info.dart b/lib/models/user_info.dart new file mode 100644 index 0000000..bc919cc --- /dev/null +++ b/lib/models/user_info.dart @@ -0,0 +1,53 @@ +class UserData { + final String uid; + final String name; + final String username; + final int timestamp; + + UserData({ + required this.uid, + required this.name, + required this.username, + required this.timestamp, + }); + + UserData.fromJson(Map json) + : uid = json['uid'], + name = json['name'], + username = json['username'], + timestamp = json['timestamp']; + + Map toJson() => { + 'uid': uid, + 'name': name, + 'username': username, + 'timestamp': timestamp, + }; +} + +class EUserData { + final String uid; + final String username; + final String email; + final int timestamp; + + EUserData({ + required this.uid, + required this.username, + required this.email, + required this.timestamp, + }); + + EUserData.fromJson(Map json) + : uid = json['uid'], + username = json['username'], + email = json['email'], + timestamp = json['timestamp']; + + Map toJson() => { + 'uid': uid, + 'username': username, + 'email': email, + 'timestamp': timestamp, + }; +} diff --git a/lib/providers.dart b/lib/providers.dart new file mode 100644 index 0000000..45d4cfa --- /dev/null +++ b/lib/providers.dart @@ -0,0 +1,35 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:my_flutter_puzzle/application/notifiers/image_splitter_notifier.dart'; +import 'package:my_flutter_puzzle/application/notifiers/login_register_notifier.dart'; +import 'package:my_flutter_puzzle/application/notifiers/puzzle_notifier.dart'; +import 'package:my_flutter_puzzle/application/notifiers/puzzle_type_notifier.dart'; +import 'package:my_flutter_puzzle/application/states/image_splitter_state.dart'; +import 'package:my_flutter_puzzle/application/states/puzzle_state.dart'; +import 'package:my_flutter_puzzle/utils/image_splitter.dart'; +import 'package:my_flutter_puzzle/utils/puzzle_solver.dart'; + +import 'application/notifiers/timer_notifier.dart'; + +final imageSplitterProvider = Provider((ref) => ImageSplitter()); + +final puzzleNotifierProvider = StateNotifierProvider.family( + (ref, solverClient) => PuzzleNotifier(solverClient), +); + +final imageSplitterNotifierProvider = StateNotifierProvider( + (ref) => ImageSplitterNotifier( + ref.watch(imageSplitterProvider), + ), +); + +final timerNotifierProvider = StateNotifierProvider( + ((ref) => TimerNotifier()), +); + +final puzzleTypeNotifierProvider = StateNotifierProvider( + (ref) => PuzzleTypeNotifier(), +); + +final isLoginNotifier = StateNotifierProvider( + (ref) => IsLoginNotifier(), +); diff --git a/lib/res/palette.dart b/lib/res/palette.dart new file mode 100644 index 0000000..264bb04 --- /dev/null +++ b/lib/res/palette.dart @@ -0,0 +1,7 @@ +import 'package:flutter/material.dart'; + +class Palette { + static const Color blue = Color(0xFF2868d7); + static const Color violet = Color(0xFF5E239D); + static const Color crimson = Color(0xffD33F49); +} diff --git a/lib/res/puzzle_constants.dart b/lib/res/puzzle_constants.dart new file mode 100644 index 0000000..c0617d8 --- /dev/null +++ b/lib/res/puzzle_constants.dart @@ -0,0 +1 @@ +const int kInitialSpeed = 1000; diff --git a/lib/res/strings.dart b/lib/res/strings.dart new file mode 100644 index 0000000..0aa5d4a --- /dev/null +++ b/lib/res/strings.dart @@ -0,0 +1,19 @@ +class Strings { + static const usersCollectionName = 'users'; + static const queueCollectionName = 'queue'; + static const matchedCollectionName = 'matched'; + static const idFieldName = 'id'; + static const myuidFieldName = 'myuid'; + static const otheruidFieldName = 'otheruid'; + static const mylistFieldName = 'mylist'; + static const otherlistFieldName = 'otherlist'; + static const mymovesFieldName = 'mymoves'; + static const othermovesFieldName = 'othermoves'; +} + +const defaultImagePath = 'assets/images/default_image.png'; + +/// username can be made like this +/// @ +/// eg: souvik@124432523 + diff --git a/lib/screens/level_list/screen/level_list_screen.dart b/lib/screens/level_list/screen/level_list_screen.dart new file mode 100644 index 0000000..35b61c5 --- /dev/null +++ b/lib/screens/level_list/screen/level_list_screen.dart @@ -0,0 +1,79 @@ +import 'package:flutter/material.dart'; +import 'package:my_flutter_puzzle/screens/puzzle/puzzle_starter_screen.dart'; +import 'package:my_flutter_puzzle/utils/extensions/string_extensions.dart'; + +class LevelListScreen extends StatefulWidget { + const LevelListScreen({Key? key}) : super(key: key); + + @override + State createState() => _LevelListScreenState(); +} + +class _LevelListScreenState extends State { + final List _levelList = []; + + @override + void initState() { + _levelList.add(Level(image: 'default_image'.pngPath, duration: 4, puzzleSize: 3)); + _levelList.add(Level(image: 'default_image'.pngPath, duration: 4, puzzleSize: 3)); + _levelList.add(Level(image: 'default_image'.pngPath, duration: 4, puzzleSize: 3)); + _levelList.add(Level(image: 'default_image'.pngPath, duration: 6, puzzleSize: 4)); + _levelList.add(Level(image: 'default_image'.pngPath, duration: 6, puzzleSize: 4)); + _levelList.add(Level(image: 'default_image'.pngPath, duration: 6, puzzleSize: 4)); + _levelList.add(Level(image: 'default_image'.pngPath, duration: 8, puzzleSize: 5)); + _levelList.add(Level(image: 'default_image'.pngPath, duration: 8, puzzleSize: 5)); + _levelList.add(Level(image: 'default_image'.pngPath, duration: 8, puzzleSize: 5)); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: GridView.builder( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 4), + itemBuilder: (context, index) { + return Container( + margin: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: Colors.orange, + borderRadius: BorderRadius.circular(16), + ), + child: Column( + children: [ + Text('مرحله شماره ${index + 1}'), + const SizedBox(height: 8), + Text("زمان بازی : ${_levelList[index].duration} دقیقه"), + const SizedBox(height: 8), + GestureDetector( + child: const Text('شروغ'), + onTap: () => _startLevel(_levelList[index]), + ), + ], + ), + ); + }, + itemCount: _levelList.length, + ), + ); + } + + void _startLevel(Level level) { + Navigator.push(context, MaterialPageRoute( + builder: (context) { + return PuzzleStarterScreen( + duration: level.duration, + puzzleSize: level.puzzleSize, + image: level.image, + ); + }, + )); + } +} + +class Level { + final String image; + final int puzzleSize; + final int duration; + + Level({required this.image, required this.duration, required this.puzzleSize}); +} diff --git a/lib/screens/login_screen_name.dart b/lib/screens/login_screen_name.dart new file mode 100644 index 0000000..0770714 --- /dev/null +++ b/lib/screens/login_screen_name.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:my_flutter_puzzle/widgets/login_widgets/name_field.dart'; + +class LoginScreenName extends StatefulWidget { + const LoginScreenName({Key? key}) : super(key: key); + + @override + State createState() => _LoginScreenNameState(); +} + +class _LoginScreenNameState extends State { + late final TextEditingController _nameTextController; + late final FocusNode _nameFocusNode; + + String _nameString = ''; + + @override + void initState() { + super.initState(); + _nameTextController = TextEditingController(); + _nameFocusNode = FocusNode(); + } + + @override + Widget build(BuildContext context) { + var screenSize = MediaQuery.of(context).size; + return GestureDetector( + onTap: () => _nameFocusNode.unfocus(), + child: Scaffold( + backgroundColor: Colors.white, + body: Center( + child: SizedBox( + width: screenSize.width * 0.5, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + NameField( + focusNode: _nameFocusNode, + onChange: (value) => setState( + () => _nameString = value, + ), + ), + const SizedBox(height: 24), + // StartGameWidget( + // nameString: _nameString, + // ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/screens/menu_screen.dart b/lib/screens/menu_screen.dart new file mode 100644 index 0000000..5ec4817 --- /dev/null +++ b/lib/screens/menu_screen.dart @@ -0,0 +1,180 @@ +// import 'package:cloud_firestore/cloud_firestore.dart'; +// import 'package:flutter/material.dart'; +// import 'package:flutter_riverpod/flutter_riverpod.dart'; +// import 'package:my_flutter_puzzle/application/states/player_matching_state.dart'; +// import 'package:my_flutter_puzzle/models/user_info.dart'; +// import 'package:my_flutter_puzzle/providers.dart'; +// import 'package:my_flutter_puzzle/res/palette.dart'; +// import 'package:my_flutter_puzzle/utils/database_client.dart'; +// import 'package:my_flutter_puzzle/screens/puzzle_screen.dart'; +// import 'package:my_flutter_puzzle/widgets/menu_widgets/menu_widgets.dart'; + +// class MenuScreen extends ConsumerStatefulWidget { +// const MenuScreen({ +// Key? key, +// required this.userData, +// }) : super(key: key); + +// final UserData userData; + +// @override +// ConsumerState createState() => _MenuScreenState(); +// } + +// class _MenuScreenState extends ConsumerState { +// final numberList = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; +// late final UserData userData; + +// @override +// void initState() { +// super.initState(); +// userData = widget.userData; +// numberList.shuffle(); +// } + +// @override +// Widget build(BuildContext context) { +// var screenSize = MediaQuery.of(context).size; + +// ref.listen(playerMatchingNotifierProvider, (previous, next) { +// if (next is PlayerMatched) { +// Navigator.of(context).push( +// MaterialPageRoute( +// builder: (context) => PuzzleScreen( +// initialList: numberList, +// id: next.id, +// myInfo: userData, +// ), +// ), +// ); +// } +// }); + +// return Scaffold( +// backgroundColor: Colors.white, +// body: Center( +// child: SizedBox( +// width: screenSize.width * 0.5, +// child: Consumer( +// builder: (context, ref, child) { +// final state = ref.watch(playerMatchingNotifierProvider); + +// return state.when( +// () => Column( +// mainAxisAlignment: MainAxisAlignment.center, +// children: [ +// const Text( +// 'Game Mode', +// style: TextStyle( +// fontSize: 30, +// // fontWeight: FontWeight.bold, +// color: Palette.crimson, +// ), +// ), +// const SizedBox(height: 24), +// MultiplayerButton( +// myInfo: userData, +// list: numberList, +// ), +// const SizedBox(height: 16), +// const SoloButton(), +// const SizedBox(height: 30), +// ], +// ), +// processing: () => Row( +// mainAxisAlignment: MainAxisAlignment.center, +// children: const [ +// CircularProgressIndicator( +// valueColor: +// AlwaysStoppedAnimation(Palette.violet), +// ), +// SizedBox(width: 16), +// Text( +// 'Finding player ...', +// style: TextStyle( +// fontSize: 24, +// ), +// ), +// ], +// ), +// isMatched: (id) => Row( +// mainAxisAlignment: MainAxisAlignment.center, +// children: const [ +// Icon( +// Icons.check_circle, +// color: Palette.violet, +// size: 50, +// ), +// SizedBox(width: 16), +// Text( +// 'Found player', +// style: TextStyle( +// fontSize: 24, +// ), +// ), +// ], +// ), +// isQueued: () => PlayerQueuedWidget( +// myInfo: userData, +// ), +// error: (message) => Text( +// message.toString(), +// style: const TextStyle( +// fontSize: 24, +// ), +// ), +// ); +// }, +// ), +// ), +// )); +// } +// } + +// class PlayerQueuedWidget extends ConsumerWidget { +// PlayerQueuedWidget({ +// Key? key, +// required this.myInfo, +// }) : super(key: key); + +// final UserData myInfo; + +// final _databaseClient = DatabaseClient(); + +// @override +// Widget build(BuildContext context, WidgetRef ref) { +// return StreamBuilder( +// stream: _databaseClient.isMatched(myInfo: myInfo), +// builder: (context, snapshot) { +// if (snapshot.hasData && snapshot.data!.data() != null) { +// final queuedUserData = +// snapshot.data!.data() as Map; +// bool isMatched = queuedUserData['ismatched']; + +// if (isMatched) { +// WidgetsBinding.instance?.addPostFrameCallback((_) { +// ref +// .read(playerMatchingNotifierProvider.notifier) +// .foundUser(myInfo: myInfo); +// }); +// } +// } + +// return Row( +// mainAxisAlignment: MainAxisAlignment.center, +// children: const [ +// CircularProgressIndicator( +// valueColor: AlwaysStoppedAnimation(Palette.violet), +// ), +// SizedBox(width: 16), +// Text( +// 'Your are in queue ...', +// style: TextStyle( +// fontSize: 24, +// ), +// ), +// ], +// ); +// }); +// } +// } diff --git a/lib/screens/photo/photo_screen.dart b/lib/screens/photo/photo_screen.dart new file mode 100644 index 0000000..cf98af5 --- /dev/null +++ b/lib/screens/photo/photo_screen.dart @@ -0,0 +1,175 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:my_flutter_puzzle/application/states/image_splitter_state.dart'; +import 'package:my_flutter_puzzle/application/states/puzzle_state.dart'; +import 'package:my_flutter_puzzle/models/puzzle_data.dart'; +import 'package:my_flutter_puzzle/providers.dart'; +import 'package:my_flutter_puzzle/res/palette.dart'; +import 'package:my_flutter_puzzle/screens/photo/photo_screen_large.dart'; +import 'package:my_flutter_puzzle/screens/photo/photo_screen_medium.dart'; +import 'package:my_flutter_puzzle/screens/photo/photo_screen_small.dart'; +import 'package:my_flutter_puzzle/utils/color_brightness.dart'; +import 'package:my_flutter_puzzle/utils/puzzle_solver.dart'; +import 'package:my_flutter_puzzle/utils/responsive_layout.dart'; +import 'package:rive/rive.dart'; + +class PhotoScreen extends ConsumerStatefulWidget { + const PhotoScreen({ + required this.solverClient, + required this.initialPuzzleData, + required this.puzzleSize, + required this.riveController, + Key? key, + }) : super(key: key); + + final PuzzleSolverClient solverClient; + final PuzzleData initialPuzzleData; + final int puzzleSize; + final RiveAnimationController riveController; + + @override + ConsumerState createState() => _PhotoScreenState(); +} + +class _PhotoScreenState extends ConsumerState { + late final PuzzleSolverClient _solverClient; + late final int _puzzleSize; + late final PuzzleData _initialPuzzleData; + late final RiveAnimationController _riveController; + late ThemeData _themeData = Theme.of(context); + + @override + void initState() { + _solverClient = widget.solverClient; + _puzzleSize = widget.puzzleSize; + _initialPuzzleData = widget.initialPuzzleData; + _riveController = widget.riveController; + super.initState(); + } + + @override + Widget build(BuildContext context) { + ref.listen(puzzleNotifierProvider(_solverClient), (previous, PuzzleState next) { + if (next is PuzzleSolved) { + ref.read(timerNotifierProvider.notifier).stopTimer(); + } + }); + + ref.listen(imageSplitterNotifierProvider, (previous, next) { + if (next is ImageSplitterComplete) { + _themeData = Theme.of(context).copyWith( + colorScheme: ColorScheme( + brightness: Theme.of(context).brightness, + primary: next.palette.vibrantColor?.color ?? Palette.blue, + onPrimary: next.palette.lightVibrantColor?.color ?? Colors.white, + secondary: Theme.of(context).colorScheme.secondary, + onSecondary: Theme.of(context).colorScheme.onSecondary, + error: Theme.of(context).colorScheme.error, + onError: Theme.of(context).colorScheme.onError, + background: next.palette.darkMutedColor?.color ?? Palette.blue.darken(0.3), + onBackground: Colors.white, + surface: Theme.of(context).colorScheme.surface, + onSurface: Theme.of(context).colorScheme.onSurface, + ), + ); + + setState(() {}); + } + }); + + return Consumer( + builder: (context, ref, child) { + final state = ref.watch(imageSplitterNotifierProvider); + + return state.maybeWhen( + () => Theme( + data: _themeData, + child: ResponsiveLayout( + largeChild: PhotoScreenLarge( + solverClient: _solverClient, + initialPuzzleData: _initialPuzzleData, + puzzleSize: _puzzleSize, + riveController: _riveController, + ), + mediumChild: PhotoScreenMedium( + solverClient: _solverClient, + initialPuzzleData: _initialPuzzleData, + puzzleSize: _puzzleSize, + riveController: _riveController, + ), + smallChild: PhotoScreenSmall( + solverClient: _solverClient, + initialPuzzleData: _initialPuzzleData, + puzzleSize: _puzzleSize, + riveController: _riveController, + ), + ), + ), + complete: (image, images, palette) { + _themeData = Theme.of(context).copyWith( + colorScheme: ColorScheme( + brightness: Theme.of(context).brightness, + primary: palette.vibrantColor?.color ?? Palette.blue, + onPrimary: palette.lightVibrantColor?.color ?? Colors.white, + secondary: Theme.of(context).colorScheme.secondary, + onSecondary: Theme.of(context).colorScheme.onSecondary, + error: Theme.of(context).colorScheme.error, + onError: Theme.of(context).colorScheme.onError, + background: palette.darkMutedColor?.color ?? Palette.blue.darken(0.3), + onBackground: Colors.white, + surface: Theme.of(context).colorScheme.surface, + onSurface: Theme.of(context).colorScheme.onSurface, + ), + ); + return Theme( + data: _themeData, + child: ResponsiveLayout( + largeChild: PhotoScreenLarge( + solverClient: _solverClient, + initialPuzzleData: _initialPuzzleData, + puzzleSize: _puzzleSize, + riveController: _riveController, + ), + mediumChild: PhotoScreenMedium( + solverClient: _solverClient, + initialPuzzleData: _initialPuzzleData, + puzzleSize: _puzzleSize, + riveController: _riveController, + ), + smallChild: PhotoScreenSmall( + solverClient: _solverClient, + initialPuzzleData: _initialPuzzleData, + puzzleSize: _puzzleSize, + riveController: _riveController, + ), + ), + ); + }, + orElse: () => Theme( + data: _themeData, + child: ResponsiveLayout( + largeChild: PhotoScreenLarge( + solverClient: _solverClient, + initialPuzzleData: _initialPuzzleData, + puzzleSize: _puzzleSize, + riveController: _riveController, + ), + mediumChild: PhotoScreenMedium( + solverClient: _solverClient, + initialPuzzleData: _initialPuzzleData, + puzzleSize: _puzzleSize, + riveController: _riveController, + ), + smallChild: PhotoScreenSmall( + solverClient: _solverClient, + initialPuzzleData: _initialPuzzleData, + puzzleSize: _puzzleSize, + riveController: _riveController, + ), + ), + ), + ); + }, + ); + } +} diff --git a/lib/screens/photo/photo_screen_large.dart b/lib/screens/photo/photo_screen_large.dart new file mode 100644 index 0000000..53099f6 --- /dev/null +++ b/lib/screens/photo/photo_screen_large.dart @@ -0,0 +1,214 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:my_flutter_puzzle/application/states/image_splitter_state.dart'; +import 'package:my_flutter_puzzle/application/states/puzzle_state.dart'; +import 'package:my_flutter_puzzle/models/puzzle_data.dart'; +import 'package:my_flutter_puzzle/providers.dart'; +import 'package:my_flutter_puzzle/res/puzzle_constants.dart'; +import 'package:my_flutter_puzzle/utils/puzzle_solver.dart'; +import 'package:my_flutter_puzzle/widgets/photo_screen/image_viewer.dart'; +import 'package:my_flutter_puzzle/widgets/photo_screen/pick_image_button.dart'; +import 'package:my_flutter_puzzle/widgets/solo_screen/solo_screen_export.dart'; +import 'package:palette_generator/palette_generator.dart'; +import 'package:rive/rive.dart'; + +class PhotoScreenLarge extends ConsumerStatefulWidget { + const PhotoScreenLarge({ + required this.solverClient, + required this.initialPuzzleData, + required this.puzzleSize, + required this.riveController, + Key? key, + }) : super(key: key); + + final PuzzleSolverClient solverClient; + final PuzzleData initialPuzzleData; + final int puzzleSize; + final RiveAnimationController riveController; + + @override + ConsumerState createState() => _SoloScreenLargeState(); +} + +class _SoloScreenLargeState extends ConsumerState { + late final PuzzleSolverClient _solverClient; + late final int _puzzleSize; + late final PuzzleData _initialPuzzleData; + late final RiveAnimationController _riveController; + + bool _isStartPressed = false; + final _imagePicker = ImagePicker(); + + List? _previousImages; + Image? _previousImage; + PaletteGenerator? _previousPalette; + + @override + void initState() { + _solverClient = widget.solverClient; + _puzzleSize = widget.puzzleSize; + _initialPuzzleData = widget.initialPuzzleData; + _riveController = widget.riveController; + super.initState(); + } + + @override + Widget build(BuildContext context) { + ref.listen(puzzleNotifierProvider(_solverClient), (previous, PuzzleState next) { + if (next is PuzzleInitializing) { + setState(() { + _isStartPressed = true; + }); + } + }); + + ref.listen(imageSplitterNotifierProvider, (previous, next) { + if (next is ImageSplitterComplete) { + setState(() { + _previousImages = next.images; + _previousImage = next.image; + _previousPalette = next.palette; + }); + } + }); + + var fontSize = 70.0; + var boardSize = 450.0; + + var spacing = 5; + var eachBoxSize = (boardSize / _puzzleSize) - (spacing * (_puzzleSize - 1)); + + return Scaffold( + backgroundColor: Theme.of(context).colorScheme.background, + body: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.only(left: 56.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 32), + MovesTilesWidget(solverClient: _solverClient), + const SizedBox(height: 32), + GameButtonWidget( + solverClient: _solverClient, + initialPuzzleData: _initialPuzzleData, + ), + ], + ), + ), + SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + const TimerWidget( + fontSize: 40, + ), + const SizedBox(height: 36), + Consumer( + builder: (context, ref, child) { + final state = ref.watch(imageSplitterNotifierProvider); + + return state.maybeWhen( + () => PuzzleWidget( + solverClient: _solverClient, + boardSize: boardSize, + eachBoxSize: eachBoxSize, + initialPuzzleData: _initialPuzzleData, + fontSize: fontSize, + images: _previousImages, + kInitialSpeed: kInitialSpeed, + ), + complete: (image, images, palette) { + _previousImages = images; + _previousImage = image; + _previousPalette = palette; + + return PuzzleWidget( + solverClient: _solverClient, + boardSize: boardSize, + eachBoxSize: eachBoxSize, + initialPuzzleData: _initialPuzzleData, + fontSize: fontSize, + images: images, + kInitialSpeed: kInitialSpeed, + ); + }, + orElse: () => PuzzleWidget( + solverClient: _solverClient, + boardSize: boardSize, + eachBoxSize: eachBoxSize, + initialPuzzleData: _initialPuzzleData, + fontSize: fontSize, + images: _previousImages, + kInitialSpeed: kInitialSpeed, + ), + ); + }, + ), + const SizedBox(height: 30), + ], + ), + ), + Stack( + children: [ + AnimatedDash( + boardSize: boardSize * 0.80, + riveController: _riveController, + onInit: (_) => setState(() {}), + ), + Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + const Spacer(), + Row(), + CountdownWidget( + isStartPressed: _isStartPressed, + onFinish: () { + ref.read(timerNotifierProvider.notifier).startTimer(); + setState(() { + _isStartPressed = false; + }); + }, + initialSpeed: kInitialSpeed, + ), + Visibility( + visible: !_isStartPressed, + child: Column( + children: [ + ImageViewer( + imagePicker: _imagePicker, + puzzleSize: _puzzleSize, + previousImage: _previousImage, + previousPalette: _previousPalette, + imageSize: 200, + ), + PickImageButton( + text: 'Pick Image', + onTap: ref.read(imageSplitterNotifierProvider) is ImageSplitterComplete + ? () => ref.read(imageSplitterNotifierProvider.notifier).generateImages( + picker: _imagePicker, + puzzleSize: _puzzleSize, + ) + : null, + ), + ], + ), + ), + const Spacer(), + const Spacer(), + const Spacer(), + ], + ), + ], + ), + // SizedBox(), + ], + ), + ); + } +} diff --git a/lib/screens/photo/photo_screen_medium.dart b/lib/screens/photo/photo_screen_medium.dart new file mode 100644 index 0000000..dae2d3d --- /dev/null +++ b/lib/screens/photo/photo_screen_medium.dart @@ -0,0 +1,217 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:my_flutter_puzzle/application/states/image_splitter_state.dart'; +import 'package:my_flutter_puzzle/application/states/puzzle_state.dart'; +import 'package:my_flutter_puzzle/models/puzzle_data.dart'; +import 'package:my_flutter_puzzle/providers.dart'; +import 'package:my_flutter_puzzle/res/palette.dart'; +import 'package:my_flutter_puzzle/res/puzzle_constants.dart'; +import 'package:my_flutter_puzzle/utils/color_brightness.dart'; +import 'package:my_flutter_puzzle/utils/puzzle_solver.dart'; +import 'package:my_flutter_puzzle/widgets/photo_screen/image_viewer.dart'; +import 'package:my_flutter_puzzle/widgets/photo_screen/pick_image_button.dart'; +import 'package:my_flutter_puzzle/widgets/solo_screen/countdown_overlay.dart'; +import 'package:my_flutter_puzzle/widgets/solo_screen/solo_screen_export.dart'; +import 'package:palette_generator/palette_generator.dart'; +import 'package:rive/rive.dart'; + +class PhotoScreenMedium extends ConsumerStatefulWidget { + const PhotoScreenMedium({ + required this.solverClient, + required this.initialPuzzleData, + required this.puzzleSize, + required this.riveController, + Key? key, + }) : super(key: key); + + final PuzzleSolverClient solverClient; + final PuzzleData initialPuzzleData; + final int puzzleSize; + final RiveAnimationController riveController; + + @override + ConsumerState createState() => _PhotoScreenMediumState(); +} + +class _PhotoScreenMediumState extends ConsumerState { + late final PuzzleSolverClient _solverClient; + late final int _puzzleSize; + late final PuzzleData _initialPuzzleData; + late final RiveAnimationController _riveController; + bool _isStartPressed = false; + + final _imagePicker = ImagePicker(); + + List? _previousImages; + Image? _previousImage; + PaletteGenerator? _previousPalette; + + @override + void initState() { + _solverClient = widget.solverClient; + _puzzleSize = widget.puzzleSize; + _initialPuzzleData = widget.initialPuzzleData; + _riveController = widget.riveController; + super.initState(); + } + + @override + Widget build(BuildContext context) { + ref.listen(puzzleNotifierProvider(_solverClient), (previous, PuzzleState next) { + if (next is PuzzleSolved) {} + if (next is PuzzleInitializing) { + setState(() { + _isStartPressed = true; + }); + } + }); + + ref.listen(imageSplitterNotifierProvider, (previous, next) { + if (next is ImageSplitterComplete) { + setState(() { + _previousImages = next.images; + _previousImage = next.image; + _previousPalette = next.palette; + }); + } + }); + + var fontSize = 64.0; + var boardSize = 400.0; + + var spacing = 5; + var eachBoxSize = (boardSize / _puzzleSize) - (spacing * (_puzzleSize - 1)); + + return Stack( + children: [ + Scaffold( + backgroundColor: Palette.blue.darken(0.3), + body: Stack( + children: [ + Row( + children: [ + const Spacer(), + AnimatedDash( + boardSize: boardSize / 1.5, + riveController: _riveController, + onInit: (_) => setState(() {}), + ), + ], + ), + SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + // crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row(), + // Text( + // _puzzleType, + // style: const TextStyle( + // fontSize: 18, + // fontWeight: FontWeight.w500, + // color: Colors.white, + // ), + // ), + const SizedBox(height: 8), + const Text( + 'Puzzle Challenge', + style: TextStyle( + fontSize: 36, + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + const SizedBox(height: 16), + MovesTilesWidget(solverClient: _solverClient), + const SizedBox(height: 16), + const TimerWidget(fontSize: 36), + const SizedBox(height: 36), + Consumer( + builder: (context, ref, child) { + final state = ref.watch(imageSplitterNotifierProvider); + return state.maybeWhen( + () => PuzzleWidget( + solverClient: _solverClient, + boardSize: boardSize, + eachBoxSize: eachBoxSize, + initialPuzzleData: _initialPuzzleData, + fontSize: fontSize, + images: _previousImages, + kInitialSpeed: kInitialSpeed, + ), + complete: (image, images, palette) { + _previousImages = images; + _previousImage = image; + _previousPalette = palette; + + return PuzzleWidget( + solverClient: _solverClient, + boardSize: boardSize, + eachBoxSize: eachBoxSize, + initialPuzzleData: _initialPuzzleData, + fontSize: fontSize, + images: images, + kInitialSpeed: kInitialSpeed, + ); + }, + orElse: () => PuzzleWidget( + solverClient: _solverClient, + boardSize: boardSize, + eachBoxSize: eachBoxSize, + initialPuzzleData: _initialPuzzleData, + fontSize: fontSize, + images: _previousImages, + kInitialSpeed: kInitialSpeed, + ), + ); + }, + ), + const SizedBox(height: 36), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + GameButtonWidget( + solverClient: _solverClient, + initialPuzzleData: _initialPuzzleData, + ), + const SizedBox(width: 36), + PickImageButton( + text: 'Pick Image', + onTap: ref.read(imageSplitterNotifierProvider) is ImageSplitterComplete + ? () => ref.read(imageSplitterNotifierProvider.notifier).generateImages( + picker: _imagePicker, + puzzleSize: _puzzleSize, + ) + : null, + ), + ], + ), + const SizedBox(height: 36), + ImageViewer( + imagePicker: _imagePicker, + puzzleSize: _puzzleSize, + previousImage: _previousImage, + previousPalette: _previousPalette, + imageSize: 150, + ), + ], + ), + ), + ], + ), + ), + CountdownOverlay( + isStartPressed: _isStartPressed, + onFinish: () { + ref.read(timerNotifierProvider.notifier).startTimer(); + setState(() { + _isStartPressed = false; + }); + }, + initialSpeed: kInitialSpeed, + ), + ], + ); + } +} diff --git a/lib/screens/photo/photo_screen_small.dart b/lib/screens/photo/photo_screen_small.dart new file mode 100644 index 0000000..f4a0c94 --- /dev/null +++ b/lib/screens/photo/photo_screen_small.dart @@ -0,0 +1,242 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:my_flutter_puzzle/application/states/image_splitter_state.dart'; +import 'package:my_flutter_puzzle/application/states/puzzle_state.dart'; +import 'package:my_flutter_puzzle/models/puzzle_data.dart'; +import 'package:my_flutter_puzzle/providers.dart'; +import 'package:my_flutter_puzzle/res/puzzle_constants.dart'; +import 'package:my_flutter_puzzle/utils/puzzle_solver.dart'; +import 'package:my_flutter_puzzle/widgets/photo_screen/image_viewer.dart'; +import 'package:my_flutter_puzzle/widgets/photo_screen/pick_image_button.dart'; +import 'package:my_flutter_puzzle/widgets/solo_screen/countdown_overlay.dart'; +import 'package:my_flutter_puzzle/widgets/solo_screen/solo_screen_export.dart'; +import 'package:palette_generator/palette_generator.dart'; +import 'package:rive/rive.dart'; + +class PhotoScreenSmall extends ConsumerStatefulWidget { + const PhotoScreenSmall({ + required this.solverClient, + required this.initialPuzzleData, + required this.puzzleSize, + required this.riveController, + Key? key, + }) : super(key: key); + + final PuzzleSolverClient solverClient; + final PuzzleData initialPuzzleData; + final int puzzleSize; + final RiveAnimationController riveController; + + @override + ConsumerState createState() => _SoloScreenLargeState(); +} + +class _SoloScreenLargeState extends ConsumerState { + late final PuzzleSolverClient _solverClient; + late final int _puzzleSize; + late final PuzzleData _initialPuzzleData; + late final RiveAnimationController _riveController; + bool _isStartPressed = false; + + final _imagePicker = ImagePicker(); + + List? _previousImages; + Image? _previousImage; + PaletteGenerator? _previousPalette; + + @override + void initState() { + _solverClient = widget.solverClient; + _puzzleSize = widget.puzzleSize; + _initialPuzzleData = widget.initialPuzzleData; + _riveController = widget.riveController; + super.initState(); + } + + @override + Widget build(BuildContext context) { + ref.listen(puzzleNotifierProvider(_solverClient), (previous, PuzzleState next) { + if (next is PuzzleInitializing) { + setState(() { + _isStartPressed = true; + }); + } + }); + + ref.listen(imageSplitterNotifierProvider, (previous, next) { + if (next is ImageSplitterComplete) { + setState(() { + _previousImages = next.images; + _previousImage = next.image; + _previousPalette = next.palette; + }); + } + }); + + var fontSize = 48.0; + var boardSize = 300.0; + + var spacing = 2; + var eachBoxSize = (boardSize / _puzzleSize) - (spacing * (_puzzleSize - 1)); + + return Stack( + children: [ + Scaffold( + backgroundColor: Theme.of(context).colorScheme.background, + // appBar: PreferredSize( + // child: Container( + // color: Theme.of(context).colorScheme.background, + // ), + // preferredSize: const Size(double.maxFinite, 30), + // ), + body: Stack( + children: [ + // CountdownWidget( + // isStartPressed: _isStartPressed, + // onFinish: () { + // ref.read(timerNotifierProvider.notifier).startTimer(); + // setState(() { + // _isStartPressed = false; + // }); + // }, + // initialSpeed: kInitialSpeed, + // ), + Row( + children: [ + const Spacer(), + AnimatedDash( + boardSize: boardSize / 1.6, + riveController: _riveController, + onInit: (_) => setState(() {}), + padding: const EdgeInsets.only(right: 16.0, bottom: 30), + ), + ], + ), + SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + // crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row(), + // Text( + // _puzzleType, + // style: const TextStyle( + // fontSize: 18, + // fontWeight: FontWeight.w500, + // color: Colors.white, + // ), + // ), + const SizedBox(height: 30), + const Text( + 'Puzzle Challenge', + style: TextStyle( + fontSize: 32, + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + const SizedBox(height: 8), + MovesTilesWidget( + solverClient: _solverClient, + fontSize: 22, + ), + const SizedBox(height: 8), + const TimerWidget(fontSize: 24), + Consumer( + builder: (context, ref, child) { + final state = ref.watch(imageSplitterNotifierProvider); + + return state.maybeWhen( + () => PuzzleWidget( + solverClient: _solverClient, + boardSize: boardSize, + eachBoxSize: eachBoxSize, + initialPuzzleData: _initialPuzzleData, + fontSize: fontSize, + images: _previousImages, + kInitialSpeed: kInitialSpeed, + borderRadius: 16, + ), + complete: (image, images, palette) { + _previousImages = images; + _previousImage = image; + _previousPalette = palette; + + return PuzzleWidget( + solverClient: _solverClient, + boardSize: boardSize, + eachBoxSize: eachBoxSize, + initialPuzzleData: _initialPuzzleData, + fontSize: fontSize, + images: images, + kInitialSpeed: kInitialSpeed, + borderRadius: 16, + ); + }, + orElse: () => PuzzleWidget( + solverClient: _solverClient, + boardSize: boardSize, + eachBoxSize: eachBoxSize, + initialPuzzleData: _initialPuzzleData, + fontSize: fontSize, + images: _previousImages, + kInitialSpeed: kInitialSpeed, + borderRadius: 16, + ), + ); + }, + ), + const SizedBox(height: 24), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + GameButtonWidget( + solverClient: _solverClient, + initialPuzzleData: _initialPuzzleData, + padding: const EdgeInsets.only(top: 10.0, bottom: 9.0), + width: 130, + ), + const SizedBox(width: 16), + PickImageButton( + text: 'Pick Image', + onTap: ref.read(imageSplitterNotifierProvider) is ImageSplitterComplete + ? () => ref.read(imageSplitterNotifierProvider.notifier).generateImages( + picker: _imagePicker, + puzzleSize: _puzzleSize, + ) + : null, + padding: const EdgeInsets.only(top: 10.0, bottom: 9.0), + width: 130, + ), + ], + ), + const SizedBox(height: 16), + ImageViewer( + imagePicker: _imagePicker, + puzzleSize: _puzzleSize, + previousImage: _previousImage, + previousPalette: _previousPalette, + imageSize: 110, + ), + const SizedBox(height: 50), + ], + ), + ), + ], + ), + ), + CountdownOverlay( + isStartPressed: _isStartPressed, + onFinish: () { + ref.read(timerNotifierProvider.notifier).startTimer(); + setState(() { + _isStartPressed = false; + }); + }, + initialSpeed: kInitialSpeed, + ), + ], + ); + } +} diff --git a/lib/screens/puzzle/puzzle_screen_large.dart b/lib/screens/puzzle/puzzle_screen_large.dart new file mode 100644 index 0000000..cc8dcc9 --- /dev/null +++ b/lib/screens/puzzle/puzzle_screen_large.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:my_flutter_puzzle/application/notifiers/puzzle_type_notifier.dart'; +import 'package:my_flutter_puzzle/models/puzzle_data.dart'; +import 'package:my_flutter_puzzle/screens/photo/photo_screen_large.dart'; +import 'package:my_flutter_puzzle/screens/solo/solo_screen_large.dart'; +import 'package:my_flutter_puzzle/utils/puzzle_solver.dart'; +import 'package:rive/rive.dart'; + +import '../../providers.dart'; + +class PuzzleScreenLarge extends ConsumerWidget { + const PuzzleScreenLarge({ + required this.solverClient, + required this.initialPuzzleData, + required this.puzzleSize, + required this.riveController, + Key? key, + }) : super(key: key); + + final PuzzleSolverClient solverClient; + final PuzzleData initialPuzzleData; + final int puzzleSize; + final RiveAnimationController riveController; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final currentPuzzleType = ref.watch(puzzleTypeNotifierProvider); + + final name = currentPuzzleType.name[0].toUpperCase() + currentPuzzleType.name.substring(1); + + switch (currentPuzzleType) { + case PuzzleType.normal: + return SoloScreenLarge( + solverClient: solverClient, + initialPuzzleData: initialPuzzleData, + puzzleSize: puzzleSize, + puzzleType: name, + riveController: riveController, + ); + case PuzzleType.photo: + return PhotoScreenLarge( + solverClient: solverClient, + initialPuzzleData: initialPuzzleData, + puzzleSize: puzzleSize, + riveController: riveController, + ); + case PuzzleType.multiplayer: + default: + return SoloScreenLarge( + solverClient: solverClient, + initialPuzzleData: initialPuzzleData, + puzzleSize: puzzleSize, + puzzleType: name, + riveController: riveController, + ); + } + } +} diff --git a/lib/screens/puzzle/puzzle_starter_screen.dart b/lib/screens/puzzle/puzzle_starter_screen.dart new file mode 100644 index 0000000..933ab82 --- /dev/null +++ b/lib/screens/puzzle/puzzle_starter_screen.dart @@ -0,0 +1,58 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:my_flutter_puzzle/application/states/puzzle_state.dart'; +import 'package:my_flutter_puzzle/models/puzzle_data.dart'; +import 'package:my_flutter_puzzle/providers.dart'; +import 'package:my_flutter_puzzle/screens/photo/photo_screen.dart'; +import 'package:my_flutter_puzzle/utils/puzzle_solver.dart'; +import 'package:rive/rive.dart'; + +class PuzzleStarterScreen extends ConsumerStatefulWidget { + final int puzzleSize; + final String image; + final int duration; + + const PuzzleStarterScreen({ + Key? key, + required this.duration, + required this.puzzleSize, + required this.image, + }) : super(key: key); + + @override + ConsumerState createState() => _SoloScreenState(); +} + +class _SoloScreenState extends ConsumerState { + late final PuzzleSolverClient _solverClient; + late RiveAnimationController _riveController; + late final int _puzzleSize; + late final PuzzleData _initialPuzzleData; + + @override + void initState() { + _puzzleSize = widget.puzzleSize; + _riveController = SimpleAnimation('idle'); + _solverClient = PuzzleSolverClient(size: _puzzleSize); + _initialPuzzleData = ref.read(puzzleNotifierProvider(_solverClient).notifier).generateInitialPuzzle(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + ref.listen(puzzleNotifierProvider(_solverClient), (previous, PuzzleState next) { + if (next is PuzzleSolved) { + ref.read(timerNotifierProvider.notifier).stopTimer(); + } + }); + return AnimatedSwitcher( + duration: const Duration(milliseconds: 400), + child: PhotoScreen( + solverClient: _solverClient, + initialPuzzleData: _initialPuzzleData, + puzzleSize: _puzzleSize, + riveController: _riveController, + ), + ); + } +} diff --git a/lib/screens/puzzle/top_bar.dart b/lib/screens/puzzle/top_bar.dart new file mode 100644 index 0000000..3225deb --- /dev/null +++ b/lib/screens/puzzle/top_bar.dart @@ -0,0 +1,152 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +class TopBar extends ConsumerWidget { + const TopBar({ + required Color color, + required int puzzleSize, + this.padding = const EdgeInsets.fromLTRB(24.0, 16.0, 24.0, 16.0), + this.tileGap = 16, + this.isCentered = false, + Key? key, + }) : _color = color, + _puzzleSize = puzzleSize, + super(key: key); + + final Color _color; + final int _puzzleSize; + final EdgeInsets padding; + final double tileGap; + final bool isCentered; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Container( + color: _color, + child: Padding( + padding: padding, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + isCentered ? const SizedBox() : const Spacer(), + // TextButton( + // style: _puzzleType == 'Normal' + // ? ButtonStyle( + // foregroundColor: MaterialStateProperty.resolveWith( + // (Set states) { + // return Palette.blue; + // }, + // ), + // ) + // : null, + // onPressed: _puzzleType == 'Normal' + // ? null + // : () { + // ref.read(puzzleTypeNotifierProvider.notifier).changeToNormal(); + // }, + // child: Padding( + // padding: const EdgeInsets.all(8.0), + // child: Row( + // children: [ + // Opacity( + // opacity: _puzzleType == 'Normal' ? 1 : 0.5, + // child: const FaIcon(FontAwesomeIcons.rocket), + // ), + // const SizedBox(width: 8), + // Text( + // 'Normal', + // style: TextStyle( + // fontSize: 16, + // color: Colors.white.withOpacity(_puzzleType == 'Normal' ? 1 : 0.5), + // ), + // ), + // ], + // ), + // ), + // ), + // SizedBox(width: tileGap), + // TextButton( + // style: _puzzleType == 'Photo' + // ? ButtonStyle( + // foregroundColor: MaterialStateProperty.resolveWith( + // (Set states) { + // return Theme.of(context).colorScheme.primary; + // }, + // ), + // ) + // : null, + // onPressed: _puzzleType == 'Photo' + // ? null + // : () { + // final state = ref.read(imageSplitterNotifierProvider); + // if (state is! ImageSplitterComplete) { + // ref.read(imageSplitterNotifierProvider.notifier).getInitialImages(puzzleSize: _puzzleSize); + // } + // + // ref.read(puzzleTypeNotifierProvider.notifier).changeToPhoto(); + // }, + // child: Padding( + // padding: const EdgeInsets.all(8.0), + // child: Row( + // children: [ + // Opacity( + // opacity: _puzzleType == 'Photo' ? 1 : 0.5, + // child: const FaIcon(FontAwesomeIcons.image), + // ), + // const SizedBox(width: 8), + // Text( + // 'Photo', + // style: TextStyle( + // fontSize: 16, + // color: Colors.white.withOpacity(_puzzleType == 'Photo' ? 1 : 0.5), + // ), + // ), + // ], + // ), + // ), + // ), + // SizedBox(width: tileGap), + // TextButton( + // style: _puzzleType == 'Multiplayer' + // ? ButtonStyle( + // foregroundColor: MaterialStateProperty.resolveWith( + // (Set states) { + // return Palette.blue; + // }, + // ), + // ) + // : null, + // onPressed: _puzzleType == 'Multiplayer' + // ? null + // : () { + // // ref.read(puzzleTypeNotifierProvider.notifier).dispose(); + // + // ref.read(puzzleTypeNotifierProvider.notifier).changeToMultiplayer(); + // ref.read(emailAuthNotificationProvider.notifier).checkForSignedUser(); + // }, + // child: Padding( + // padding: const EdgeInsets.all(8.0), + // child: Row( + // children: [ + // Opacity( + // opacity: _puzzleType == 'Multiplayer' ? 1 : 0.5, + // child: const FaIcon(FontAwesomeIcons.gamepad), + // ), + // const SizedBox(width: 8), + // Text( + // 'Multiplayer', + // style: TextStyle( + // fontSize: 16, + // color: Colors.white.withOpacity(_puzzleType == 'Multiplayer' ? 1 : 0.5), + // ), + // ), + // ], + // ), + // ), + // ), + ], + ), + ), + ); + } +} diff --git a/lib/screens/puzzle_screen.dart b/lib/screens/puzzle_screen.dart new file mode 100644 index 0000000..03e705a --- /dev/null +++ b/lib/screens/puzzle_screen.dart @@ -0,0 +1,220 @@ +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:my_flutter_puzzle/models/user_info.dart'; +import 'package:my_flutter_puzzle/widgets/puzzle_widgets/puzzle_widgets.dart'; + +class PuzzleScreen extends StatefulWidget { + const PuzzleScreen({ + Key? key, + required this.initialList, + required this.id, + required this.myInfo, + }) : super(key: key); + + final List initialList; + final String id; + final UserData myInfo; + + @override + _PuzzleScreenState createState() => _PuzzleScreenState(); +} + +class _PuzzleScreenState extends State { + late final List myList; + late final List opponentList; + int _moves = 0; + + onClick(index) { + log('-----------------------'); + log('Tapped index: $index'); + + int emptyTilePosIndex = myList.indexOf(0); + int emptyTilePosRow = emptyTilePosIndex ~/ 4; + int emptyTilePosCol = emptyTilePosIndex % 4; + + int currentTileRow = index ~/ 4; + int currentTileCol = index % 4; + + //current element moves up + + if ((currentTileRow - 1 == emptyTilePosRow) && (currentTileCol == emptyTilePosCol)) { + setState(() { + myList[emptyTilePosIndex] = myList[index]; + myList[index] = 0; + _moves++; + }); + } + + //current element moves down + + else if ((currentTileRow + 1 == emptyTilePosRow) && (currentTileCol == emptyTilePosCol)) { + setState(() { + myList[emptyTilePosIndex] = myList[index]; + myList[index] = 0; + _moves++; + }); + } + + //current element moves left + + else if ((currentTileRow == emptyTilePosRow) && (currentTileCol + 1 == emptyTilePosCol)) { + setState(() { + myList[emptyTilePosIndex] = myList[index]; + myList[index] = 0; + _moves++; + }); + } + + //current element moves right + + else if ((currentTileRow == emptyTilePosRow) && (currentTileCol - 1 == emptyTilePosCol)) { + setState(() { + myList[emptyTilePosIndex] = myList[index]; + myList[index] = 0; + _moves++; + }); + } else { + if (currentTileCol == emptyTilePosCol) { + int low; + int high; + + // multiple elements move up + + if (emptyTilePosRow < currentTileRow) { + low = emptyTilePosRow; + high = currentTileRow; + + int i = low; + while (i < high) { + setState(() { + myList[(i * 4) + emptyTilePosCol] = myList[(((i + 1) * 4) + emptyTilePosCol)]; + }); + + i += 1; + } + setState(() { + myList[(high * 4) + emptyTilePosCol] = 0; + _moves++; + }); + } + + //multiple elements move down + + else { + low = emptyTilePosRow; + high = currentTileRow; + + int i = low; + while (i > high) { + setState(() { + myList[(i * 4) + emptyTilePosCol] = myList[(((i - 1) * 4) + emptyTilePosCol)]; + }); + + i -= 1; + } + setState(() { + myList[(high * 4) + emptyTilePosCol] = 0; + _moves++; + }); + } + } + + // multiple elements move left + + // multiple elements move right + + if (currentTileRow == emptyTilePosRow) { + int low; + int high; + + // multiple elements move left + + if (emptyTilePosCol < currentTileCol) { + low = emptyTilePosCol; + high = currentTileCol; + + int i = low; + while (i < high) { + setState(() { + myList[(emptyTilePosRow * 4) + i] = myList[(emptyTilePosRow * 4) + (i + 1)]; + }); + + i += 1; + } + setState(() { + myList[high + (emptyTilePosRow * 4)] = 0; + _moves++; + }); + } + + //multiple elements move right + + else { + low = emptyTilePosCol; + high = currentTileCol; + + int i = low; + while (i > high) { + setState(() { + myList[(i + (emptyTilePosRow * 4))] = myList[(i - 1) + (emptyTilePosRow * 4)]; + }); + + i -= 1; + } + setState(() { + myList[high + (emptyTilePosRow * 4)] = 0; + _moves++; + }); + } + } + } + + // _databaseClient.updateGameState( + // id: widget.id, + // mydata: widget.myInfo, + // numberList: myList, + // moves: _moves, + // ); + + log('List: $myList'); + log('-----------------------'); + } + + @override + void initState() { + super.initState(); + myList = widget.initialList; + opponentList = widget.initialList; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + body: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + // PLAYER 1 puzzle --> my (own) + Column( + children: [ + const PlayerText( + displayName: 'PLAYER 1', + ), + // TODO: Change font size + MovesText( + moves: _moves, + fontSize: 60, + ), + ], + ), + Container( + height: MediaQuery.of(context).size.height * 0.6, + width: 2, + color: Colors.black, + ), + ], + ), + ); + } +} diff --git a/lib/screens/puzzle_solo_screen.dart b/lib/screens/puzzle_solo_screen.dart new file mode 100644 index 0000000..6e95baa --- /dev/null +++ b/lib/screens/puzzle_solo_screen.dart @@ -0,0 +1,489 @@ +import 'dart:developer'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:my_flutter_puzzle/res/palette.dart'; +import 'package:my_flutter_puzzle/utils/puzzle_solver.dart'; +import 'package:my_flutter_puzzle/widgets/puzzle_widgets/puzzle_widgets.dart'; + +class PuzzleSoloScreen extends StatefulWidget { + const PuzzleSoloScreen({ + Key? key, + }) : super(key: key); + + @override + _PuzzleSoloScreenState createState() => _PuzzleSoloScreenState(); +} + +class _PuzzleSoloScreenState extends State { + late final PuzzleSolverClient _solverClient; + + List>? _board2D; + List? myList; + int _moves = 0; + final _puzzleSize = 3; + final int _animationSpeedInMilliseconds = 300; + bool _isComputing = false; + bool _isAutoSolving = false; + bool _isSolved = false; + + Map? _offsetMap; + List _solvedList = []; + + @override + void initState() { + super.initState(); + _solverClient = PuzzleSolverClient(size: _puzzleSize); + initBoard(); + generateSolvedList(); + } + + generateSolvedList() { + for (int i = 1; i < _puzzleSize * _puzzleSize; i++) { + _solvedList.add(i); + } + _solvedList.add(0); + } + + scrambleBoard() { + final generated2DBoard = _solverClient.createRandomBoard(); + final generated1DBoard = _solverClient.convertTo1D(generated2DBoard); + updateOffset(generated1DBoard); + setState(() { + _board2D = generated2DBoard; + myList = generated1DBoard; + _moves = 0; + _isSolved = false; + }); + } + + initBoard() { + final generated2DBoard = _solverClient.createRandomBoard(); + final generated1DBoard = _solverClient.convertTo1D(generated2DBoard); + + createOffset(generated1DBoard); + setState(() { + _board2D = generated2DBoard; + myList = generated1DBoard; + + _moves = 0; + }); + } + + startAutoSolver() async { + if (_board2D != null) { + setState(() { + _isComputing = true; + }); + + List>? boardStates = await compute(_solverClient.runner, _solverClient.convertTo2D(myList!)); + + setState(() { + _isComputing = false; + _isAutoSolving = true; + }); + + if (boardStates != null) { + for (var board in boardStates) { + await Future.delayed(Duration( + milliseconds: _animationSpeedInMilliseconds, + )); + setState(() { + myList = board; + _moves++; + }); + updateOffset(myList!); + } + } + } + setState(() { + _isAutoSolving = false; + _isSolved = true; + }); + showCompleteDialogBox(context); + } + + isSolved(List currentBoard) { + if (listEquals(currentBoard, _solvedList)) { + setState(() { + _isSolved = true; + }); + return true; + } + setState(() { + _isSolved = false; + }); + + return false; + } + + onClick(index) { + log('-----------------------'); + log('Tapped index: $index'); + + if (myList != null) { + int emptyTilePosIndex = myList!.indexOf(0); + int emptyTilePosRow = emptyTilePosIndex ~/ _puzzleSize; + int emptyTilePosCol = emptyTilePosIndex % _puzzleSize; + + int currentTileRow = index ~/ _puzzleSize; + int currentTileCol = index % _puzzleSize; + + //current element moves up + if ((currentTileRow - 1 == emptyTilePosRow) && (currentTileCol == emptyTilePosCol)) { + myList![emptyTilePosIndex] = myList![index]; + myList![index] = 0; + _moves++; + } + + //current element moves down + else if ((currentTileRow + 1 == emptyTilePosRow) && (currentTileCol == emptyTilePosCol)) { + myList![emptyTilePosIndex] = myList![index]; + myList![index] = 0; + _moves++; + } + + //current element moves left + else if ((currentTileRow == emptyTilePosRow) && (currentTileCol + 1 == emptyTilePosCol)) { + myList![emptyTilePosIndex] = myList![index]; + myList![index] = 0; + _moves++; + } + + //current element moves right + else if ((currentTileRow == emptyTilePosRow) && (currentTileCol - 1 == emptyTilePosCol)) { + myList![emptyTilePosIndex] = myList![index]; + myList![index] = 0; + _moves++; + } else { + if (currentTileCol == emptyTilePosCol) { + int low; + int high; + + // multiple elements move up + if (emptyTilePosRow < currentTileRow) { + low = emptyTilePosRow; + high = currentTileRow; + + int i = low; + while (i < high) { + myList![(i * _puzzleSize) + emptyTilePosCol] = myList![(((i + 1) * _puzzleSize) + emptyTilePosCol)]; + + i += 1; + } + + myList![(high * _puzzleSize) + emptyTilePosCol] = 0; + _moves++; + } + + //multiple elements move down + else { + low = emptyTilePosRow; + high = currentTileRow; + + int i = low; + while (i > high) { + myList![(i * _puzzleSize) + emptyTilePosCol] = myList![(((i - 1) * _puzzleSize) + emptyTilePosCol)]; + + i -= 1; + } + + myList![(high * _puzzleSize) + emptyTilePosCol] = 0; + _moves++; + } + } + + // multiple elements move left or right + if (currentTileRow == emptyTilePosRow) { + int low; + int high; + + // multiple elements move left + if (emptyTilePosCol < currentTileCol) { + low = emptyTilePosCol; + high = currentTileCol; + + int i = low; + while (i < high) { + myList![(emptyTilePosRow * _puzzleSize) + i] = myList![(emptyTilePosRow * _puzzleSize) + (i + 1)]; + + i += 1; + } + + myList![high + (emptyTilePosRow * _puzzleSize)] = 0; + _moves++; + } + + //multiple elements move right + else { + low = emptyTilePosCol; + high = currentTileCol; + + int i = low; + while (i > high) { + myList![(i + (emptyTilePosRow * _puzzleSize))] = myList![(i - 1) + (emptyTilePosRow * _puzzleSize)]; + + i -= 1; + } + + myList![high + (emptyTilePosRow * _puzzleSize)] = 0; + _moves++; + } + } + } + + // Update Offset list + // setState(() { + // updateOffset(myList!); + // }); + updateOffset(myList!); + setState(() {}); + + if (isSolved(myList!)) { + showCompleteDialogBox(context); + } + + log('List: $myList'); + log('-----------------------'); + } + } + + createOffset(List board) { + Map offsetMap = {}; + int j = 0; + + log('BOARD: $board'); + + for (int i = 0; i < board.length; i++) { + final xMod = i % _puzzleSize; + double x = xMod / (_puzzleSize - 1); + + if (x % i == 0 && i != 0) j++; + int yMod = j % _puzzleSize; + double y = yMod / (_puzzleSize - 1); + + offsetMap.addEntries([ + MapEntry( + board[i], + FractionalOffset(x, y), + ) + ]); + } + + log('INITIAL OFFSET MAP: $offsetMap'); + setState(() { + _offsetMap = offsetMap; + }); + } + + updateOffset(List board) { + int j = 0; + + for (int i = 0; i < board.length; i++) { + final xMod = i % _puzzleSize; + double x = xMod / (_puzzleSize - 1); + + if (x % i == 0 && i != 0) j++; + int yMod = j % _puzzleSize; + double y = yMod / (_puzzleSize - 1); + + _offsetMap![board[i]] = FractionalOffset(x, y); + } + log('OFFSET MAP: $_offsetMap'); + } + + showCompleteDialogBox(BuildContext context) { + showDialog( + context: context, + builder: (context) => Dialog( + child: Padding( + padding: const EdgeInsets.all(24.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Text( + 'Solved successfully!', + style: TextStyle(fontSize: 22), + ), + const SizedBox(height: 16), + ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Palette.violet, + onSurface: Palette.violet, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(50), + ), + ), + onPressed: () { + Navigator.of(context).pop(); + }, + child: const Padding( + padding: EdgeInsets.all(16.0), + child: Text( + 'OK', + style: TextStyle(fontSize: 22), + ), + ), + ), + ], + ), + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + var screenSize = MediaQuery.of(context).size; + + var boardSize = screenSize.height * 0.6; + + var spacing = 4; + var eachBoxSize = (boardSize / _puzzleSize) - (spacing * (_puzzleSize - 1)); + + return Scaffold( + backgroundColor: Colors.white, + body: myList != null && _offsetMap != null + ? Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Row(), + MovesText( + moves: _moves, + fontSize: 60, + ), + SizedBox( + height: boardSize, + width: boardSize, + child: Stack( + children: [ + for (int i = 0; i < _offsetMap!.length; i++) + _offsetMap!.entries.toList()[i].key != 0 + ? AnimatedAlign( + alignment: _offsetMap!.entries.toList()[i].value, + duration: Duration( + milliseconds: _animationSpeedInMilliseconds, + ), + curve: Curves.easeInOut, + child: GestureDetector( + onTap: () => onClick(myList!.indexOf(_offsetMap!.entries.toList()[i].key)), + child: Card( + elevation: 4, + color: const Color(0xFF2868d7), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + child: SizedBox( + height: eachBoxSize, + width: eachBoxSize, + child: Center( + child: Text( + _offsetMap!.entries.toList()[i].key.toString(), + style: const TextStyle( + fontSize: 60, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + ), + ), + ), + ), + ) + : const SizedBox(), + ], + ), + ), + // AnimatedGrid( + // puzzleSize: _puzzleSize, + // key: UniqueKey(), + // number: myList!, + // offsetList: _offsetList!, + // onTap: onClick, + // color: const Color(0xFF2868d7), + // ), + const SizedBox(height: 30), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 30.0), + child: SizedBox( + width: MediaQuery.of(context).size.width * 0.2, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Palette.violet, + onSurface: Palette.violet, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(50), + ), + ), + onPressed: _isComputing || _isAutoSolving || _isSolved + ? null + : () { + startAutoSolver(); + }, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: _isComputing || _isAutoSolving + ? Row( + children: [ + const SizedBox( + width: 25, + height: 25, + child: CircularProgressIndicator( + color: Palette.violet, + strokeWidth: 2, + ), + ), + const SizedBox(width: 16), + Text( + _isComputing ? 'Computing ...' : 'Solving ...', + style: const TextStyle(fontSize: 20), + ), + ], + ) + : const Text( + 'Start Auto Solver', + style: TextStyle(fontSize: 22), + ), + ), + ), + ), + ), + const SizedBox(width: 16.0), + Padding( + padding: const EdgeInsets.only(bottom: 30.0), + child: SizedBox( + width: MediaQuery.of(context).size.width * 0.2, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Palette.crimson, + onSurface: Palette.crimson, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(50), + ), + ), + onPressed: _isComputing || _isAutoSolving + ? null + : () { + scrambleBoard(); + }, + child: const Padding( + padding: EdgeInsets.all(16.0), + child: Text( + 'Scramble', + style: TextStyle(fontSize: 22), + ), + ), + ), + ), + ), + ], + ), + ], + ) + : const SizedBox(), + ); + } +} diff --git a/lib/screens/solo/solo_large_screen_prev.dart b/lib/screens/solo/solo_large_screen_prev.dart new file mode 100644 index 0000000..d2cc772 --- /dev/null +++ b/lib/screens/solo/solo_large_screen_prev.dart @@ -0,0 +1,635 @@ +import 'dart:developer'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:my_flutter_puzzle/res/palette.dart'; +import 'package:my_flutter_puzzle/utils/puzzle_solver.dart'; +import 'package:rive/rive.dart'; + +class SoloLargeScreen extends StatefulWidget { + const SoloLargeScreen({ + Key? key, + }) : super(key: key); + + @override + _SoloLargeScreenState createState() => _SoloLargeScreenState(); +} + +class _SoloLargeScreenState extends State { + late PuzzleSolverClient _solverClient; + late RiveAnimationController _riveController; + + List>? _board2D; + List? myList; + int _moves = 0; + final _puzzleSize = 3; + final int _animationSpeedInMilliseconds = 300; + bool _isComputing = false; + bool _isAutoSolving = false; + bool _isSolved = false; + + Map? _offsetMap; + List _solvedList = []; + + @override + void initState() { + super.initState(); + _solverClient = PuzzleSolverClient(size: _puzzleSize); + _riveController = SimpleAnimation('idle'); + initBoard(); + generateSolvedList(); + } + + generateSolvedList() { + for (int i = 1; i < _puzzleSize * _puzzleSize; i++) { + _solvedList.add(i); + } + _solvedList.add(0); + } + + scrambleBoard() { + final generated2DBoard = _solverClient.createRandomBoard(); + final generated1DBoard = _solverClient.convertTo1D(generated2DBoard); + updateOffset(generated1DBoard); + setState(() { + _board2D = generated2DBoard; + myList = generated1DBoard; + _moves = 0; + _isSolved = false; + }); + } + + initBoard() { + final generated2DBoard = _solverClient.createRandomBoard(); + final generated1DBoard = _solverClient.convertTo1D(generated2DBoard); + + createOffset(generated1DBoard); + + setState(() { + _board2D = generated2DBoard; + myList = generated1DBoard; + _moves = 0; + }); + } + + startAutoSolver() async { + if (_board2D != null) { + setState(() { + _isComputing = true; + }); + + List>? boardStates = await compute( + _solverClient.runner, _solverClient.convertTo2D(myList!)); + + setState(() { + _isComputing = false; + _isAutoSolving = true; + }); + + if (boardStates != null) { + for (var board in boardStates) { + await Future.delayed(Duration( + milliseconds: _animationSpeedInMilliseconds, + )); + setState(() { + myList = board; + _moves++; + }); + updateOffset(myList!); + } + } + } + setState(() { + _isAutoSolving = false; + _isSolved = true; + }); + showCompleteDialogBox(context); + } + + isSolved(List currentBoard) { + if (listEquals(currentBoard, _solvedList)) { + setState(() { + _isSolved = true; + }); + return true; + } + setState(() { + _isSolved = false; + }); + + return false; + } + + onClick(index) { + log('-----------------------'); + log('Tapped index: $index'); + + if (myList != null) { + int emptyTilePosIndex = myList!.indexOf(0); + int emptyTilePosRow = emptyTilePosIndex ~/ _puzzleSize; + int emptyTilePosCol = emptyTilePosIndex % _puzzleSize; + + int currentTileRow = index ~/ _puzzleSize; + int currentTileCol = index % _puzzleSize; + + //current element moves up + if ((currentTileRow - 1 == emptyTilePosRow) && + (currentTileCol == emptyTilePosCol)) { + myList![emptyTilePosIndex] = myList![index]; + myList![index] = 0; + _moves++; + } + + //current element moves down + else if ((currentTileRow + 1 == emptyTilePosRow) && + (currentTileCol == emptyTilePosCol)) { + myList![emptyTilePosIndex] = myList![index]; + myList![index] = 0; + _moves++; + } + + //current element moves left + else if ((currentTileRow == emptyTilePosRow) && + (currentTileCol + 1 == emptyTilePosCol)) { + myList![emptyTilePosIndex] = myList![index]; + myList![index] = 0; + _moves++; + } + + //current element moves right + else if ((currentTileRow == emptyTilePosRow) && + (currentTileCol - 1 == emptyTilePosCol)) { + myList![emptyTilePosIndex] = myList![index]; + myList![index] = 0; + _moves++; + } else { + if (currentTileCol == emptyTilePosCol) { + int low; + int high; + + // multiple elements move up + if (emptyTilePosRow < currentTileRow) { + low = emptyTilePosRow; + high = currentTileRow; + + int i = low; + while (i < high) { + myList![(i * _puzzleSize) + emptyTilePosCol] = + myList![(((i + 1) * _puzzleSize) + emptyTilePosCol)]; + + i += 1; + } + + myList![(high * _puzzleSize) + emptyTilePosCol] = 0; + _moves++; + } + + //multiple elements move down + else { + low = emptyTilePosRow; + high = currentTileRow; + + int i = low; + while (i > high) { + myList![(i * _puzzleSize) + emptyTilePosCol] = + myList![(((i - 1) * _puzzleSize) + emptyTilePosCol)]; + + i -= 1; + } + + myList![(high * _puzzleSize) + emptyTilePosCol] = 0; + _moves++; + } + } + + // multiple elements move left or right + if (currentTileRow == emptyTilePosRow) { + int low; + int high; + + // multiple elements move left + if (emptyTilePosCol < currentTileCol) { + low = emptyTilePosCol; + high = currentTileCol; + + int i = low; + while (i < high) { + myList![(emptyTilePosRow * _puzzleSize) + i] = + myList![(emptyTilePosRow * _puzzleSize) + (i + 1)]; + + i += 1; + } + + myList![high + (emptyTilePosRow * _puzzleSize)] = 0; + _moves++; + } + + //multiple elements move right + else { + low = emptyTilePosCol; + high = currentTileCol; + + int i = low; + while (i > high) { + myList![(i + (emptyTilePosRow * _puzzleSize))] = + myList![(i - 1) + (emptyTilePosRow * _puzzleSize)]; + + i -= 1; + } + + myList![high + (emptyTilePosRow * _puzzleSize)] = 0; + _moves++; + } + } + } + + // Update Offset list + // setState(() { + // updateOffset(myList!); + // }); + updateOffset(myList!); + setState(() {}); + + if (isSolved(myList!)) { + showCompleteDialogBox(context); + } + + log('List: $myList'); + log('-----------------------'); + } + } + + createOffset(List board) { + Map offsetMap = {}; + int j = 0; + + log('BOARD: $board'); + + for (int i = 0; i < board.length; i++) { + final xMod = i % _puzzleSize; + double x = xMod / (_puzzleSize - 1); + + if (x % i == 0 && i != 0) j++; + int yMod = j % _puzzleSize; + double y = yMod / (_puzzleSize - 1); + + offsetMap.addEntries([ + MapEntry( + board[i], + FractionalOffset(x, y), + ) + ]); + } + + log('INITIAL OFFSET MAP: $offsetMap'); + setState(() { + _offsetMap = offsetMap; + }); + } + + updateOffset(List board) { + int j = 0; + + for (int i = 0; i < board.length; i++) { + final xMod = i % _puzzleSize; + double x = xMod / (_puzzleSize - 1); + + if (x % i == 0 && i != 0) j++; + int yMod = j % _puzzleSize; + double y = yMod / (_puzzleSize - 1); + + _offsetMap![board[i]] = FractionalOffset(x, y); + } + log('OFFSET MAP: $_offsetMap'); + } + + showCompleteDialogBox(BuildContext context) { + showDialog( + context: context, + builder: (context) => Dialog( + child: Padding( + padding: const EdgeInsets.all(24.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Text( + 'Solved successfully!', + style: TextStyle(fontSize: 22), + ), + const SizedBox(height: 16), + ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Palette.violet, + onSurface: Palette.violet, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(50), + ), + ), + onPressed: () { + Navigator.of(context).pop(); + }, + child: const Padding( + padding: EdgeInsets.all(16.0), + child: Text( + 'OK', + style: TextStyle(fontSize: 22), + ), + ), + ), + ], + ), + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + var screenSize = MediaQuery.of(context).size; + + var shortestSide = screenSize.shortestSide; + var fontSize = shortestSide * 0.08; + var boardSize = shortestSide * 0.45; + + var spacing = 5; + var eachBoxSize = (boardSize / _puzzleSize) - (spacing * (_puzzleSize - 1)); + + return Scaffold( + backgroundColor: Colors.black, + appBar: PreferredSize( + child: Container( + color: Colors.black, + ), + preferredSize: Size(double.maxFinite, shortestSide * 0.1), + ), + body: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.only(left: 56.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row(), + const Text( + 'Photo', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + const SizedBox(height: 8), + const Text( + 'Puzzle', + style: TextStyle( + fontSize: 58, + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + const Text( + 'Challenge', + style: TextStyle( + fontSize: 58, + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + const SizedBox(height: 32), + RichText( + text: TextSpan( + style: const TextStyle( + fontSize: 24, + // fontWeight: FontWeight.w500, + color: Colors.white, + ), + children: [ + TextSpan( + text: _moves.toString(), + style: const TextStyle( + fontWeight: FontWeight.w600, + ), + ), + const TextSpan(text: ' Moves | '), + const TextSpan( + text: '15', + style: TextStyle( + fontWeight: FontWeight.w600, + ), + ), + const TextSpan(text: ' Tiles'), + ], + ), + ), + const SizedBox(height: 32), + SizedBox( + width: 145, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30), + ), + ), + onPressed: () {}, + child: const Padding( + padding: EdgeInsets.only(top: 13.0, bottom: 12.0), + child: Text( + 'Start Game', + // 'Restart', + // 'Get ready...', + style: TextStyle( + fontSize: 16, + ), + ), + ), + ), + ), + ], + ), + ), + myList != null && _offsetMap != null + ? Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + // Row(), + // MovesText( + // moves: _moves, + // fontSize: fontSize, + // ), + Row( + mainAxisSize: MainAxisSize.min, + children: const [ + Text( + '00:00:00', + style: TextStyle( + fontSize: 40, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + SizedBox(width: 8), + Icon( + Icons.timer, + color: Colors.white, + size: 40, + ) + ], + ), + SizedBox( + height: boardSize, + width: boardSize, + child: Stack( + children: [ + for (int i = 0; i < _offsetMap!.length; i++) + _offsetMap!.entries.toList()[i].key != 0 + ? AnimatedAlign( + alignment: + _offsetMap!.entries.toList()[i].value, + duration: Duration( + milliseconds: + _animationSpeedInMilliseconds, + ), + curve: Curves.easeInOut, + child: GestureDetector( + onTap: () => onClick(myList!.indexOf( + _offsetMap!.entries.toList()[i].key)), + child: Card( + elevation: 4, + color: const Color(0xFF2868d7), + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(20), + ), + child: SizedBox( + height: eachBoxSize, + width: eachBoxSize, + child: Center( + child: Text( + _offsetMap!.entries + .toList()[i] + .key + .toString(), + style: TextStyle( + fontSize: fontSize, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + ), + ), + ), + ), + ) + : const SizedBox(), + ], + ), + ), + const SizedBox(height: 30), + // Row( + // mainAxisSize: MainAxisSize.min, + // children: [ + // Padding( + // padding: const EdgeInsets.only(bottom: 30.0), + // child: SizedBox( + // width: MediaQuery.of(context).size.width * 0.2, + // child: ElevatedButton( + // style: ElevatedButton.styleFrom( + // primary: Palette.violet, + // onSurface: Palette.violet, + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(50), + // ), + // ), + // onPressed: _isComputing || _isAutoSolving || _isSolved + // ? null + // : () { + // startAutoSolver(); + // }, + // child: Padding( + // padding: const EdgeInsets.all(16.0), + // child: _isComputing || _isAutoSolving + // ? Row( + // children: [ + // const SizedBox( + // width: 25, + // height: 25, + // child: CircularProgressIndicator( + // color: Palette.violet, + // strokeWidth: 2, + // ), + // ), + // const SizedBox(width: 16), + // Text( + // _isComputing + // ? 'Computing ...' + // : 'Solving ...', + // style: const TextStyle(fontSize: 20), + // ), + // ], + // ) + // : const Text( + // 'Start Auto Solver', + // style: TextStyle(fontSize: 22), + // ), + // ), + // ), + // ), + // ), + // const SizedBox(width: 16.0), + // Padding( + // padding: const EdgeInsets.only(bottom: 30.0), + // child: SizedBox( + // width: MediaQuery.of(context).size.width * 0.2, + // child: ElevatedButton( + // style: ElevatedButton.styleFrom( + // primary: Palette.crimson, + // onSurface: Palette.crimson, + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(50), + // ), + // ), + // onPressed: _isComputing || _isAutoSolving + // ? null + // : () { + // scrambleBoard(); + // }, + // child: const Padding( + // padding: EdgeInsets.all(16.0), + // child: Text( + // 'Scramble', + // style: TextStyle(fontSize: 22), + // ), + // ), + // ), + // ), + // ), + // ], + // ), + ], + ) + : const SizedBox(), + Align( + alignment: Alignment.bottomCenter, + child: Padding( + padding: const EdgeInsets.only(right: 56.0, bottom: 56), + child: SizedBox( + width: boardSize * 0.75, + height: boardSize * 0.75, + child: RiveAnimation.asset( + 'assets/rive/dash.riv', + fit: BoxFit.contain, + antialiasing: true, + controllers: [_riveController], + onInit: (_) => setState(() {}), + ), + ), + ), + ), + // SizedBox(), + ], + ), + ); + } +} diff --git a/lib/screens/solo/solo_screen.dart b/lib/screens/solo/solo_screen.dart new file mode 100644 index 0000000..9540436 --- /dev/null +++ b/lib/screens/solo/solo_screen.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:my_flutter_puzzle/application/states/puzzle_state.dart'; +import 'package:my_flutter_puzzle/providers.dart'; +import 'package:my_flutter_puzzle/screens/solo/solo_screen_large.dart'; +import 'package:my_flutter_puzzle/screens/solo/solo_screen_small.dart'; +import 'package:my_flutter_puzzle/utils/puzzle_solver.dart'; +import 'package:my_flutter_puzzle/utils/responsive_layout.dart'; +import 'package:rive/rive.dart'; + +import '../../models/puzzle_data.dart'; +import 'solo_screen_medium.dart'; + +class SoloScreen extends ConsumerWidget { + const SoloScreen({ + required this.solverClient, + required this.initialPuzzleData, + required this.puzzleSize, + required this.puzzleType, + required this.riveController, + Key? key, + }) : super(key: key); + + final PuzzleSolverClient solverClient; + final PuzzleData initialPuzzleData; + final int puzzleSize; + final String puzzleType; + final RiveAnimationController riveController; + + @override + Widget build(BuildContext context, WidgetRef ref) { + ref.listen(puzzleNotifierProvider(solverClient), + (previous, PuzzleState next) { + if (next is PuzzleSolved) { + ref.read(timerNotifierProvider.notifier).stopTimer(); + } + }); + + return ResponsiveLayout( + largeChild: SoloScreenLarge( + solverClient: solverClient, + puzzleType: puzzleType, + initialPuzzleData: initialPuzzleData, + puzzleSize: puzzleSize, + riveController: riveController, + ), + mediumChild: SoloScreenMedium( + solverClient: solverClient, + initialPuzzleData: initialPuzzleData, + puzzleSize: puzzleSize, + puzzleType: puzzleType, + riveController: riveController, + ), + smallChild: SoloScreenSmall( + solverClient: solverClient, + initialPuzzleData: initialPuzzleData, + puzzleSize: puzzleSize, + puzzleType: puzzleType, + riveController: riveController, + ), + ); + } +} diff --git a/lib/screens/solo/solo_screen_large.dart b/lib/screens/solo/solo_screen_large.dart new file mode 100644 index 0000000..86a2e55 --- /dev/null +++ b/lib/screens/solo/solo_screen_large.dart @@ -0,0 +1,164 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:my_flutter_puzzle/application/states/puzzle_state.dart'; +import 'package:my_flutter_puzzle/providers.dart'; +import 'package:my_flutter_puzzle/res/puzzle_constants.dart'; +import 'package:my_flutter_puzzle/screens/puzzle/top_bar.dart'; +import 'package:my_flutter_puzzle/utils/puzzle_solver.dart'; +import 'package:my_flutter_puzzle/widgets/solo_screen/solo_screen_export.dart'; +import 'package:rive/rive.dart'; + +import '../../models/puzzle_data.dart'; + +class SoloScreenLarge extends ConsumerStatefulWidget { + const SoloScreenLarge({ + required this.solverClient, + required this.initialPuzzleData, + required this.puzzleSize, + required this.puzzleType, + required this.riveController, + Key? key, + }) : super(key: key); + + final PuzzleSolverClient solverClient; + final PuzzleData initialPuzzleData; + final int puzzleSize; + final String puzzleType; + final RiveAnimationController riveController; + + @override + ConsumerState createState() => _SoloScreenLargeState(); +} + +class _SoloScreenLargeState extends ConsumerState { + late final PuzzleSolverClient _solverClient; + late final int _puzzleSize; + late final PuzzleData _initialPuzzleData; + late final String _puzzleType; + late final RiveAnimationController _riveController; + bool _isStartPressed = false; + + @override + void initState() { + _solverClient = widget.solverClient; + _puzzleSize = widget.puzzleSize; + _initialPuzzleData = widget.initialPuzzleData; + _puzzleType = widget.puzzleType; + _riveController = widget.riveController; + super.initState(); + } + + @override + Widget build(BuildContext context) { + ref.listen(puzzleNotifierProvider(_solverClient), (previous, PuzzleState next) { + if (next is PuzzleSolved) { + // TODO: Add celebration + } + if (next is PuzzleInitializing) { + setState(() { + _isStartPressed = true; + }); + } + }); + + var fontSize = 70.0; + var boardSize = 450.0; + + var spacing = 5; + var eachBoxSize = (boardSize / _puzzleSize) - (spacing * (_puzzleSize - 1)); + + return Scaffold( + backgroundColor: Theme.of(context).backgroundColor, + body: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.only(left: 56.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row(), + Text( + _puzzleType, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + const SizedBox(height: 8), + const Text( + 'Puzzle', + style: TextStyle( + fontSize: 58, + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + const Text( + 'Challenge', + style: TextStyle( + fontSize: 58, + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + const SizedBox(height: 32), + MovesTilesWidget(solverClient: _solverClient), + const SizedBox(height: 32), + GameButtonWidget( + solverClient: _solverClient, + initialPuzzleData: _initialPuzzleData, + ), + ], + ), + ), + SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + const TimerWidget( + fontSize: 40, + ), + const SizedBox(height: 36), + PuzzleWidget( + solverClient: _solverClient, + boardSize: boardSize, + eachBoxSize: eachBoxSize, + initialPuzzleData: _initialPuzzleData, + fontSize: fontSize, + kInitialSpeed: kInitialSpeed, + ), + const SizedBox(height: 30), + ], + ), + ), + Column( + mainAxisSize: MainAxisSize.max, + children: [ + const Spacer(), + CountdownWidget( + isStartPressed: _isStartPressed, + onFinish: () { + ref.read(timerNotifierProvider.notifier).startTimer(); + setState(() { + _isStartPressed = false; + }); + }, + initialSpeed: kInitialSpeed, + ), + const Spacer(), + AnimatedDash( + boardSize: boardSize * 0.8, + riveController: _riveController, + onInit: (_) => setState(() {}), + ), + ], + ), + // SizedBox(), + ], + ), + ); + } +} diff --git a/lib/screens/solo/solo_screen_medium.dart b/lib/screens/solo/solo_screen_medium.dart new file mode 100644 index 0000000..629f75f --- /dev/null +++ b/lib/screens/solo/solo_screen_medium.dart @@ -0,0 +1,148 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:my_flutter_puzzle/application/states/puzzle_state.dart'; +import 'package:my_flutter_puzzle/providers.dart'; +import 'package:my_flutter_puzzle/res/puzzle_constants.dart'; +import 'package:my_flutter_puzzle/screens/puzzle/top_bar.dart'; +import 'package:my_flutter_puzzle/utils/puzzle_solver.dart'; +import 'package:my_flutter_puzzle/widgets/solo_screen/countdown_overlay.dart'; +import 'package:my_flutter_puzzle/widgets/solo_screen/solo_screen_export.dart'; +import 'package:rive/rive.dart'; + +import '../../models/puzzle_data.dart'; + +class SoloScreenMedium extends ConsumerStatefulWidget { + const SoloScreenMedium({ + required this.solverClient, + required this.initialPuzzleData, + required this.puzzleSize, + required this.puzzleType, + required this.riveController, + Key? key, + }) : super(key: key); + + final PuzzleSolverClient solverClient; + final PuzzleData initialPuzzleData; + final int puzzleSize; + final String puzzleType; + final RiveAnimationController riveController; + + @override + ConsumerState createState() => _SoloScreenLargeState(); +} + +class _SoloScreenLargeState extends ConsumerState { + late final PuzzleSolverClient _solverClient; + late final int _puzzleSize; + late final PuzzleData _initialPuzzleData; + late final String _puzzleType; + late final RiveAnimationController _riveController; + bool _isStartPressed = false; + + @override + void initState() { + _solverClient = widget.solverClient; + _puzzleSize = widget.puzzleSize; + _initialPuzzleData = widget.initialPuzzleData; + _puzzleType = widget.puzzleType; + _riveController = widget.riveController; + super.initState(); + } + + @override + Widget build(BuildContext context) { + ref.listen(puzzleNotifierProvider(_solverClient), (previous, PuzzleState next) { + if (next is PuzzleSolved) { + // TODO: Add celebration + } + if (next is PuzzleInitializing) { + setState(() { + _isStartPressed = true; + }); + } + }); + + var fontSize = 64.0; + var boardSize = 400.0; + + var spacing = 5; + var eachBoxSize = (boardSize / _puzzleSize) - (spacing * (_puzzleSize - 1)); + + return Stack( + children: [ + Scaffold( + backgroundColor: Theme.of(context).backgroundColor, + body: Stack( + children: [ + Row( + children: [ + const Spacer(), + AnimatedDash( + boardSize: boardSize / 1.5, + riveController: _riveController, + onInit: (_) => setState(() {}), + ), + ], + ), + SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + // crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row(), + // Text( + // _puzzleType, + // style: const TextStyle( + // fontSize: 18, + // fontWeight: FontWeight.w500, + // color: Colors.white, + // ), + // ), + const SizedBox(height: 8), + const Text( + 'Puzzle Challenge', + style: TextStyle( + fontSize: 36, + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + const SizedBox(height: 16), + MovesTilesWidget(solverClient: _solverClient), + const SizedBox(height: 16), + const TimerWidget(fontSize: 36), + const SizedBox(height: 36), + PuzzleWidget( + solverClient: _solverClient, + boardSize: boardSize, + eachBoxSize: eachBoxSize, + initialPuzzleData: _initialPuzzleData, + fontSize: fontSize, + kInitialSpeed: kInitialSpeed, + ), + const SizedBox(height: 36), + GameButtonWidget( + solverClient: _solverClient, + initialPuzzleData: _initialPuzzleData, + ), + const SizedBox(height: 100), + ], + ), + ), + ], + ), + ), + CountdownOverlay( + isStartPressed: _isStartPressed, + onFinish: () { + ref.read(timerNotifierProvider.notifier).startTimer(); + setState(() { + _isStartPressed = false; + }); + }, + initialSpeed: kInitialSpeed, + ), + ], + ); + } +} diff --git a/lib/screens/solo/solo_screen_small.dart b/lib/screens/solo/solo_screen_small.dart new file mode 100644 index 0000000..9f3fb80 --- /dev/null +++ b/lib/screens/solo/solo_screen_small.dart @@ -0,0 +1,145 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:my_flutter_puzzle/application/states/puzzle_state.dart'; +import 'package:my_flutter_puzzle/providers.dart'; +import 'package:my_flutter_puzzle/res/puzzle_constants.dart'; +import 'package:my_flutter_puzzle/screens/puzzle/top_bar.dart'; +import 'package:my_flutter_puzzle/utils/puzzle_solver.dart'; +import 'package:my_flutter_puzzle/widgets/solo_screen/countdown_overlay.dart'; +import 'package:my_flutter_puzzle/widgets/solo_screen/solo_screen_export.dart'; +import 'package:rive/rive.dart'; + +import '../../models/puzzle_data.dart'; + +class SoloScreenSmall extends ConsumerStatefulWidget { + const SoloScreenSmall({ + required this.solverClient, + required this.initialPuzzleData, + required this.puzzleSize, + required this.puzzleType, + required this.riveController, + Key? key, + }) : super(key: key); + + final PuzzleSolverClient solverClient; + final PuzzleData initialPuzzleData; + final int puzzleSize; + final String puzzleType; + final RiveAnimationController riveController; + + @override + ConsumerState createState() => _SoloScreenLargeState(); +} + +class _SoloScreenLargeState extends ConsumerState { + late final PuzzleSolverClient _solverClient; + late final int _puzzleSize; + late final PuzzleData _initialPuzzleData; + late final String _puzzleType; + late final RiveAnimationController _riveController; + bool _isStartPressed = false; + + @override + void initState() { + _solverClient = widget.solverClient; + _puzzleSize = widget.puzzleSize; + _initialPuzzleData = widget.initialPuzzleData; + _puzzleType = widget.puzzleType; + _riveController = widget.riveController; + super.initState(); + } + + @override + Widget build(BuildContext context) { + ref.listen(puzzleNotifierProvider(_solverClient), (previous, PuzzleState next) { + if (next is PuzzleSolved) { + // TODO: Add celebration + } + if (next is PuzzleInitializing) { + setState(() { + _isStartPressed = true; + }); + } + }); + + var fontSize = 48.0; + var boardSize = 300.0; + + var spacing = 3; + var eachBoxSize = (boardSize / _puzzleSize) - (spacing * (_puzzleSize - 1)); + + return Stack( + children: [ + Scaffold( + backgroundColor: Theme.of(context).backgroundColor, + body: Stack( + children: [ + Row( + children: [ + const Spacer(), + AnimatedDash( + boardSize: boardSize / 1.5, + riveController: _riveController, + onInit: (_) => setState(() {}), + padding: const EdgeInsets.only(right: 16.0, bottom: 30), + ), + ], + ), + SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row(), + const SizedBox(height: 30), + const Text( + 'Puzzle Challenge', + style: TextStyle( + fontSize: 32, + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + const SizedBox(height: 8), + MovesTilesWidget( + solverClient: _solverClient, + fontSize: 22, + ), + const SizedBox(height: 8), + const TimerWidget(fontSize: 24), + PuzzleWidget( + solverClient: _solverClient, + boardSize: boardSize, + eachBoxSize: eachBoxSize, + initialPuzzleData: _initialPuzzleData, + fontSize: fontSize, + kInitialSpeed: kInitialSpeed, + borderRadius: 16, + ), + const SizedBox(height: 24), + GameButtonWidget( + solverClient: _solverClient, + initialPuzzleData: _initialPuzzleData, + padding: const EdgeInsets.only(top: 10.0, bottom: 9.0), + width: 130, + ), + const SizedBox(height: 100), + ], + ), + ), + ], + ), + ), + CountdownOverlay( + isStartPressed: _isStartPressed, + onFinish: () { + ref.read(timerNotifierProvider.notifier).startTimer(); + setState(() { + _isStartPressed = false; + }); + }, + initialSpeed: kInitialSpeed, + ), + ], + ); + } +} diff --git a/lib/utils/color_brightness.dart b/lib/utils/color_brightness.dart new file mode 100644 index 0000000..4695c2d --- /dev/null +++ b/lib/utils/color_brightness.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; + +extension ColorBrightness on Color { + Color darken([double amount = .1]) { + assert(amount >= 0 && amount <= 1); + + final hsl = HSLColor.fromColor(this); + final hslDark = hsl.withLightness((hsl.lightness - amount).clamp(0.0, 1.0)); + + return hslDark.toColor(); + } + + Color lighten([double amount = .1]) { + assert(amount >= 0 && amount <= 1); + + final hsl = HSLColor.fromColor(this); + final hslLight = + hsl.withLightness((hsl.lightness + amount).clamp(0.0, 1.0)); + + return hslLight.toColor(); + } +} \ No newline at end of file diff --git a/lib/utils/extensions/string_extensions.dart b/lib/utils/extensions/string_extensions.dart new file mode 100644 index 0000000..2b725a8 --- /dev/null +++ b/lib/utils/extensions/string_extensions.dart @@ -0,0 +1,9 @@ +extension StringExtension on String { + String get pngPath { + return 'assets/images/$this.png'; + } + + String get rivePath { + return 'assets/rive/$this.riv'; + } +} diff --git a/lib/utils/helpers.dart b/lib/utils/helpers.dart new file mode 100644 index 0000000..85570a8 --- /dev/null +++ b/lib/utils/helpers.dart @@ -0,0 +1,37 @@ +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:my_flutter_puzzle/utils/puzzle_solver.dart'; + +class Helpers { + static Map createOffset( + List board, + PuzzleSolverClient solverClient, + ) { + Map offsetMap = {}; + int j = 0; + + log('BOARD: $board'); + + for (int i = 0; i < board.length; i++) { + var _puzzleSize = solverClient.size; + final xMod = i % _puzzleSize; + double x = xMod / (_puzzleSize - 1); + + if (x % i == 0 && i != 0) j++; + int yMod = j % _puzzleSize; + double y = yMod / (_puzzleSize - 1); + + offsetMap.addEntries([ + MapEntry( + board[i], + FractionalOffset(x, y), + ) + ]); + } + + log('INITIAL OFFSET MAP: $offsetMap'); + + return offsetMap; + } +} diff --git a/lib/utils/image_splitter.dart b/lib/utils/image_splitter.dart new file mode 100644 index 0000000..3dbdea6 --- /dev/null +++ b/lib/utils/image_splitter.dart @@ -0,0 +1,74 @@ +import 'dart:developer'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:image/image.dart' as image_lib; +import 'package:image_picker/image_picker.dart'; +import 'package:palette_generator/palette_generator.dart'; +import 'package:tuple/tuple.dart'; + +class ImageSplitter { + List splitImage(Map mapData) { + List input = mapData['input']; + int size = mapData['size']; + // convert image to image from image package + image_lib.Image image = image_lib.decodeImage(input)!; + + int x = 0, y = 0; + int width = (image.width / size).round(); + int height = (image.height / size).round(); + + // split image to parts + List parts = []; + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + parts.add(image_lib.copyCrop(image, x, y, width, height)); + x += width; + } + x = 0; + y += height; + } + + // convert image from image package to Image Widget to display + List output = []; + for (var img in parts) { + output.add(Image.memory(Uint8List.fromList(image_lib.encodeJpg(img)))); + } + + return output; + } + + Future getImagePalette(ImageProvider imageProvider) async { + final paletteGenerator = await PaletteGenerator.fromImageProvider(imageProvider); + return paletteGenerator; + } + + Future?> getImage({required ImagePicker picker}) async { + final pickedFile = await picker.pickImage(source: ImageSource.gallery); + + if (pickedFile != null) { + // final imageFile = File(pickedFile.path); + // final image = Image.file(File(pickedFile.path)); + final image = Image.memory(await pickedFile.readAsBytes()); + final palette = await getImagePalette(image.image); + // final PaletteGenerator palette = await getImagePalette( + // FileImage(imageFile), + // ); + final bytes = await pickedFile.readAsBytes(); + log('Image properties retrieved'); + return Tuple3(image, bytes, palette); + } + + return null; + } + + Future> runSplitterIsolate(List input, int size) async { + final Map data = { + 'input': input, + 'size': size, + }; + List splitImages = await compute(splitImage, data); + + return splitImages; + } +} diff --git a/lib/utils/puzzle_solver.dart b/lib/utils/puzzle_solver.dart new file mode 100644 index 0000000..6fbeb9b --- /dev/null +++ b/lib/utils/puzzle_solver.dart @@ -0,0 +1,385 @@ +import 'dart:collection'; +import 'dart:math'; + +import 'package:collection/collection.dart'; +import 'package:flutter/foundation.dart'; +import 'package:tuple/tuple.dart'; + +class Node { + final List> board; + final Node? previous; + final int heuristic; + final int depth; + + Node({ + required this.board, + required this.previous, + required this.heuristic, + required this.depth, + }); + + Node getNodeState() { + return Node( + board: board, + previous: previous, + heuristic: heuristic, + depth: depth, + ); + } + + List generateChildren({required int size}) { + late int x; + late int y; + + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + if (board[i][j] == 0) { + x = i; + y = j; + } + } + } + + // print('x: $x, y: $y'); + + List> valList = [ + [x, y - 1], + [x, y + 1], + [x - 1, y], + [x + 1, y] + ]; + + List children = []; + + for (var child in valList) { + if ((child[0] >= 0) && (child[0] < size) && (child[1] >= 0) && (child[1] < size)) { + List> childBoard = []; + // ERROR (FIXED): This board is getting modified + // print('------------------------------'); + // print('BOARD: $board'); + // print('------------------------------'); + for (var row in board) { + childBoard.add([...row]); + } + // print('***********************************'); + // print('CHILD: $childBoard'); + // print('***********************************'); + childBoard[x][y] = childBoard[child[0]][child[1]]; + childBoard[child[0]][child[1]] = 0; + final solverClient = PuzzleSolverClient(size: size); + + final childNode = Node( + board: childBoard, + previous: this, + heuristic: solverClient.nodeManhattan(childBoard), + depth: depth + 1, + ); + children.add(childNode); + } + } + return children; + } +} + +class PuzzleSolverClient { + int size; + + PuzzleSolverClient({this.size = 3}); + + set setSize(int newSize) => size = newSize; + + List> createRandomBoard({bool solvable = true}) { + List> board = []; + for (int i = 0; i < size; i++) { + List temp = []; + for (int j = 0; j < size; j++) { + temp.add(0); + } + board.add(temp); + } + + List s = []; + for (int i = 0; i < (size * size); i++) { + s.add(i); + } + + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + int item = s[Random().nextInt(s.length)]; + board[i][j] = item; + s.remove(item); + } + } + + if (solvable) { + if (!(isSolvable(board))) { + if ((board[2][1] != 0) && (board[2][2] != 0)) { + var temp = board[2][1]; + board[2][1] = board[2][2]; + board[2][2] = temp; + } else { + var temp = board[0][0]; + board[0][0] = board[0][1]; + board[0][1] = temp; + } + } + } + + return board; + } + + void plainPrint(List> b) { + for (var i in b) { + for (var j in i) { + print('$j '); + } + } + } + + int nodeManhattan(List> board) { + int sum = 0; + int n = board.length; + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + int x = (board[i][j] - 1) ~/ n; + int y = (board[i][j] - 1) % n; + if (board[i][j] != 0) { + sum += (x - i).abs() + (y - j).abs(); + } + } + } + return sum; + } + + int manhattan(List> board, Set goalNums) { + int sum = 0; + int count = 0; + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + if (goalNums.contains(board[i][j])) { + int x = (board[i][j] - 1) ~/ size; + int y = ((board[i][j] - 1) % size).toInt(); + + sum += (x - i).abs() + (y - j).abs(); + } + } + } + return sum; + } + + List toTuple(List> board) { + List lst = []; + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + lst.add(board[i][j]); + } + } + + return lst; + } + + bool isGoal(List> board, Set goalNums) { + int count = 0; + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + count += 1; + if ((goalNums.contains(count)) && (board[i][j] != count)) { + return false; + } + } + } + return true; + } + + bool isSolvable(board) { + int n = board.length; + List lst = []; + bool blankOnEven = false; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (board[i][j] != 0) { + lst.add(board[i][j]); + } else { + if (i % 2 == 0) { + blankOnEven = true; + } + } + } + } + int inversions = 0; + for (int i = 0; i < lst.length; i++) { + for (int j = i + 1; j < lst.length; j++) { + if (lst[i] > lst[j]) { + inversions += 1; + } + } + } + + if (n % 2 == 1) { + if (inversions % 2 == 1) { + return false; + } else { + return true; + } + } + + if (((inversions % 2 == 0) && blankOnEven) || ((inversions % 2 == 1) && !blankOnEven)) { + return false; + } + return true; + } + + List> inOrderGoalStates(n) { + List> goalStates = []; + int c = 1; + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + if ((i == (n - 1)) && (j == n - 1)) { + break; + } + Set set1 = {c}; + goalStates.add(set1); + if ((i > 0) || (j > 0)) { + goalStates.last = (goalStates.last).union(goalStates[goalStates.length - 2]); + } + c += 1; + } + } + return goalStates; + } + + List> rowColGoalStates(n) { + List> goalStates = []; + for (int layer = 0; layer < n - 2; layer++) { + for (int i = 0; i < n - layer; i++) { + Set set1 = {(n * layer) + i + 1}; + goalStates.add(set1); + if (goalStates.length > 1) { + goalStates.last = (goalStates.last).union(goalStates[goalStates.length - 2]); + } + } + + for (int i = 0; i < n - layer - 1; i++) { + Set set1 = {(n + 1) + (i * n)}; + goalStates.add(set1); + if (goalStates.length > 1) { + goalStates.last = (goalStates.last).union(goalStates[goalStates.length - 2]); + } + } + } + Set set1 = {}; + + for (int i = 1; i < n * n; i++) { + set1.add(i); + } + + goalStates.add(set1); + + return (goalStates); + } + + List convertTo1D(List> board) { + List board1D = []; + for (var row in board) { + board1D.addAll(row); + } + return board1D; + } + + List> convertTo2D(List board1D) { + List> chunks = []; + int chunkSize = size; + + for (var i = 0; i < board1D.length; i += chunkSize) { + chunks.add(board1D.sublist(i, i + chunkSize > board1D.length ? board1D.length : i + chunkSize)); + } + + return chunks; + } + + List>? runner(List> board) { + final queue = HeapPriorityQueue>((a, b) => a.item1.compareTo(b.item1)); + HashSet> visited = HashSet>(); + if (kDebugMode) { + print('BOARD:'); + } + for (var element in board) { + if (kDebugMode) { + print(element); + } + } + + List> goalStates = []; + int count = 1; + String flag = 'A_STAR'; + + if (flag == 'A_STAR') { + goalStates = rowColGoalStates(size); + goalStates = [goalStates.last]; + } else if (flag == "HUMAN") { + goalStates = rowColGoalStates(size); + } + + int hScaleFactor = 3; + int currGoal = 0; + Node root = Node( + board: board, + previous: null, + heuristic: manhattan(board, goalStates[currGoal]), + depth: 0, + ); + queue.add(Tuple2(root.depth + hScaleFactor * root.heuristic, root)); + while (queue.isNotEmpty) { + count += 1; + final node = queue.removeFirst().item2; + if (isGoal(node.board, goalStates[currGoal])) { + queue.clear(); + currGoal += 1; + + if (currGoal == goalStates.length) { + Node? temp = node; + List>> boards = []; + while (temp != null) { + boards.add(temp.board); + temp = temp.previous; + } + boards = boards.reversed.toList(); + List> finalBoards = []; + for (var i in boards) { + finalBoards.add(convertTo1D(i)); + } + return finalBoards; + } + + root = Node( + board: board, + previous: null, + heuristic: manhattan(board, goalStates[currGoal]), + depth: 0, + ); + + queue.add(Tuple2(root.depth + hScaleFactor * root.heuristic, root)); + } + + var t = toTuple(node.board); + visited.add(t); + + final children = node.generateChildren(size: size); + for (Node child in children) { + var tt = toTuple(child.board); + + bool isSetEqual = visited.any((value) { + return value.equals(tt); + }); + + if (!isSetEqual) { + queue.add( + Tuple2(child.depth + hScaleFactor * manhattan(child.board, goalStates[currGoal]), child), + ); + // print('${queue.first.item1}, ${queue.first.item2.board}'); + } + } + } + return null; + } +} diff --git a/lib/utils/responsive_layout.dart b/lib/utils/responsive_layout.dart new file mode 100644 index 0000000..3a5765d --- /dev/null +++ b/lib/utils/responsive_layout.dart @@ -0,0 +1,38 @@ +import 'dart:developer'; + +import 'package:flutter/material.dart'; + +class ResponsiveLayout extends StatelessWidget { + const ResponsiveLayout({ + Key? key, + required this.largeChild, + this.mediumChild, + this.smallChild, + this.largeBreakPoint = 1200.0, + this.mediumBreakPoint = 580.0, + }) : super(key: key); + + final Widget largeChild; + + final Widget? mediumChild; + + final Widget? smallChild; + + final double largeBreakPoint; + + final double mediumBreakPoint; + + @override + Widget build(BuildContext context) { + final smallestWidth = MediaQuery.of(context).size.width; + log('width: $smallestWidth'); + return AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + child: smallestWidth >= largeBreakPoint + ? largeChild + : smallestWidth >= mediumBreakPoint + ? mediumChild ?? largeChild + : smallChild ?? mediumChild ?? largeChild, + ); + } +} diff --git a/lib/utils/validators.dart b/lib/utils/validators.dart new file mode 100644 index 0000000..6f5d109 --- /dev/null +++ b/lib/utils/validators.dart @@ -0,0 +1,43 @@ +class Validators { + static String? validateName({required String? name}) { + if (name == null || name.isEmpty) { + return 'Name can\'t be empty'; + } + + return null; + } + + static String? validateEmail({required String? email}) { + RegExp emailRegExp = RegExp( + r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?)*$"); + + if (email == null || email.isEmpty) { + return 'Email can\'t be empty'; + } else if (!emailRegExp.hasMatch(email)) { + return 'Enter a correct email'; + } + + return null; + } + + static String? validatePassword({required String? password}) { + if (password == null || password.isEmpty) { + return 'Password can\'t be empty'; + } else if (password.length < 6) { + return 'Enter a password with length at least 6'; + } + + return null; + } + + static String? validateConfirmPassword( + {required String? password, required String? confirmPassword}) { + if (password == null || + confirmPassword == null || + password != confirmPassword) { + return 'Enter the same password'; + } + + return null; + } +} diff --git a/lib/widgets/animated_grid.dart b/lib/widgets/animated_grid.dart new file mode 100644 index 0000000..1cbdab7 --- /dev/null +++ b/lib/widgets/animated_grid.dart @@ -0,0 +1,308 @@ +import 'package:flutter/material.dart'; + +class AnimatedGrid extends StatefulWidget { + const AnimatedGrid({ + Key? key, + required this.number, + required this.offsetList, + required this.onTap, + required this.color, + required this.puzzleSize, + }) : super(key: key); + + final Function onTap; + final List number; + final List offsetList; + final Color color; + final int puzzleSize; + + @override + State createState() => _AnimatedGridState(); +} + +class _AnimatedGridState extends State { + late List _offsetList; + + @override + void initState() { + _offsetList = widget.offsetList; + super.initState(); + } + + @override + Widget build(BuildContext context) { + var screenSize = MediaQuery.of(context).size; + var boardSize = screenSize.width * 0.4; + + var spacing = 4; + var eachBoxSize = + (boardSize / widget.puzzleSize) - (spacing * (widget.puzzleSize - 1)); + + return SizedBox( + height: boardSize, + width: boardSize, + child: Stack( + children: [ + for (int i = 0; i < widget.offsetList.length; i++) + widget.number[i] != 0 + ? AnimatedAlign( + alignment: widget.offsetList[i], + duration: const Duration(seconds: 1), + curve: Curves.easeInOut, + child: GestureDetector( + onTap: () => widget.onTap(i), + child: Card( + elevation: 4, + color: widget.color, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + child: SizedBox( + height: eachBoxSize, + width: eachBoxSize, + child: Center( + child: Text( + widget.number[i].toString(), + style: const TextStyle( + fontSize: 60, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + ), + ), + ), + ), + ) + : const SizedBox(), + ], + ), + ); + + // ----------------------------- + // Working implementation: + // ----------------------------- + // return SizedBox( + // height: screenSize.height * 0.7, + // width: screenSize.width * 0.4, + // child: GridView.builder( + // gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + // crossAxisCount: puzzleSize, + // ), + // itemCount: number.length, + // itemBuilder: (context, index) { + // return number[index] != 0 + // ? Padding( + // padding: const EdgeInsets.all(2.0), + // child: GestureDetector( + // onTap: () => onTap(index), + // child: AnimatedPositioned( + // duration: Duration(milliseconds: 600), + // child: Container( + // height: 20, + // width: 20, + // color: Colors.orange, + // ), + // ), + // ), + // ) + // // ? MouseRegion( + // // child: Padding( + // // padding: const EdgeInsets.all(2.0), + // // child: GestureDetector( + // // onTap: () => onTap(index), + // // child: Card( + // // elevation: 4, + // // color: color, + // // // dark -> 0xFF14407a + // // // light -> 0xFF43b9fd + // // shape: RoundedRectangleBorder( + // // borderRadius: BorderRadius.circular(20), + // // ), + // // child: SizedBox( + // // height: 20, + // // width: 20, + // // child: Center( + // // child: Text( + // // number[index].toString(), + // // style: const TextStyle( + // // fontSize: 60, + // // fontWeight: FontWeight.bold, + // // color: Colors.white, + // // ), + // // ), + // // ), + // // ), + // // ), + // // ), + // // ), + // // ) + // : const SizedBox(); + // // }, + // // ), + // // ); + // } + } +} + +// class AnimatedGrid extends StatefulWidget { +// const AnimatedGrid({ +// Key? key, +// required this.number, +// required this.offsetList, +// required this.onTap, +// required this.color, +// required this.puzzleSize, +// }) : super(key: key); + +// final Function onTap; +// final List number; +// final List offsetList; +// final Color color; +// final int puzzleSize; + +// @override +// State createState() => _AnimatedGridState(); +// } + +// class _AnimatedGridState extends State { +// // FractionalOffset offset = FractionalOffset(0, 0); +// late final List _numberList; +// // late final int _puzzleSize; + +// @override +// void initState() { +// _numberList = widget.number; +// // _puzzleSize = widget.puzzleSize; +// // print(_numberList); +// // print(widget.offsetList); +// super.initState(); +// } + +// // calculateOffset() { +// // List offsetList = []; + +// // for (int i = 0; i < _puzzleSize; i++) { +// // int yMod = i % _puzzleSize; +// // double y = yMod / (_puzzleSize - 1); +// // for (int j = 0; j < _puzzleSize; j++) { +// // final xMod = _numberList[i][j] % _puzzleSize; +// // double x = xMod / (_puzzleSize - 1); +// // offsetList.add(FractionalOffset(x, y)); +// // } +// // } +// // } + +// @override +// Widget build(BuildContext context) { +// var screenSize = MediaQuery.of(context).size; +// var boardSize = screenSize.width * 0.4; + +// var spacing = 4; +// var eachBoxSize = +// (boardSize / widget.puzzleSize) - (spacing * (widget.puzzleSize - 1)); + +// return SizedBox( +// height: boardSize, +// width: boardSize, +// child: Stack( +// children: [ +// for (var offset in widget.offsetList) +// AnimatedAlign( +// alignment: offset, +// duration: const Duration(seconds: 1), +// curve: Curves.easeInOut, +// child: Container( +// height: eachBoxSize, +// width: eachBoxSize, +// color: Colors.black, +// ), +// ), +// // AnimatedAlign( +// // alignment: FractionalOffset(1, 0), +// // duration: Duration(seconds: 1), +// // curve: Curves.easeInOut, +// // child: Container( +// // height: eachBoxSize, +// // width: eachBoxSize, +// // color: Colors.black, +// // ), +// // ), +// // AnimatedAlign( +// // alignment: FractionalOffset(0.5, 1), +// // duration: Duration(seconds: 1), +// // curve: Curves.easeInOut, +// // child: Container( +// // height: eachBoxSize, +// // width: eachBoxSize, +// // color: Colors.black, +// // ), +// // ), +// ], +// ), +// ); + +// // ----------------------------- +// // Working implementation: +// // ----------------------------- +// // return SizedBox( +// // height: screenSize.height * 0.7, +// // width: screenSize.width * 0.4, +// // child: GridView.builder( +// // gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( +// // crossAxisCount: puzzleSize, +// // ), +// // itemCount: number.length, +// // itemBuilder: (context, index) { +// // return number[index] != 0 +// // ? Padding( +// // padding: const EdgeInsets.all(2.0), +// // child: GestureDetector( +// // onTap: () => onTap(index), +// // child: AnimatedPositioned( +// // duration: Duration(milliseconds: 600), +// // child: Container( +// // height: 20, +// // width: 20, +// // color: Colors.orange, +// // ), +// // ), +// // ), +// // ) +// // // ? MouseRegion( +// // // child: Padding( +// // // padding: const EdgeInsets.all(2.0), +// // // child: GestureDetector( +// // // onTap: () => onTap(index), +// // // child: Card( +// // // elevation: 4, +// // // color: color, +// // // // dark -> 0xFF14407a +// // // // light -> 0xFF43b9fd +// // // shape: RoundedRectangleBorder( +// // // borderRadius: BorderRadius.circular(20), +// // // ), +// // // child: SizedBox( +// // // height: 20, +// // // width: 20, +// // // child: Center( +// // // child: Text( +// // // number[index].toString(), +// // // style: const TextStyle( +// // // fontSize: 60, +// // // fontWeight: FontWeight.bold, +// // // color: Colors.white, +// // // ), +// // // ), +// // // ), +// // // ), +// // // ), +// // // ), +// // // ), +// // // ) +// // : const SizedBox(); +// // }, +// // ), +// // ); +// } +// } diff --git a/lib/widgets/grid.dart b/lib/widgets/grid.dart new file mode 100644 index 0000000..ad1f1ce --- /dev/null +++ b/lib/widgets/grid.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; + +class Grid extends StatelessWidget { + const Grid({ + Key? key, + required this.number, + required this.onTap, + required this.color, + required this.puzzleSize, + }) : super(key: key); + + final Function onTap; + final List number; + final Color color; + final int puzzleSize; + + @override + Widget build(BuildContext context) { + var screenSize = MediaQuery.of(context).size; + + return SizedBox( + height: screenSize.height * 0.7, + width: screenSize.width * 0.4, + child: GridView.builder( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: puzzleSize, + ), + itemCount: number.length, + itemBuilder: (context, index) { + return number[index] != 0 + ? MouseRegion( + child: Padding( + padding: const EdgeInsets.all(2.0), + child: GestureDetector( + onTap: () => onTap(index), + child: Card( + elevation: 4, + color: color, + // dark -> 0xFF14407a + // light -> 0xFF43b9fd + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + child: SizedBox( + height: 20, + width: 20, + child: Center( + child: Text( + number[index].toString(), + style: const TextStyle( + fontSize: 60, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + ), + ), + ), + ), + ), + ) + : const SizedBox(); + }, + ), + ); + } +} diff --git a/lib/widgets/login_widgets/name_field.dart b/lib/widgets/login_widgets/name_field.dart new file mode 100644 index 0000000..576cac0 --- /dev/null +++ b/lib/widgets/login_widgets/name_field.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; +import 'package:my_flutter_puzzle/res/palette.dart'; + +class NameField extends StatefulWidget { + final FocusNode focusNode; + final Function(String) onChange; + + const NameField({ + Key? key, + required this.focusNode, + required this.onChange, + }) : super(key: key); + + @override + State createState() => _NameFieldState(); +} + +class _NameFieldState extends State { + late final TextEditingController _nameTextController; + late final FocusNode _nameFocusNode; + + @override + void initState() { + super.initState(); + _nameTextController = TextEditingController(); + _nameFocusNode = widget.focusNode; + } + + @override + Widget build(BuildContext context) { + return TextField( + controller: _nameTextController, + focusNode: _nameFocusNode, + style: const TextStyle( + fontSize: 24, + color: Palette.crimson, + ), + cursorColor: Palette.violet, + decoration: InputDecoration( + border: const UnderlineInputBorder(), + focusedBorder: const UnderlineInputBorder( + borderSide: BorderSide( + color: Palette.violet, + width: 3, + ), + ), + enabledBorder: UnderlineInputBorder( + borderSide: BorderSide( + color: Palette.violet.withOpacity(0.5), + width: 2, + ), + ), + hintText: 'Enter your name', + hintStyle: TextStyle( + fontSize: 22, + color: Palette.violet.withOpacity(0.2), + ), + ), + onChanged: (value) => widget.onChange(value), + ); + } +} diff --git a/lib/widgets/login_widgets/start_game_widget.dart b/lib/widgets/login_widgets/start_game_widget.dart new file mode 100644 index 0000000..f3e3e9a --- /dev/null +++ b/lib/widgets/login_widgets/start_game_widget.dart @@ -0,0 +1,72 @@ +// import 'package:flutter/material.dart'; +// import 'package:flutter_riverpod/flutter_riverpod.dart'; +// import 'package:my_flutter_puzzle/application/states/anonymous_auth_state.dart'; +// import 'package:my_flutter_puzzle/providers.dart'; +// import 'package:my_flutter_puzzle/res/palette.dart'; +// import 'package:my_flutter_puzzle/screens/menu_screen.dart'; +// import 'package:my_flutter_puzzle/screens/puzzle_screen.dart'; +// import 'package:my_flutter_puzzle/widgets/login_widgets/start_game_widget/start_game_button.dart'; + +// class StartGameWidget extends ConsumerWidget { +// const StartGameWidget({ +// Key? key, +// required String nameString, +// }) : _nameString = nameString, +// super(key: key); + +// final String _nameString; + +// @override +// Widget build(BuildContext context, WidgetRef ref) { +// ref.listen(anonymousAuthNotificationProvider, (previous, next) { +// if (next is StorageDone) { +// Navigator.of(context).pushAndRemoveUntil( +// MaterialPageRoute( +// builder: (context) => MenuScreen( +// userData: next.userData, +// ), +// ), +// (route) => false, +// ); +// } +// }); + +// return Consumer( +// builder: (context, ref, child) { +// final state = ref.watch(anonymousAuthNotificationProvider); + +// return state.when( +// () => StartGameButton( +// nameString: _nameString, +// ), +// processing: () => const CircularProgressIndicator( +// valueColor: AlwaysStoppedAnimation(Palette.violet), +// ), +// done: (_) => const CircularProgressIndicator( +// valueColor: AlwaysStoppedAnimation(Palette.violet), +// ), +// storingInfo: () => const CircularProgressIndicator( +// valueColor: AlwaysStoppedAnimation(Palette.violet), +// ), +// storageDone: (userData) => const Icon( +// Icons.check, +// size: 50, +// color: Palette.violet, +// ), +// // done: (_) => const Icon( +// // Icons.check, +// // size: 50, +// // color: Palette.violet, +// // ), +// error: (message) => Column( +// children: [ +// Text('$message'), +// const SizedBox(height: 16), +// StartGameButton(nameString: _nameString), +// ], +// ), +// ); +// }, +// ); +// } +// } diff --git a/lib/widgets/login_widgets/start_game_widget/start_game_button.dart b/lib/widgets/login_widgets/start_game_widget/start_game_button.dart new file mode 100644 index 0000000..fd84386 --- /dev/null +++ b/lib/widgets/login_widgets/start_game_widget/start_game_button.dart @@ -0,0 +1,39 @@ +// import 'package:flutter/material.dart'; +// import 'package:flutter_riverpod/flutter_riverpod.dart'; +// import 'package:my_flutter_puzzle/providers.dart'; +// import 'package:my_flutter_puzzle/res/palette.dart'; + +// class StartGameButton extends ConsumerWidget { +// const StartGameButton({ +// Key? key, +// required String nameString, +// }) : _nameString = nameString, +// super(key: key); + +// final String _nameString; + +// @override +// Widget build(BuildContext context, WidgetRef ref) { +// return SizedBox( +// width: double.maxFinite, +// child: ElevatedButton( +// style: ElevatedButton.styleFrom( +// primary: Palette.violet, +// onSurface: Palette.violet, +// ), +// onPressed: _nameString.isEmpty || _nameString.length < 3 +// ? null +// : () => ref +// .read(anonymousAuthNotificationProvider.notifier) +// .triggerAnonymousLogin(name: _nameString), +// child: const Padding( +// padding: EdgeInsets.all(16.0), +// child: Text( +// 'Start game', +// style: TextStyle(fontSize: 22), +// ), +// ), +// ), +// ); +// } +// } diff --git a/lib/widgets/menu_widgets/menu_widgets.dart b/lib/widgets/menu_widgets/menu_widgets.dart new file mode 100644 index 0000000..02b5ea8 --- /dev/null +++ b/lib/widgets/menu_widgets/menu_widgets.dart @@ -0,0 +1,2 @@ +export 'multiplayer_button.dart'; +export 'solo_button.dart'; \ No newline at end of file diff --git a/lib/widgets/menu_widgets/multiplayer_button.dart b/lib/widgets/menu_widgets/multiplayer_button.dart new file mode 100644 index 0000000..b243b67 --- /dev/null +++ b/lib/widgets/menu_widgets/multiplayer_button.dart @@ -0,0 +1,108 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:my_flutter_puzzle/models/user_info.dart'; +import 'package:my_flutter_puzzle/res/palette.dart'; + +class MultiplayerButton extends ConsumerStatefulWidget { + const MultiplayerButton({ + Key? key, + required this.myInfo, + required this.list, + }) : super(key: key); + + final UserData myInfo; + final List list; + + @override + ConsumerState createState() => _MultiplayerButtonState(); +} + +class _MultiplayerButtonState extends ConsumerState { + late final List numberList; + + @override + void initState() { + super.initState(); + numberList = widget.list; + numberList.shuffle(); + } + + @override + Widget build(BuildContext context) { + return SizedBox( + width: double.maxFinite, + child: OutlinedButton( + style: OutlinedButton.styleFrom( + primary: Palette.violet, + onSurface: Palette.violet, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(50), + ), + side: const BorderSide( + width: 2, + color: Palette.violet, + ), + ), + onPressed: () { + // ref + // .read(playerMatchingNotifierProvider.notifier) + // .triggerMatching(myInfo: widget.myInfo, numbers: numberList); + }, + child: const Padding( + padding: EdgeInsets.all(16.0), + child: Text( + 'Multiplayer', + style: TextStyle(fontSize: 22), + ), + ), + ), + ); + } +} + +// class MultiplayerButton extends StatefulWidget { +// final UserData myInfo; +// MultiplayerButton({ +// Key? key, +// required this.myInfo, +// }) : super(key: key); + +// @override +// State createState() => _MultiplayerButtonState(); +// } + +// class _MultiplayerButtonState extends State { +// final numberList = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + +// @override +// Widget build(BuildContext context) { +// return SizedBox( +// width: double.maxFinite, +// child: OutlinedButton( +// style: OutlinedButton.styleFrom( +// primary: Palette.violet, +// onSurface: Palette.violet, +// shape: RoundedRectangleBorder( +// borderRadius: BorderRadius.circular(50), +// ), +// side: const BorderSide( +// width: 2, +// color: Palette.violet, +// ), +// ), +// onPressed: () { +// ref.read(playerMatchingNotifierProvider.notifier).triggerMatching( +// myInfo: widget.myInfo, +// numbers: [14, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 15]); +// }, +// child: const Padding( +// padding: EdgeInsets.all(16.0), +// child: Text( +// 'Multiplayer', +// style: TextStyle(fontSize: 22), +// ), +// ), +// ), +// ); +// } +// } diff --git a/lib/widgets/menu_widgets/solo_button.dart b/lib/widgets/menu_widgets/solo_button.dart new file mode 100644 index 0000000..204ffab --- /dev/null +++ b/lib/widgets/menu_widgets/solo_button.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; +import 'package:my_flutter_puzzle/res/palette.dart'; +import 'package:my_flutter_puzzle/screens/puzzle_solo_screen.dart'; + +class SoloButton extends StatelessWidget { + const SoloButton({ + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return SizedBox( + width: double.maxFinite, + child: OutlinedButton( + style: OutlinedButton.styleFrom( + primary: Palette.violet, + onSurface: Palette.violet, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(50), + ), + side: const BorderSide( + width: 2, + color: Palette.violet, + ), + ), + onPressed: () { + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (context) => const PuzzleSoloScreen(), + ), + (route) => false, + ); + }, + child: const Padding( + padding: EdgeInsets.all(16.0), + child: Text( + 'Solo', + style: TextStyle(fontSize: 22), + ), + ), + ), + ); + } +} diff --git a/lib/widgets/photo_screen/image_viewer.dart b/lib/widgets/photo_screen/image_viewer.dart new file mode 100644 index 0000000..ebaf9aa --- /dev/null +++ b/lib/widgets/photo_screen/image_viewer.dart @@ -0,0 +1,199 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:my_flutter_puzzle/providers.dart'; + +// import 'package:my_flutter_puzzle/widgets/photo_screen/pick_image_button.dart'; +import 'package:palette_generator/palette_generator.dart'; + +class ImageViewer extends ConsumerWidget { + const ImageViewer({ + required this.imagePicker, + required this.puzzleSize, + required this.previousImage, + required this.previousPalette, + required this.imageSize, + Key? key, + }) : super(key: key); + + final ImagePicker imagePicker; + final int puzzleSize; + final Image? previousImage; + final PaletteGenerator? previousPalette; + final double imageSize; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Consumer( + builder: (context, ref, child) { + final state = ref.watch(imageSplitterNotifierProvider); + return state.maybeWhen( + () => const SizedBox(), + complete: (image, images, palette) => Padding( + padding: const EdgeInsets.only(right: 0.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + // border: Border.all(color: Colors.white, width: 3), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(20), + child: Image( + image: image.image, + height: imageSize, + width: imageSize, + ), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: SizedBox( + width: imageSize, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 4, + horizontal: 16, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Container( + width: imageSize / 5, + height: imageSize / 5, + decoration: BoxDecoration( + color: palette.colors.elementAt(0), + shape: BoxShape.circle, + border: Border.all( + color: Colors.white60, + width: 3, + ), + ), + ), + Container( + width: imageSize / 5, + height: imageSize / 5, + decoration: BoxDecoration( + color: palette.colors.elementAt(1), + shape: BoxShape.circle, + border: Border.all( + color: Colors.white60, + width: 3, + ), + ), + ), + Container( + width: imageSize / 5, + height: imageSize / 5, + decoration: BoxDecoration( + color: palette.colors.elementAt(2), + shape: BoxShape.circle, + border: Border.all( + color: Colors.white60, + width: 3, + ), + ), + ), + ], + ), + ), + ), + ), + const SizedBox(height: 16), + // PickImageButton( + // text: 'Pick Image', + // onTap: () => ref + // .read(imageSplitterNotifierProvider.notifier) + // .generateImages( + // picker: imagePicker, + // puzzleSize: puzzleSize, + // ), + // ), + ], + ), + ), + orElse: () => previousImage != null + ? Padding( + padding: const EdgeInsets.only(right: 0.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + // border: Border.all(color: Colors.white, width: 3), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(20), + child: Image( + image: previousImage!.image, + height: imageSize, + width: imageSize, + ), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: SizedBox( + width: imageSize, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 4, + horizontal: 16, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Container( + width: imageSize / 5, + height: imageSize / 5, + decoration: BoxDecoration( + color: previousPalette!.colors.elementAt(0), + shape: BoxShape.circle, + border: Border.all( + color: Colors.white60, + width: 3, + ), + ), + ), + Container( + width: imageSize / 5, + height: imageSize / 5, + decoration: BoxDecoration( + color: previousPalette!.colors.elementAt(1), + shape: BoxShape.circle, + border: Border.all( + color: Colors.white60, + width: 3, + ), + ), + ), + Container( + width: imageSize / 5, + height: imageSize / 5, + decoration: BoxDecoration( + color: previousPalette!.colors.elementAt(2), + shape: BoxShape.circle, + border: Border.all( + color: Colors.white60, + width: 3, + ), + ), + ), + ], + ), + ), + ), + ), + const SizedBox(height: 16), + ], + ), + ) + : const SizedBox(), + ); + }, + ); + } +} diff --git a/lib/widgets/photo_screen/pick_image_button.dart b/lib/widgets/photo_screen/pick_image_button.dart new file mode 100644 index 0000000..868b651 --- /dev/null +++ b/lib/widgets/photo_screen/pick_image_button.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +class PickImageButton extends ConsumerWidget { + const PickImageButton({ + Key? key, + required this.text, + required this.onTap, + this.padding = const EdgeInsets.only(top: 13.0, bottom: 12.0), + this.width = 145, + }) : super(key: key); + + final String text; + final Function()? onTap; + final EdgeInsets padding; + final double width; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return SizedBox( + width: width, + child: ElevatedButton( + style: ButtonStyle( + shape: MaterialStateProperty.all( + RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)), + ), + backgroundColor: MaterialStateProperty.resolveWith( + (Set states) { + if (states.contains(MaterialState.pressed)) { + return Theme.of(context).colorScheme.primary.withOpacity(0.5); + } else if (states.contains(MaterialState.disabled)) { + return Theme.of(context).colorScheme.primary.withOpacity(0.5); + } + + return Theme.of(context) + .colorScheme + .primary; // Use the component's default. + }, + ), + ), + onPressed: onTap, + child: Padding( + padding: padding, + child: Text( + text, + style: TextStyle( + fontSize: 16, + color: Colors.white.withOpacity(onTap == null ? 0.6 : 1), + ), + ), + ), + ), + ); + } +} diff --git a/lib/widgets/puzzle_widgets/moves_text.dart b/lib/widgets/puzzle_widgets/moves_text.dart new file mode 100644 index 0000000..1d70378 --- /dev/null +++ b/lib/widgets/puzzle_widgets/moves_text.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; + +class MovesText extends StatelessWidget { + const MovesText({ + Key? key, + required int moves, + required this.fontSize, + }) : _moves = moves, + super(key: key); + + final int _moves; + final double fontSize; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.fromLTRB(0, 8, 0, 24), + child: Text( + '$_moves Moves', + style: TextStyle( + fontSize: fontSize, + fontWeight: FontWeight.bold, + color: Colors.black, + ), + ), + ); + } +} diff --git a/lib/widgets/puzzle_widgets/player_text.dart b/lib/widgets/puzzle_widgets/player_text.dart new file mode 100644 index 0000000..726ea76 --- /dev/null +++ b/lib/widgets/puzzle_widgets/player_text.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; + +class PlayerText extends StatelessWidget { + final String displayName; + + const PlayerText({ + Key? key, + required this.displayName, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(top: 24.0), + child: Text( + displayName, + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Color(0xFF14407a), + letterSpacing: 2, + ), + ), + ); + } +} diff --git a/lib/widgets/puzzle_widgets/puzzle_widgets.dart b/lib/widgets/puzzle_widgets/puzzle_widgets.dart new file mode 100644 index 0000000..4906bbe --- /dev/null +++ b/lib/widgets/puzzle_widgets/puzzle_widgets.dart @@ -0,0 +1,2 @@ +export 'moves_text.dart'; +export 'player_text.dart'; \ No newline at end of file diff --git a/lib/widgets/puzzle_widgets/solo/timer_widget.dart b/lib/widgets/puzzle_widgets/solo/timer_widget.dart new file mode 100644 index 0000000..e7532fd --- /dev/null +++ b/lib/widgets/puzzle_widgets/solo/timer_widget.dart @@ -0,0 +1,73 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; + +class TimerWidget extends StatefulWidget { + const TimerWidget({ + Key? key, + required this.fontSize, + required this.color, + }) : super(key: key); + + final double fontSize; + final Color color; + + @override + _TimerWidgetState createState() => _TimerWidgetState(); +} + +class _TimerWidgetState extends State { + late final Timer _timer; + int _start = 0; + + void startTimer() { + _timer = Timer.periodic( + const Duration(seconds: 1), + (Timer timer) => setState(() => _start++), + ); + } + + String printDuration() { + var duration = Duration(seconds: _start); + String twoDigits(int n) => n.toString().padLeft(2, "0"); + String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60)); + String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60)); + return "${twoDigits(duration.inHours)}:$twoDigitMinutes:$twoDigitSeconds"; + } + + @override + void initState() { + startTimer(); + super.initState(); + } + + @override + void dispose() { + _timer.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Center( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + printDuration(), + style: TextStyle( + fontSize: widget.fontSize, + color: widget.color, + ), + ), + const SizedBox(width: 8), + Icon( + Icons.timer, + color: widget.color, + size: widget.fontSize * 1.3, + ) + ], + ), + ); + } +} diff --git a/lib/widgets/puzzle_widgets/timer_test_screen.dart b/lib/widgets/puzzle_widgets/timer_test_screen.dart new file mode 100644 index 0000000..be76c5e --- /dev/null +++ b/lib/widgets/puzzle_widgets/timer_test_screen.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:my_flutter_puzzle/providers.dart'; + +class TimerTestScreen extends ConsumerWidget { + const TimerTestScreen({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final state = ref.watch(timerNotifierProvider); + + return Scaffold( + body: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(state), + ElevatedButton( + onPressed: () => ref.read(timerNotifierProvider.notifier).startTimer(), + child: const Text('Start timer'), + ), + ElevatedButton( + onPressed: () => ref.read(timerNotifierProvider.notifier).stopTimer(), + child: const Text('Stop timer'), + ), + ], + ), + ); + } +} diff --git a/lib/widgets/solo_screen/animated_dash.dart b/lib/widgets/solo_screen/animated_dash.dart new file mode 100644 index 0000000..7415ec5 --- /dev/null +++ b/lib/widgets/solo_screen/animated_dash.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; +import 'package:rive/rive.dart'; + +class AnimatedDash extends StatelessWidget { + const AnimatedDash({ + Key? key, + required this.boardSize, + required Function(Artboard artboard) onInit, + required RiveAnimationController riveController, + EdgeInsets padding = const EdgeInsets.only(right: 56.0, bottom: 56), + }) : _riveController = riveController, + _onInit = onInit, + _padding = padding, + super(key: key); + + final double boardSize; + final Function(Artboard artboard) _onInit; + final RiveAnimationController _riveController; + final EdgeInsets _padding; + + @override + Widget build(BuildContext context) { + return Align( + alignment: Alignment.bottomCenter, + child: Padding( + padding: _padding, + // child: SizedBox( + // width: boardSize * 0.75, + // height: boardSize * 0.75, + // child: RiveAnimation.asset( + // 'assets/rive/dash.riv', + // fit: BoxFit.contain, + // antialiasing: true, + // controllers: [_riveController], + // onInit: _onInit, + // ), + // ), + ), + ); + } +} diff --git a/lib/widgets/solo_screen/countdown_overlay.dart b/lib/widgets/solo_screen/countdown_overlay.dart new file mode 100644 index 0000000..b97cdf2 --- /dev/null +++ b/lib/widgets/solo_screen/countdown_overlay.dart @@ -0,0 +1,88 @@ +import 'package:animated_text_kit/animated_text_kit.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +class CountdownOverlay extends ConsumerWidget { + const CountdownOverlay({ + required bool isStartPressed, + required Function() onFinish, + required int initialSpeed, + Key? key, + }) : _isStartPressed = isStartPressed, + _onFinish = onFinish, + _kInitialSpeed = initialSpeed, + super(key: key); + + final bool _isStartPressed; + final Function() _onFinish; + final int _kInitialSpeed; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Visibility( + visible: _isStartPressed, + child: Container( + height: double.maxFinite, + width: double.maxFinite, + color: Colors.black54, + child: Center( + child: Wrap( + children: [ + SizedBox( + width: 250.0, + child: DefaultTextStyle( + style: const TextStyle( + fontSize: 150.0, + color: Colors.white, + fontFamily: 'GoogleSans', + ), + child: AnimatedTextKit( + // totalRepeatCount: 1, + isRepeatingAnimation: false, + pause: const Duration(milliseconds: 0), + onFinished: _onFinish, + animatedTexts: [ + RotateAnimatedText( + '3', + transitionHeight: 100 * 2.5, + textStyle: const TextStyle( + color: Colors.white, + ), + duration: Duration(milliseconds: _kInitialSpeed), + ), + RotateAnimatedText( + '2', + transitionHeight: 100 * 2.5, + textStyle: const TextStyle( + color: Colors.white, + ), + duration: Duration(milliseconds: _kInitialSpeed), + ), + RotateAnimatedText( + '1', + transitionHeight: 100 * 2.5, + textStyle: const TextStyle( + color: Colors.white, + ), + duration: Duration(milliseconds: _kInitialSpeed), + ), + RotateAnimatedText( + 'GO!', + textStyle: const TextStyle( + fontSize: 120.0, + color: Colors.white, + ), + transitionHeight: 120 * 2.5, + duration: Duration(milliseconds: _kInitialSpeed), + ), + ], + ), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/widgets/solo_screen/countdown_widget.dart b/lib/widgets/solo_screen/countdown_widget.dart new file mode 100644 index 0000000..7c15370 --- /dev/null +++ b/lib/widgets/solo_screen/countdown_widget.dart @@ -0,0 +1,77 @@ +import 'package:animated_text_kit/animated_text_kit.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +class CountdownWidget extends ConsumerWidget { + const CountdownWidget({ + required bool isStartPressed, + required Function() onFinish, + required int initialSpeed, + Key? key, + }) : _isStartPressed = isStartPressed, + _onFinish = onFinish, + _kInitialSpeed = initialSpeed, + super(key: key); + + final bool _isStartPressed; + final Function() _onFinish; + final int _kInitialSpeed; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Visibility( + visible: _isStartPressed, + child: SizedBox( + width: 250.0, + child: DefaultTextStyle( + style: const TextStyle( + fontSize: 150.0, + color: Colors.white, + fontFamily: 'GoogleSans', + ), + child: AnimatedTextKit( + // totalRepeatCount: 1, + isRepeatingAnimation: false, + pause: const Duration(milliseconds: 0), + onFinished: _onFinish, + animatedTexts: [ + RotateAnimatedText( + '3', + transitionHeight: 100 * 2.5, + textStyle: const TextStyle( + color: Colors.white, + ), + duration: Duration(milliseconds: _kInitialSpeed), + ), + RotateAnimatedText( + '2', + transitionHeight: 100 * 2.5, + textStyle: const TextStyle( + color: Colors.white, + ), + duration: Duration(milliseconds: _kInitialSpeed), + ), + RotateAnimatedText( + '1', + transitionHeight: 100 * 2.5, + textStyle: const TextStyle( + color: Colors.white, + ), + duration: Duration(milliseconds: _kInitialSpeed), + ), + RotateAnimatedText( + 'GO!', + textStyle: const TextStyle( + fontSize: 120.0, + color: Colors.white, + ), + transitionHeight: 120 * 2.5, + duration: Duration(milliseconds: _kInitialSpeed), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/widgets/solo_screen/game_button_widget.dart b/lib/widgets/solo_screen/game_button_widget.dart new file mode 100644 index 0000000..e985f55 --- /dev/null +++ b/lib/widgets/solo_screen/game_button_widget.dart @@ -0,0 +1,101 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:my_flutter_puzzle/models/puzzle_data.dart'; +import 'package:my_flutter_puzzle/providers.dart'; +import 'package:my_flutter_puzzle/utils/puzzle_solver.dart'; + +import 'game_button_widget/puzzle_game_button.dart'; + +class GameButtonWidget extends StatelessWidget { + const GameButtonWidget({ + Key? key, + required PuzzleSolverClient solverClient, + required PuzzleData initialPuzzleData, + this.width = 145, + this.padding = const EdgeInsets.only(top: 13.0, bottom: 12.0), + }) : _solverClient = solverClient, + _initialPuzzleData = initialPuzzleData, + super(key: key); + + final PuzzleSolverClient _solverClient; + final PuzzleData _initialPuzzleData; + final double width; + final EdgeInsets padding; + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, ref, child) { + final state = ref.watch(puzzleNotifierProvider(_solverClient)); + + return state.when( + () => PuzzleGameButton( + text: 'Start Game', + onTap: () => ref + .read(puzzleNotifierProvider(_solverClient).notifier) + .initializePuzzle( + initialPuzzleData: _initialPuzzleData, + ), + padding: padding, + width: width, + ), + initializing: () => PuzzleGameButton( + text: 'Get ready...', + onTap: null, + padding: padding, + width: width, + ), + scrambling: (_) => PuzzleGameButton( + text: 'Get ready...', + onTap: null, + padding: padding, + width: width, + ), + current: (puzzleData) => PuzzleGameButton( + text: 'Restart', + onTap: () { + ref.read(timerNotifierProvider.notifier).stopTimer(); + ref + .read(puzzleNotifierProvider(_solverClient).notifier) + .restartPuzzle(); + }, + padding: padding, + width: width, + ), + computingSolution: (puzzleData) => PuzzleGameButton( + text: 'Processing...', + onTap: null, + padding: padding, + width: width, + ), + autoSolving: (puzzleData) => PuzzleGameButton( + text: 'Solving...', + onTap: null, + padding: padding, + width: width, + ), + solved: (puzzleData) => PuzzleGameButton( + text: 'Start Game', + onTap: () => ref + .read(puzzleNotifierProvider(_solverClient).notifier) + .initializePuzzle( + initialPuzzleData: puzzleData, + ), + padding: padding, + width: width, + ), + error: (_) => PuzzleGameButton( + text: 'Start Game', + onTap: () => ref + .read(puzzleNotifierProvider(_solverClient).notifier) + .initializePuzzle( + initialPuzzleData: _initialPuzzleData, + ), + padding: padding, + width: width, + ), + ); + }, + ); + } +} diff --git a/lib/widgets/solo_screen/game_button_widget/puzzle_game_button.dart b/lib/widgets/solo_screen/game_button_widget/puzzle_game_button.dart new file mode 100644 index 0000000..9c1b63e --- /dev/null +++ b/lib/widgets/solo_screen/game_button_widget/puzzle_game_button.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +class PuzzleGameButton extends ConsumerWidget { + const PuzzleGameButton({ + Key? key, + required this.text, + required this.onTap, + required this.width, + required this.padding, + }) : super(key: key); + + final String text; + final Function()? onTap; + final double width; + final EdgeInsets padding; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return SizedBox( + width: width, + child: ElevatedButton( + style: ButtonStyle( + shape: MaterialStateProperty.all( + RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)), + ), + backgroundColor: MaterialStateProperty.resolveWith( + (Set states) { + if (states.contains(MaterialState.pressed)) { + return Theme.of(context).colorScheme.primary.withOpacity(0.5); + } else if (states.contains(MaterialState.disabled)) { + return Theme.of(context).colorScheme.primary.withOpacity(0.5); + } + + return Theme.of(context) + .colorScheme + .primary; // Use the component's default. + }, + ), + ), + // style: ElevatedButton.styleFrom( + // onPrimary: Palette.blue, + // onSurface: Palette.blue, + // primary: Palette.blue, + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(30), + // ), + // ), + onPressed: onTap, + child: Padding( + padding: padding, + child: Text( + text, + style: TextStyle( + fontSize: 16, + color: Colors.white.withOpacity(onTap == null ? 0.6 : 1), + ), + ), + ), + ), + ); + } +} diff --git a/lib/widgets/solo_screen/moves_tiles_widget.dart b/lib/widgets/solo_screen/moves_tiles_widget.dart new file mode 100644 index 0000000..18278b9 --- /dev/null +++ b/lib/widgets/solo_screen/moves_tiles_widget.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:my_flutter_puzzle/providers.dart'; +import 'package:my_flutter_puzzle/utils/puzzle_solver.dart'; + +import 'moves_tiles_widget/moves_tiles_text.dart'; + +class MovesTilesWidget extends StatelessWidget { + const MovesTilesWidget({ + Key? key, + required PuzzleSolverClient solverClient, + this.fontSize = 24, + }) : _solverClient = solverClient, + super(key: key); + + final PuzzleSolverClient _solverClient; + final double fontSize; + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, ref, child) { + final state = ref.watch(puzzleNotifierProvider(_solverClient)); + + return state.when( + () => MovesTilesText( + moves: 0, + tiles: 0, + fontSize: fontSize, + ), + initializing: () => MovesTilesText( + moves: 0, + tiles: 0, + fontSize: fontSize, + ), + scrambling: (_) => MovesTilesText( + moves: 0, + tiles: 0, + fontSize: fontSize, + ), + current: (puzzleData) => MovesTilesText( + moves: puzzleData.moves, + tiles: puzzleData.tiles, + fontSize: fontSize, + ), + computingSolution: (puzzleData) => MovesTilesText( + moves: puzzleData.moves, + tiles: puzzleData.tiles, + fontSize: fontSize, + ), + autoSolving: (puzzleData) => MovesTilesText( + moves: puzzleData.moves, + tiles: puzzleData.tiles, + fontSize: fontSize, + ), + solved: (puzzleData) => MovesTilesText( + moves: puzzleData.moves, + tiles: puzzleData.tiles, + fontSize: fontSize, + ), + error: (_) => MovesTilesText( + moves: 0, + tiles: 0, + fontSize: fontSize, + ), + ); + }, + ); + } +} diff --git a/lib/widgets/solo_screen/moves_tiles_widget/moves_tiles_text.dart b/lib/widgets/solo_screen/moves_tiles_widget/moves_tiles_text.dart new file mode 100644 index 0000000..f0fee5f --- /dev/null +++ b/lib/widgets/solo_screen/moves_tiles_widget/moves_tiles_text.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; + +class MovesTilesText extends StatelessWidget { + final int moves; + final int tiles; + final double fontSize; + + const MovesTilesText({ + Key? key, + required this.moves, + required this.tiles, + required this.fontSize, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return RichText( + text: TextSpan( + style: TextStyle( + fontSize: fontSize, + // fontWeight: FontWeight.w500, + color: Colors.white, + ), + children: [ + TextSpan( + text: moves.toString(), + style: const TextStyle( + fontWeight: FontWeight.w600, + ), + ), + const TextSpan(text: ' Moves | '), + TextSpan( + text: tiles.toString(), + style: const TextStyle( + fontWeight: FontWeight.w600, + ), + ), + const TextSpan(text: ' Tiles'), + ], + ), + ); + } +} diff --git a/lib/widgets/solo_screen/puzzle_widget.dart b/lib/widgets/solo_screen/puzzle_widget.dart new file mode 100644 index 0000000..275b54c --- /dev/null +++ b/lib/widgets/solo_screen/puzzle_widget.dart @@ -0,0 +1,124 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:my_flutter_puzzle/models/puzzle_data.dart'; +import 'package:my_flutter_puzzle/providers.dart'; +import 'package:my_flutter_puzzle/utils/puzzle_solver.dart'; + +import 'puzzle_widget/puzzle_board.dart'; + +class PuzzleWidget extends StatelessWidget { + const PuzzleWidget({ + Key? key, + required PuzzleSolverClient solverClient, + required this.boardSize, + required this.eachBoxSize, + required PuzzleData initialPuzzleData, + required this.fontSize, + this.images, + this.borderRadius = 20, + required int kInitialSpeed, + }) : _solverClient = solverClient, + _initialPuzzleData = initialPuzzleData, + _kInitialSpeed = kInitialSpeed, + super(key: key); + + final PuzzleSolverClient _solverClient; + final double boardSize; + final double eachBoxSize; + final PuzzleData _initialPuzzleData; + final double fontSize; + final List? images; + final int _kInitialSpeed; + final double borderRadius; + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, ref, child) { + final state = ref.watch(puzzleNotifierProvider(_solverClient)); + + return state.when( + () => PuzzleBoard( + solverClient: _solverClient, + boardSize: boardSize, + eachBoxSize: eachBoxSize, + puzzleData: _initialPuzzleData, + fontSize: fontSize, + images: images, + isEnabled: false, + animationSpeed: _kInitialSpeed, + borderRadius: borderRadius, + ), + initializing: () => PuzzleBoard( + solverClient: _solverClient, + boardSize: boardSize, + eachBoxSize: eachBoxSize, + puzzleData: _initialPuzzleData, + fontSize: fontSize, + images: images, + isEnabled: false, + animationSpeed: _kInitialSpeed, + borderRadius: borderRadius, + ), + scrambling: (puzzleData) => PuzzleBoard( + solverClient: _solverClient, + boardSize: boardSize, + eachBoxSize: eachBoxSize, + puzzleData: puzzleData, + fontSize: fontSize, + images: images, + isEnabled: false, + animationSpeed: _kInitialSpeed, + borderRadius: borderRadius, + ), + current: (puzzleData) => PuzzleBoard( + solverClient: _solverClient, + boardSize: boardSize, + eachBoxSize: eachBoxSize, + puzzleData: puzzleData, + fontSize: fontSize, + images: images, + borderRadius: borderRadius, + ), + computingSolution: (puzzleData) => PuzzleBoard( + solverClient: _solverClient, + boardSize: boardSize, + eachBoxSize: eachBoxSize, + puzzleData: puzzleData, + fontSize: fontSize, + images: images, + borderRadius: borderRadius, + ), + autoSolving: (puzzleData) => PuzzleBoard( + solverClient: _solverClient, + boardSize: boardSize, + eachBoxSize: eachBoxSize, + puzzleData: puzzleData, + fontSize: fontSize, + images: images, + borderRadius: borderRadius, + ), + solved: (puzzleData) => PuzzleBoard( + solverClient: _solverClient, + boardSize: boardSize, + eachBoxSize: eachBoxSize, + puzzleData: puzzleData, + fontSize: fontSize, + images: images, + isEnabled: false, + borderRadius: borderRadius, + ), + error: (_) => PuzzleBoard( + solverClient: _solverClient, + boardSize: boardSize, + eachBoxSize: eachBoxSize, + puzzleData: _initialPuzzleData, + fontSize: fontSize, + images: images, + borderRadius: borderRadius, + ), + ); + }, + ); + } +} diff --git a/lib/widgets/solo_screen/puzzle_widget/puzzle_board.dart b/lib/widgets/solo_screen/puzzle_widget/puzzle_board.dart new file mode 100644 index 0000000..0bb3497 --- /dev/null +++ b/lib/widgets/solo_screen/puzzle_widget/puzzle_board.dart @@ -0,0 +1,98 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:my_flutter_puzzle/models/puzzle_data.dart'; +import 'package:my_flutter_puzzle/providers.dart'; +import 'package:my_flutter_puzzle/utils/puzzle_solver.dart'; + +class PuzzleBoard extends ConsumerWidget { + const PuzzleBoard({ + Key? key, + required this.solverClient, + required this.boardSize, + required this.eachBoxSize, + required this.puzzleData, + required this.fontSize, + this.images, + this.animationSpeed = 300, + this.isEnabled = true, + this.borderRadius = 20, + }) : super(key: key); + + final PuzzleSolverClient solverClient; + final double boardSize; + final double eachBoxSize; + final double fontSize; + final PuzzleData puzzleData; + final bool isEnabled; + final int animationSpeed; + final List? images; + final double borderRadius; + + // final int _animationSpeedInMilliseconds = 300; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return SizedBox( + height: boardSize, + width: boardSize, + child: Stack( + children: [ + for (int i = 0; i < puzzleData.offsetMap.length; i++) + puzzleData.offsetMap.entries.toList()[i].key != 0 + ? AnimatedAlign( + alignment: puzzleData.offsetMap.entries.toList()[i].value, + duration: Duration( + milliseconds: animationSpeed, + ), + curve: Curves.easeOut, + child: MouseRegion( + cursor: !isEnabled ? SystemMouseCursors.forbidden : SystemMouseCursors.click, + child: GestureDetector( + onTap: !isEnabled + ? null + : () => ref.read(puzzleNotifierProvider(solverClient).notifier).onClick( + index: puzzleData.board1D.indexOf(puzzleData.offsetMap.entries.toList()[i].key), + prev: puzzleData, + ), + child: images == null + ? Card( + elevation: 4, + color: Theme.of(context).colorScheme.primary.withOpacity(isEnabled ? 1 : 0.5), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(borderRadius), + ), + child: SizedBox( + height: eachBoxSize, + width: eachBoxSize, + child: Center( + child: Text( + puzzleData.offsetMap.entries.toList()[i].key.toString(), + style: TextStyle( + fontSize: fontSize, + fontWeight: FontWeight.bold, + color: Colors.white.withOpacity(isEnabled ? 1 : 0.5), + ), + ), + ), + )) + : SizedBox( + height: eachBoxSize, + width: eachBoxSize, + child: Opacity( + opacity: isEnabled ? 1 : 0.5, + child: ClipRRect( + borderRadius: BorderRadius.circular(borderRadius), + child: + images![int.parse(puzzleData.offsetMap.entries.toList()[i].key.toString()) - 1], + ), + ), + ), + ), + ), + ) + : const SizedBox(), + ], + ), + ); + } +} diff --git a/lib/widgets/solo_screen/solo_screen_export.dart b/lib/widgets/solo_screen/solo_screen_export.dart new file mode 100644 index 0000000..b3cb511 --- /dev/null +++ b/lib/widgets/solo_screen/solo_screen_export.dart @@ -0,0 +1,6 @@ +export 'animated_dash.dart'; +export 'countdown_widget.dart'; +export 'game_button_widget.dart'; +export 'moves_tiles_widget.dart'; +export 'puzzle_widget.dart'; +export 'timer_widget.dart'; \ No newline at end of file diff --git a/lib/widgets/solo_screen/timer_widget.dart b/lib/widgets/solo_screen/timer_widget.dart new file mode 100644 index 0000000..cf54451 --- /dev/null +++ b/lib/widgets/solo_screen/timer_widget.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:my_flutter_puzzle/providers.dart'; + +class TimerWidget extends StatelessWidget { + const TimerWidget({ + Key? key, + required this.fontSize, + }) : super(key: key); + + final double fontSize; + + @override + Widget build(BuildContext context) { + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + Consumer( + builder: (context, ref, child) { + final state = ref.watch(timerNotifierProvider); + + return Text( + state, + style: TextStyle( + fontSize: fontSize, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ); + }, + ), + const SizedBox(width: 8), + Icon( + Icons.timer, + color: Colors.white, + size: fontSize, + ) + ], + ); + } +} diff --git a/linux/.gitignore b/linux/.gitignore new file mode 100644 index 0000000..d3896c9 --- /dev/null +++ b/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt new file mode 100644 index 0000000..68814ce --- /dev/null +++ b/linux/CMakeLists.txt @@ -0,0 +1,116 @@ +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +set(BINARY_NAME "my_flutter_puzzle") +set(APPLICATION_ID "com.souvikbiswas.my_flutter_puzzle") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +# Configure build options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Application build +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) +apply_standard_settings(${BINARY_NAME}) +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) +add_dependencies(${BINARY_NAME} flutter_assemble) +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/linux/flutter/CMakeLists.txt b/linux/flutter/CMakeLists.txt new file mode 100644 index 0000000..33fd580 --- /dev/null +++ b/linux/flutter/CMakeLists.txt @@ -0,0 +1,87 @@ +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 0000000..e71a16d --- /dev/null +++ b/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,11 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + + +void fl_register_plugins(FlPluginRegistry* registry) { +} diff --git a/linux/flutter/generated_plugin_registrant.h b/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000..e0f0a47 --- /dev/null +++ b/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake new file mode 100644 index 0000000..2e1de87 --- /dev/null +++ b/linux/flutter/generated_plugins.cmake @@ -0,0 +1,23 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/linux/main.cc b/linux/main.cc new file mode 100644 index 0000000..e7c5c54 --- /dev/null +++ b/linux/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/linux/my_application.cc b/linux/my_application.cc new file mode 100644 index 0000000..f6802be --- /dev/null +++ b/linux/my_application.cc @@ -0,0 +1,104 @@ +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen* screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "my_flutter_puzzle"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } else { + gtk_window_set_title(window, "my_flutter_puzzle"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject* object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, + "flags", G_APPLICATION_NON_UNIQUE, + nullptr)); +} diff --git a/linux/my_application.h b/linux/my_application.h new file mode 100644 index 0000000..72271d5 --- /dev/null +++ b/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/macos/.gitignore b/macos/.gitignore new file mode 100644 index 0000000..746adbb --- /dev/null +++ b/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/macos/Flutter/Flutter-Debug.xcconfig b/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 0000000..4b81f9b --- /dev/null +++ b/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/Flutter-Release.xcconfig b/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 0000000..5caa9d1 --- /dev/null +++ b/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 0000000..0d56f51 --- /dev/null +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,12 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + +import path_provider_macos + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) +} diff --git a/macos/Podfile b/macos/Podfile new file mode 100644 index 0000000..22d9caa --- /dev/null +++ b/macos/Podfile @@ -0,0 +1,40 @@ +platform :osx, '10.12' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/macos/Podfile.lock b/macos/Podfile.lock new file mode 100644 index 0000000..7d7ad32 --- /dev/null +++ b/macos/Podfile.lock @@ -0,0 +1,511 @@ +PODS: + - abseil/algorithm (0.20200225.0): + - abseil/algorithm/algorithm (= 0.20200225.0) + - abseil/algorithm/container (= 0.20200225.0) + - abseil/algorithm/algorithm (0.20200225.0): + - abseil/base/config + - abseil/algorithm/container (0.20200225.0): + - abseil/algorithm/algorithm + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/base (0.20200225.0): + - abseil/base/atomic_hook (= 0.20200225.0) + - abseil/base/base (= 0.20200225.0) + - abseil/base/base_internal (= 0.20200225.0) + - abseil/base/bits (= 0.20200225.0) + - abseil/base/config (= 0.20200225.0) + - abseil/base/core_headers (= 0.20200225.0) + - abseil/base/dynamic_annotations (= 0.20200225.0) + - abseil/base/endian (= 0.20200225.0) + - abseil/base/errno_saver (= 0.20200225.0) + - abseil/base/exponential_biased (= 0.20200225.0) + - abseil/base/log_severity (= 0.20200225.0) + - abseil/base/malloc_internal (= 0.20200225.0) + - abseil/base/periodic_sampler (= 0.20200225.0) + - abseil/base/pretty_function (= 0.20200225.0) + - abseil/base/raw_logging_internal (= 0.20200225.0) + - abseil/base/spinlock_wait (= 0.20200225.0) + - abseil/base/throw_delegate (= 0.20200225.0) + - abseil/base/atomic_hook (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/base (0.20200225.0): + - abseil/base/atomic_hook + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/log_severity + - abseil/base/raw_logging_internal + - abseil/base/spinlock_wait + - abseil/meta/type_traits + - abseil/base/base_internal (0.20200225.0): + - abseil/base/config + - abseil/meta/type_traits + - abseil/base/bits (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/config (0.20200225.0) + - abseil/base/core_headers (0.20200225.0): + - abseil/base/config + - abseil/base/dynamic_annotations (0.20200225.0) + - abseil/base/endian (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/errno_saver (0.20200225.0): + - abseil/base/config + - abseil/base/exponential_biased (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/log_severity (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/malloc_internal (0.20200225.0): + - abseil/base/base + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/raw_logging_internal + - abseil/base/periodic_sampler (0.20200225.0): + - abseil/base/core_headers + - abseil/base/exponential_biased + - abseil/base/pretty_function (0.20200225.0) + - abseil/base/raw_logging_internal (0.20200225.0): + - abseil/base/atomic_hook + - abseil/base/config + - abseil/base/core_headers + - abseil/base/log_severity + - abseil/base/spinlock_wait (0.20200225.0): + - abseil/base/base_internal + - abseil/base/core_headers + - abseil/base/errno_saver + - abseil/base/throw_delegate (0.20200225.0): + - abseil/base/config + - abseil/base/raw_logging_internal + - abseil/container/common (0.20200225.0): + - abseil/meta/type_traits + - abseil/types/optional + - abseil/container/compressed_tuple (0.20200225.0): + - abseil/utility/utility + - abseil/container/container_memory (0.20200225.0): + - abseil/memory/memory + - abseil/utility/utility + - abseil/container/fixed_array (0.20200225.0): + - abseil/algorithm/algorithm + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/throw_delegate + - abseil/container/compressed_tuple + - abseil/memory/memory + - abseil/container/flat_hash_map (0.20200225.0): + - abseil/algorithm/container + - abseil/container/container_memory + - abseil/container/hash_function_defaults + - abseil/container/raw_hash_map + - abseil/memory/memory + - abseil/container/hash_function_defaults (0.20200225.0): + - abseil/base/config + - abseil/hash/hash + - abseil/strings/strings + - abseil/container/hash_policy_traits (0.20200225.0): + - abseil/meta/type_traits + - abseil/container/hashtable_debug_hooks (0.20200225.0): + - abseil/base/config + - abseil/container/hashtablez_sampler (0.20200225.0): + - abseil/base/base + - abseil/base/core_headers + - abseil/base/exponential_biased + - abseil/container/have_sse + - abseil/debugging/stacktrace + - abseil/memory/memory + - abseil/synchronization/synchronization + - abseil/utility/utility + - abseil/container/have_sse (0.20200225.0) + - abseil/container/inlined_vector (0.20200225.0): + - abseil/algorithm/algorithm + - abseil/base/core_headers + - abseil/base/throw_delegate + - abseil/container/inlined_vector_internal + - abseil/memory/memory + - abseil/container/inlined_vector_internal (0.20200225.0): + - abseil/base/core_headers + - abseil/container/compressed_tuple + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/types/span + - abseil/container/layout (0.20200225.0): + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/strings/strings + - abseil/types/span + - abseil/utility/utility + - abseil/container/raw_hash_map (0.20200225.0): + - abseil/base/throw_delegate + - abseil/container/container_memory + - abseil/container/raw_hash_set + - abseil/container/raw_hash_set (0.20200225.0): + - abseil/base/bits + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/container/common + - abseil/container/compressed_tuple + - abseil/container/container_memory + - abseil/container/hash_policy_traits + - abseil/container/hashtable_debug_hooks + - abseil/container/hashtablez_sampler + - abseil/container/have_sse + - abseil/container/layout + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/utility/utility + - abseil/debugging/debugging_internal (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/errno_saver + - abseil/base/raw_logging_internal + - abseil/debugging/demangle_internal (0.20200225.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/debugging/stacktrace (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/debugging/debugging_internal + - abseil/debugging/symbolize (0.20200225.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/malloc_internal + - abseil/base/raw_logging_internal + - abseil/debugging/debugging_internal + - abseil/debugging/demangle_internal + - abseil/hash/city (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/hash/hash (0.20200225.0): + - abseil/base/core_headers + - abseil/base/endian + - abseil/container/fixed_array + - abseil/hash/city + - abseil/meta/type_traits + - abseil/numeric/int128 + - abseil/strings/strings + - abseil/types/optional + - abseil/types/variant + - abseil/utility/utility + - abseil/memory (0.20200225.0): + - abseil/memory/memory (= 0.20200225.0) + - abseil/memory/memory (0.20200225.0): + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/meta (0.20200225.0): + - abseil/meta/type_traits (= 0.20200225.0) + - abseil/meta/type_traits (0.20200225.0): + - abseil/base/config + - abseil/numeric/int128 (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/strings/internal (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/raw_logging_internal + - abseil/meta/type_traits + - abseil/strings/str_format (0.20200225.0): + - abseil/strings/str_format_internal + - abseil/strings/str_format_internal (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/numeric/int128 + - abseil/strings/strings + - abseil/types/span + - abseil/strings/strings (0.20200225.0): + - abseil/base/base + - abseil/base/bits + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/raw_logging_internal + - abseil/base/throw_delegate + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/numeric/int128 + - abseil/strings/internal + - abseil/synchronization/graphcycles_internal (0.20200225.0): + - abseil/base/base + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/malloc_internal + - abseil/base/raw_logging_internal + - abseil/synchronization/kernel_timeout_internal (0.20200225.0): + - abseil/base/core_headers + - abseil/base/raw_logging_internal + - abseil/time/time + - abseil/synchronization/synchronization (0.20200225.0): + - abseil/base/atomic_hook + - abseil/base/base + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/malloc_internal + - abseil/base/raw_logging_internal + - abseil/debugging/stacktrace + - abseil/debugging/symbolize + - abseil/synchronization/graphcycles_internal + - abseil/synchronization/kernel_timeout_internal + - abseil/time/time + - abseil/time (0.20200225.0): + - abseil/time/internal (= 0.20200225.0) + - abseil/time/time (= 0.20200225.0) + - abseil/time/internal (0.20200225.0): + - abseil/time/internal/cctz (= 0.20200225.0) + - abseil/time/internal/cctz (0.20200225.0): + - abseil/time/internal/cctz/civil_time (= 0.20200225.0) + - abseil/time/internal/cctz/time_zone (= 0.20200225.0) + - abseil/time/internal/cctz/civil_time (0.20200225.0): + - abseil/base/config + - abseil/time/internal/cctz/time_zone (0.20200225.0): + - abseil/base/config + - abseil/time/internal/cctz/civil_time + - abseil/time/time (0.20200225.0): + - abseil/base/base + - abseil/base/core_headers + - abseil/base/raw_logging_internal + - abseil/numeric/int128 + - abseil/strings/strings + - abseil/time/internal/cctz/civil_time + - abseil/time/internal/cctz/time_zone + - abseil/types (0.20200225.0): + - abseil/types/any (= 0.20200225.0) + - abseil/types/bad_any_cast (= 0.20200225.0) + - abseil/types/bad_any_cast_impl (= 0.20200225.0) + - abseil/types/bad_optional_access (= 0.20200225.0) + - abseil/types/bad_variant_access (= 0.20200225.0) + - abseil/types/compare (= 0.20200225.0) + - abseil/types/optional (= 0.20200225.0) + - abseil/types/span (= 0.20200225.0) + - abseil/types/variant (= 0.20200225.0) + - abseil/types/any (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/types/bad_any_cast + - abseil/utility/utility + - abseil/types/bad_any_cast (0.20200225.0): + - abseil/base/config + - abseil/types/bad_any_cast_impl + - abseil/types/bad_any_cast_impl (0.20200225.0): + - abseil/base/config + - abseil/base/raw_logging_internal + - abseil/types/bad_optional_access (0.20200225.0): + - abseil/base/config + - abseil/base/raw_logging_internal + - abseil/types/bad_variant_access (0.20200225.0): + - abseil/base/config + - abseil/base/raw_logging_internal + - abseil/types/compare (0.20200225.0): + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/types/optional (0.20200225.0): + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/types/bad_optional_access + - abseil/utility/utility + - abseil/types/span (0.20200225.0): + - abseil/algorithm/algorithm + - abseil/base/core_headers + - abseil/base/throw_delegate + - abseil/meta/type_traits + - abseil/types/variant (0.20200225.0): + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/types/bad_variant_access + - abseil/utility/utility + - abseil/utility/utility (0.20200225.0): + - abseil/base/base_internal + - abseil/base/config + - abseil/meta/type_traits + - BoringSSL-GRPC (0.0.7): + - BoringSSL-GRPC/Implementation (= 0.0.7) + - BoringSSL-GRPC/Interface (= 0.0.7) + - BoringSSL-GRPC/Implementation (0.0.7): + - BoringSSL-GRPC/Interface (= 0.0.7) + - BoringSSL-GRPC/Interface (0.0.7) + - cloud_firestore (3.1.7): + - Firebase/CoreOnly (~> 8.10.0) + - Firebase/Firestore (~> 8.10.0) + - firebase_core + - FlutterMacOS + - Firebase/Auth (8.10.0): + - Firebase/CoreOnly + - FirebaseAuth (~> 8.10.0) + - Firebase/CoreOnly (8.10.0): + - FirebaseCore (= 8.10.0) + - Firebase/Firestore (8.10.0): + - Firebase/CoreOnly + - FirebaseFirestore (~> 8.10.0) + - firebase_auth (3.3.5): + - Firebase/Auth (~> 8.10.0) + - Firebase/CoreOnly (~> 8.10.0) + - firebase_core + - FlutterMacOS + - firebase_core (1.11.0): + - Firebase/CoreOnly (~> 8.10.0) + - FlutterMacOS + - FirebaseAuth (8.10.0): + - FirebaseCore (~> 8.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.6) + - GoogleUtilities/Environment (~> 7.6) + - GTMSessionFetcher/Core (~> 1.5) + - FirebaseCore (8.10.0): + - FirebaseCoreDiagnostics (~> 8.0) + - GoogleUtilities/Environment (~> 7.6) + - GoogleUtilities/Logger (~> 7.6) + - FirebaseCoreDiagnostics (8.10.0): + - GoogleDataTransport (~> 9.1) + - GoogleUtilities/Environment (~> 7.6) + - GoogleUtilities/Logger (~> 7.6) + - nanopb (~> 2.30908.0) + - FirebaseFirestore (8.10.0): + - abseil/algorithm (= 0.20200225.0) + - abseil/base (= 0.20200225.0) + - abseil/container/flat_hash_map (= 0.20200225.0) + - abseil/memory (= 0.20200225.0) + - abseil/meta (= 0.20200225.0) + - abseil/strings/strings (= 0.20200225.0) + - abseil/time (= 0.20200225.0) + - abseil/types (= 0.20200225.0) + - FirebaseCore (~> 8.0) + - "gRPC-C++ (~> 1.28.0)" + - leveldb-library (~> 1.22) + - nanopb (~> 2.30908.0) + - FlutterMacOS (1.0.0) + - GoogleDataTransport (9.1.2): + - GoogleUtilities/Environment (~> 7.2) + - nanopb (~> 2.30908.0) + - PromisesObjC (< 3.0, >= 1.2) + - GoogleUtilities/AppDelegateSwizzler (7.7.0): + - GoogleUtilities/Environment + - GoogleUtilities/Logger + - GoogleUtilities/Network + - GoogleUtilities/Environment (7.7.0): + - PromisesObjC (< 3.0, >= 1.2) + - GoogleUtilities/Logger (7.7.0): + - GoogleUtilities/Environment + - GoogleUtilities/Network (7.7.0): + - GoogleUtilities/Logger + - "GoogleUtilities/NSData+zlib" + - GoogleUtilities/Reachability + - "GoogleUtilities/NSData+zlib (7.7.0)" + - GoogleUtilities/Reachability (7.7.0): + - GoogleUtilities/Logger + - "gRPC-C++ (1.28.2)": + - "gRPC-C++/Implementation (= 1.28.2)" + - "gRPC-C++/Interface (= 1.28.2)" + - "gRPC-C++/Implementation (1.28.2)": + - abseil/container/inlined_vector (= 0.20200225.0) + - abseil/memory/memory (= 0.20200225.0) + - abseil/strings/str_format (= 0.20200225.0) + - abseil/strings/strings (= 0.20200225.0) + - abseil/types/optional (= 0.20200225.0) + - "gRPC-C++/Interface (= 1.28.2)" + - gRPC-Core (= 1.28.2) + - "gRPC-C++/Interface (1.28.2)" + - gRPC-Core (1.28.2): + - gRPC-Core/Implementation (= 1.28.2) + - gRPC-Core/Interface (= 1.28.2) + - gRPC-Core/Implementation (1.28.2): + - abseil/container/inlined_vector (= 0.20200225.0) + - abseil/memory/memory (= 0.20200225.0) + - abseil/strings/str_format (= 0.20200225.0) + - abseil/strings/strings (= 0.20200225.0) + - abseil/types/optional (= 0.20200225.0) + - BoringSSL-GRPC (= 0.0.7) + - gRPC-Core/Interface (= 1.28.2) + - gRPC-Core/Interface (1.28.2) + - GTMSessionFetcher/Core (1.7.0) + - leveldb-library (1.22.1) + - nanopb (2.30908.0): + - nanopb/decode (= 2.30908.0) + - nanopb/encode (= 2.30908.0) + - nanopb/decode (2.30908.0) + - nanopb/encode (2.30908.0) + - path_provider_macos (0.0.1): + - FlutterMacOS + - PromisesObjC (2.0.0) + +DEPENDENCIES: + - cloud_firestore (from `Flutter/ephemeral/.symlinks/plugins/cloud_firestore/macos`) + - firebase_auth (from `Flutter/ephemeral/.symlinks/plugins/firebase_auth/macos`) + - firebase_core (from `Flutter/ephemeral/.symlinks/plugins/firebase_core/macos`) + - FlutterMacOS (from `Flutter/ephemeral`) + - path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`) + +SPEC REPOS: + trunk: + - abseil + - BoringSSL-GRPC + - Firebase + - FirebaseAuth + - FirebaseCore + - FirebaseCoreDiagnostics + - FirebaseFirestore + - GoogleDataTransport + - GoogleUtilities + - "gRPC-C++" + - gRPC-Core + - GTMSessionFetcher + - leveldb-library + - nanopb + - PromisesObjC + +EXTERNAL SOURCES: + cloud_firestore: + :path: Flutter/ephemeral/.symlinks/plugins/cloud_firestore/macos + firebase_auth: + :path: Flutter/ephemeral/.symlinks/plugins/firebase_auth/macos + firebase_core: + :path: Flutter/ephemeral/.symlinks/plugins/firebase_core/macos + FlutterMacOS: + :path: Flutter/ephemeral + path_provider_macos: + :path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos + +SPEC CHECKSUMS: + abseil: 6c8eb7892aefa08d929b39f9bb108e5367e3228f + BoringSSL-GRPC: 8edf627ee524575e2f8d19d56f068b448eea3879 + cloud_firestore: eb1740ac012412da05df8bda653c4e274c9c7762 + Firebase: 44213362f1dcc52555b935dc925ed35ac55f1b20 + firebase_auth: c2854135cf3400b0305f0b3aad42a3b11b9b276d + firebase_core: b287279ad659853ebc4738fdfb6de183eb46ed9f + FirebaseAuth: 59a2d2b933b5b79e18fb1e6ad230c6abdaa73d26 + FirebaseCore: 04186597c095da37d90ff9fd3e53bc61a1ff2440 + FirebaseCoreDiagnostics: 56fb7216d87e0e6ec2feddefa9d8a392fe8b2c18 + FirebaseFirestore: 0e7321a508cc9de54316ec45400caba6b5719beb + FlutterMacOS: 57701585bf7de1b3fc2bb61f6378d73bbdea8424 + GoogleDataTransport: 629c20a4d363167143f30ea78320d5a7eb8bd940 + GoogleUtilities: e0913149f6b0625b553d70dae12b49fc62914fd1 + "gRPC-C++": 13d8ccef97d5c3c441b7e3c529ef28ebee86fad2 + gRPC-Core: 4afa11bfbedf7cdecd04de535a9e046893404ed5 + GTMSessionFetcher: 43748f93435c2aa068b1cbe39655aaf600652e91 + leveldb-library: 50c7b45cbd7bf543c81a468fe557a16ae3db8729 + nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96 + path_provider_macos: 160cab0d5461f0c0e02995469a98f24bdb9a3f1f + PromisesObjC: 68159ce6952d93e17b2dfe273b8c40907db5ba58 + +PODFILE CHECKSUM: c7161fcf45d4fd9025dc0f48a76d6e64e52f8176 + +COCOAPODS: 1.11.2 diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..87e45bb --- /dev/null +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,632 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 80C5B59A30FB17FE3DEE10A6 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EB0889F2AB7625D2CCB049E2 /* Pods_Runner.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* my_flutter_puzzle.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = my_flutter_puzzle.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 492F72614D1F6D658CBC1C35 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 56388CD1273F86315B2E746C /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 7A141616C2BAE133DB5059A0 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + EB0889F2AB7625D2CCB049E2 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 80C5B59A30FB17FE3DEE10A6 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + 4D4AA0EAF9D7B7A382DE376A /* Pods */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* my_flutter_puzzle.app */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + 4D4AA0EAF9D7B7A382DE376A /* Pods */ = { + isa = PBXGroup; + children = ( + 492F72614D1F6D658CBC1C35 /* Pods-Runner.debug.xcconfig */, + 7A141616C2BAE133DB5059A0 /* Pods-Runner.release.xcconfig */, + 56388CD1273F86315B2E746C /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + EB0889F2AB7625D2CCB049E2 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 66A7F88385355CFE4D1EBD16 /* [CP] Check Pods Manifest.lock */, + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + D805E79591080B3076BAC58D /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* my_flutter_puzzle.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; + 66A7F88385355CFE4D1EBD16 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + D805E79591080B3076BAC58D /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..d001af8 --- /dev/null +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/Runner.xcworkspace/contents.xcworkspacedata b/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..21a3cc1 --- /dev/null +++ b/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift new file mode 100644 index 0000000..d53ef64 --- /dev/null +++ b/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..a2ec33f --- /dev/null +++ b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000..3c4935a Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000..ed4cc16 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 0000000..483be61 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 0000000..bcbf36d Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png new file mode 100644 index 0000000..9c0a652 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 0000000..e71a726 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 0000000..8a31fe2 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/macos/Runner/Base.lproj/MainMenu.xib b/macos/Runner/Base.lproj/MainMenu.xib new file mode 100644 index 0000000..537341a --- /dev/null +++ b/macos/Runner/Base.lproj/MainMenu.xib @@ -0,0 +1,339 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 0000000..66ca51d --- /dev/null +++ b/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = my_flutter_puzzle + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.souvikbiswas.myFlutterPuzzle + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2022 com.souvikbiswas. All rights reserved. diff --git a/macos/Runner/Configs/Debug.xcconfig b/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 0000000..36b0fd9 --- /dev/null +++ b/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Release.xcconfig b/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 0000000..dff4f49 --- /dev/null +++ b/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Warnings.xcconfig b/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 0000000..42bcbf4 --- /dev/null +++ b/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements new file mode 100644 index 0000000..3ba6c12 --- /dev/null +++ b/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.client + + com.apple.security.network.server + + + diff --git a/macos/Runner/Info.plist b/macos/Runner/Info.plist new file mode 100644 index 0000000..4789daa --- /dev/null +++ b/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/macos/Runner/MainFlutterWindow.swift b/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 0000000..2722837 --- /dev/null +++ b/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController.init() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements new file mode 100644 index 0000000..7a2230d --- /dev/null +++ b/macos/Runner/Release.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.network.client + + com.apple.security.network.server + + + diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 0000000..5441d02 --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,834 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: d994c55943e77955bd2cd299c6c02c52ceffc3d21a26461f903f7e10abc05b82 + url: "https://pub.dev" + source: hosted + version: "34.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "426436ca1a3390b330aedf78dad8d70d8df3dc88cf4cabd870428fb02b00119e" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + animated_text_kit: + dependency: "direct main" + description: + name: animated_text_kit + sha256: "5e6bf696c1db94fae17ef0527a17cef82f3100daebb53a3bdeb1c34f6b2ccdcc" + url: "https://pub.dev" + source: hosted + version: "4.2.1" + archive: + dependency: transitive + description: + name: archive + sha256: b4e685f9b52b7c50b6433fa4008572f157ca5841f4dbddda513a112a8c23cb69 + url: "https://pub.dev" + source: hosted + version: "3.2.1" + args: + dependency: transitive + description: + name: args + sha256: "0bd9a99b6eb96f07af141f0eb53eace8983e8e5aa5de59777aca31684680ef22" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + async: + dependency: transitive + description: + name: async + sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + url: "https://pub.dev" + source: hosted + version: "2.10.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + build: + dependency: transitive + description: + name: build + sha256: c9b6c412967d7887e88efe1ffbfe0f31bfaf6a5a4b98eb8d59964977a90f2f9e + url: "https://pub.dev" + source: hosted + version: "2.2.1" + build_config: + dependency: transitive + description: + name: build_config + sha256: ad77deb6e9c143a3f550fbb4c5c1e0c6aadabe24274898d06b9526c61b9cf4fb + url: "https://pub.dev" + source: hosted + version: "1.0.0" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: "4e2dbfa914f99bca9c447ba5aaa572edf7335a71717944e4ab2e26ca079e7f79" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: "4666aef1d045c5ca15ebba63e400bd4e4fbd9f0dd06e791b51ab210da78a27f7" + url: "https://pub.dev" + source: hosted + version: "2.0.6" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: e090beee726671ff68747cb4d8c9151704864ec9a5dc88db9fe6441c24b3fa55 + url: "https://pub.dev" + source: hosted + version: "2.1.7" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: f4d6244cc071ba842c296cb1c4ee1b31596b9f924300647ac7a1445493471a3f + url: "https://pub.dev" + source: hosted + version: "7.2.3" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: b6c9911b2d670376918d5b8779bc27e0e612a94ec3ff0343689e991d8d0a3b8a + url: "https://pub.dev" + source: hosted + version: "8.1.4" + characters: + dependency: transitive + description: + name: characters + sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + url: "https://pub.dev" + source: hosted + version: "1.2.1" + charcode: + dependency: transitive + description: + name: charcode + sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 + url: "https://pub.dev" + source: hosted + version: "1.3.1" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: dd007e4fb8270916820a0d66e24f619266b60773cddd082c6439341645af2659 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: "66f86e916d285c1a93d3b79587d94bd71984a66aac4ff74e524cfa7877f1395c" + url: "https://pub.dev" + source: hosted + version: "0.3.5" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: bdb1ab29be158c4784d7f9b7b693745a0719c5899e31c01112782bb1cb871e80 + url: "https://pub.dev" + source: hosted + version: "4.1.0" + collection: + dependency: "direct main" + description: + name: collection + sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + url: "https://pub.dev" + source: hosted + version: "1.17.0" + collision: + dependency: transitive + description: + name: collision + sha256: "2bd2eb6a9dfc69bc9d92cb3401dcfadb3b8429a261102ec0e59a91c0f5fcab25" + url: "https://pub.dev" + source: hosted + version: "0.0.3" + convert: + dependency: transitive + description: + name: convert + sha256: f08428ad63615f96a27e34221c65e1a451439b5f26030f78d790f461c686d65d + url: "https://pub.dev" + source: hosted + version: "3.0.1" + crop: + dependency: "direct main" + description: + name: crop + sha256: d46d586cc71a56d18341b61138146a41b6e11a4b16d3b453b77c7eb24da847d4 + url: "https://pub.dev" + source: hosted + version: "0.5.2" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "552ffd2f851d4314958e6265452af1891959e00cd32b6d17452c5b836e27a0fa" + url: "https://pub.dev" + source: hosted + version: "0.3.2" + crypto: + dependency: transitive + description: + name: crypto + sha256: cf75650c66c0316274e21d7c43d3dea246273af5955bd94e8184837cd577575c + url: "https://pub.dev" + source: hosted + version: "3.0.1" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: "1989d917fbe8e6b39806207df5a3fdd3d816cbd090fac2ce26fb45e9a71476e5" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "6e8086e1d3c2f6bc15056ee248c4ddc48c2bc71287c0961bf801a08633ed4333" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "35d0f481d939de0d640b3db9a7aa36a52cd22054a798a73b4f50bdad5ce12678" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + file: + dependency: transitive + description: + name: file + sha256: b69516f2c26a5bcac4eee2e32512e1a5205ab312b3536c1c1227b2b942b5f9ad + url: "https://pub.dev" + source: hosted + version: "6.1.2" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: "6a2ef17156f4dc49684f9d99aaf4a93aba8ac49f5eac861755f5730ddf6e2e4e" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: b543301ad291598523947dc534aaddc5aaad597b709d2426d3a0e0d44c5cb493 + url: "https://pub.dev" + source: hosted + version: "1.0.4" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: "5c574d21b98ec92adab05ead10afd2b13ff5856c7ca79696edb338a9dd8ed387" + url: "https://pub.dev" + source: hosted + version: "2.0.5" + flutter_riverpod: + dependency: "direct main" + description: + name: flutter_riverpod + sha256: c57d053ca394a9f4ae3cb09654564700515c01ecf969e1bff0b5be479df4e3c4 + url: "https://pub.dev" + source: hosted + version: "1.0.3" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + font_awesome_flutter: + dependency: "direct main" + description: + name: font_awesome_flutter + sha256: "1f93e5799f0e6c882819e8393a05c6ca5226010f289190f2242ec19f3f0fdba5" + url: "https://pub.dev" + source: hosted + version: "9.2.0" + freezed: + dependency: "direct dev" + description: + name: freezed + sha256: cb2313af67206bcb836272ba55d8d608305d91776dc573bddb828bfe1a4d5959 + url: "https://pub.dev" + source: hosted + version: "1.1.1" + freezed_annotation: + dependency: "direct main" + description: + name: freezed_annotation + sha256: "1be037f901137bf2b9a0c309e9bf2694d6ec77687645211218191ade4f41a4b8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: "6d2930621b9377f6a4b7d260fce525d48dd77a334f0d5d4177d07b0dcb76c032" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + glob: + dependency: transitive + description: + name: glob + sha256: "8321dd2c0ab0683a91a51307fa844c6db4aa8e3981219b78961672aaab434658" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + graphs: + dependency: transitive + description: + name: graphs + sha256: ae0b3d956ff324c6f8671f08dcb2dbd71c99cdbf2aa3ca63a14190c47aa6679c + url: "https://pub.dev" + source: hosted + version: "2.1.0" + http: + dependency: transitive + description: + name: http + sha256: "2ed163531e071c2c6b7c659635112f24cb64ecbebf6af46b550d536c0b1aa112" + url: "https://pub.dev" + source: hosted + version: "0.13.4" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: bfb651625e251a88804ad6d596af01ea903544757906addcb2dcdf088b5ea185 + url: "https://pub.dev" + source: hosted + version: "3.0.1" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: e362d639ba3bc07d5a71faebb98cde68c05bfbcfbbb444b60b6f60bb67719185 + url: "https://pub.dev" + source: hosted + version: "4.0.0" + image: + dependency: "direct main" + description: + name: image + sha256: "02bafd3b4f399bfeb10034deba9753d93b55ce41cd0c4d3d8b355626f80e5b32" + url: "https://pub.dev" + source: hosted + version: "3.1.3" + image_picker: + dependency: "direct main" + description: + name: image_picker + sha256: "28769e99d4a61b810a5b15c3d7a1dbdaf3b48273e5d26fbf7ad4d72f3a222dc3" + url: "https://pub.dev" + source: hosted + version: "0.8.4+10" + image_picker_for_web: + dependency: transitive + description: + name: image_picker_for_web + sha256: "837609ccdb538b6d255808f6920179af004f2fecd1eb529fb14d313c289a9333" + url: "https://pub.dev" + source: hosted + version: "2.1.6" + image_picker_platform_interface: + dependency: transitive + description: + name: image_picker_platform_interface + sha256: "32eeb07cc4cac86ff61d5f4efb5756098e17841411ad010f380a61837da9eaf6" + url: "https://pub.dev" + source: hosted + version: "2.4.4" + io: + dependency: transitive + description: + name: io + sha256: "0d4c73c3653ab85bf696d51a9657604c900a370549196a91f33e4c39af760852" + url: "https://pub.dev" + source: hosted + version: "1.0.3" + isolated_worker: + dependency: "direct main" + description: + name: isolated_worker + sha256: a848f249c7255d323455d7e5e932024e5e6138f09403a568fd763c2aaf78cb1b + url: "https://pub.dev" + source: hosted + version: "0.1.0" + js: + dependency: transitive + description: + name: js + sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + url: "https://pub.dev" + source: hosted + version: "0.6.5" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "2639efc0237c7b71c6584696c0847ea4e4733ddaf571ae9c79d5295e8ae17272" + url: "https://pub.dev" + source: hosted + version: "4.4.0" + lints: + dependency: transitive + description: + name: lints + sha256: a2c3d198cb5ea2e179926622d433331d8b58374ab8f29cdda6e863bd62fd369c + url: "https://pub.dev" + source: hosted + version: "1.0.1" + logging: + dependency: transitive + description: + name: logging + sha256: "293ae2d49fd79d4c04944c3a26dfd313382d5f52e821ec57119230ae16031ad4" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + matcher: + dependency: transitive + description: + name: matcher + sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + url: "https://pub.dev" + source: hosted + version: "0.12.13" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + meta: + dependency: transitive + description: + name: meta + sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + url: "https://pub.dev" + source: hosted + version: "1.8.0" + mime: + dependency: transitive + description: + name: mime + sha256: fd5f81041e6a9fc9b9d7fa2cb8a01123f9f5d5d49136e06cb9dc7d33689529f4 + url: "https://pub.dev" + source: hosted + version: "1.0.1" + package_config: + dependency: transitive + description: + name: package_config + sha256: a4d5ede5ca9c3d88a2fef1147a078570c861714c806485c596b109819135bc12 + url: "https://pub.dev" + source: hosted + version: "2.0.2" + palette_generator: + dependency: "direct main" + description: + name: palette_generator + sha256: "6a44587e4c4e4ca670f89840615bdd7ca3de708805ccee0e2519cda9bb42dc98" + url: "https://pub.dev" + source: hosted + version: "0.3.3" + path: + dependency: transitive + description: + name: path + sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + url: "https://pub.dev" + source: hosted + version: "1.8.2" + path_provider: + dependency: "direct main" + description: + name: path_provider + sha256: e92dee4d38a9044605cb3fb253e9b46eb9375dfcad4515d0379b44ac90797568 + url: "https://pub.dev" + source: hosted + version: "2.0.9" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: "8b759fb6c74955931e87f550cc9e890b0cccb7ef8e710943973efeaa9695c54d" + url: "https://pub.dev" + source: hosted + version: "2.0.12" + path_provider_ios: + dependency: transitive + description: + name: path_provider_ios + sha256: "943b76e54056386432cdc2731cb303e2f580346b61a1fc73819721767be72309" + url: "https://pub.dev" + source: hosted + version: "2.0.8" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: "1e109f4df28bd95eab71e323008b53d19c4d633bc1ab05b577518773474e9621" + url: "https://pub.dev" + source: hosted + version: "2.1.5" + path_provider_macos: + dependency: transitive + description: + name: path_provider_macos + sha256: "0adeb313e1f2c3fc52baeeee59b0fe9c2d1f7da56fd96a9234e1702ec653a453" + url: "https://pub.dev" + source: hosted + version: "2.0.5" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "3dc0d51b07f85fec3746d9f4e8d31c73bb173cafa2e763f03f8df2e8d1878882" + url: "https://pub.dev" + source: hosted + version: "2.0.3" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: "366ad4e3541ea707f859e7148d4d5aba67d589d7936cee04a05c464a277eeb27" + url: "https://pub.dev" + source: hosted + version: "2.0.5" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: "1a914995d4ef10c94ff183528c120d35ed43b5eaa8713fc6766a9be4570782e2" + url: "https://pub.dev" + source: hosted + version: "4.4.0" + platform: + dependency: transitive + description: + name: platform + sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "075f927ebbab4262ace8d0b283929ac5410c0ac4e7fc123c76429564facfb757" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + pool: + dependency: transitive + description: + name: pool + sha256: "05955e3de2683e1746222efd14b775df7131139e07695dc8e24650f6b4204504" + url: "https://pub.dev" + source: hosted + version: "1.5.0" + process: + dependency: transitive + description: + name: process + sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + url: "https://pub.dev" + source: hosted + version: "4.2.4" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: b5a5fcc6425ea43704852ba4453ba94b08c2226c63418a260240c3a054579014 + url: "https://pub.dev" + source: hosted + version: "2.1.0" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: "3686efe4a4613a4449b1a4ae08670aadbd3376f2e78d93e3f8f0919db02a7256" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + quiver: + dependency: transitive + description: + name: quiver + sha256: "616b691d1c8f5c53b7b39ce3542f6a25308d7900bf689d0210e72a644a10387e" + url: "https://pub.dev" + source: hosted + version: "3.0.1+1" + rive: + dependency: "direct main" + description: + name: rive + sha256: "46cccb2ad44033ad34448f5dc2b7381860694d5d2b658dc800913f525dc1e24e" + url: "https://pub.dev" + source: hosted + version: "0.8.1" + riverpod: + dependency: transitive + description: + name: riverpod + sha256: e7f097159b9512f5953ff544164c19057f45ce28fd0cb971fc4cad1f7b28217d + url: "https://pub.dev" + source: hosted + version: "1.0.3" + shelf: + dependency: transitive + description: + name: shelf + sha256: c240984c924796e055e831a0a36db23be8cb04f170b26df572931ab36418421d + url: "https://pub.dev" + source: hosted + version: "1.2.0" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: fd84910bf7d58db109082edf7326b75322b8f186162028482f53dc892f00332d + url: "https://pub.dev" + source: hosted + version: "1.0.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_gen: + dependency: transitive + description: + name: source_gen + sha256: d5aa894fcfa327e19196d6f4f733597a10b42a7e55c8cb2b0dd60d7e7d27dccf + url: "https://pub.dev" + source: hosted + version: "1.2.1" + source_span: + dependency: transitive + description: + name: source_span + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + url: "https://pub.dev" + source: hosted + version: "1.9.1" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" + source: hosted + version: "1.11.0" + state_notifier: + dependency: transitive + description: + name: state_notifier + sha256: "8fe42610f179b843b12371e40db58c9444f8757f8b69d181c97e50787caed289" + url: "https://pub.dev" + source: hosted + version: "0.7.2+1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: ed464977cb26a1f41537e177e190c67223dbd9f4f683489b6ab2e5d211ec564e + url: "https://pub.dev" + source: hosted + version: "2.0.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + url: "https://pub.dev" + source: hosted + version: "0.4.16" + timing: + dependency: transitive + description: + name: timing + sha256: c386d07d7f5efc613479a7c4d9d64b03710b03cfaa7e8ad5f2bfb295a1f0dfad + url: "https://pub.dev" + source: hosted + version: "1.0.0" + tuple: + dependency: "direct main" + description: + name: tuple + sha256: fe3ae4f0dca3f9aac0888e2e0d117b642ce283a82d7017b54136290c0a3b0dd3 + url: "https://pub.dev" + source: hosted + version: "2.0.0" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: "53bdf7e979cfbf3e28987552fd72f637e63f3c8724c9e56d9246942dc2fa36ee" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + url_strategy: + dependency: "direct main" + description: + name: url_strategy + sha256: "42b68b42a9864c4d710401add17ad06e28f1c1d5500c93b98c431f6b0ea4ab87" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + watcher: + dependency: transitive + description: + name: watcher + sha256: e42dfcc48f67618344da967b10f62de57e04bae01d9d3af4c2596f3712a88c99 + url: "https://pub.dev" + source: hosted + version: "1.0.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: "0c2ada1b1aeb2ad031ca81872add6be049b8cb479262c6ad3c4b0f9c24eaab2f" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + win32: + dependency: transitive + description: + name: win32 + sha256: cde1e6d546d8cfd0b3c72bc6f29d980fa629d1cb107f38e2a039ca5d10d79e41 + url: "https://pub.dev" + source: hosted + version: "2.4.1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "060b6e1c891d956f72b5ac9463466c37cce3fa962a921532fc001e86fe93438e" + url: "https://pub.dev" + source: hosted + version: "0.2.0+1" + xml: + dependency: transitive + description: + name: xml + sha256: baa23bcba1ba4ce4b22c0c7a1d9c861e7015cb5169512676da0b85138e72840c + url: "https://pub.dev" + source: hosted + version: "5.3.1" + yaml: + dependency: transitive + description: + name: yaml + sha256: "3cee79b1715110341012d27756d9bae38e650588acd38d3f3c610822e1337ace" + url: "https://pub.dev" + source: hosted + version: "3.1.0" +sdks: + dart: ">=2.18.0 <3.0.0" + flutter: ">=2.8.0" diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..6128abe --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,44 @@ +name: my_flutter_puzzle +description: A new Flutter project. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev +version: 0.6.0+16 + +environment: + sdk: ">=2.15.1 <3.0.0" +dependencies: + flutter: + sdk: flutter + cupertino_icons: ^1.0.2 + url_strategy: ^0.2.0 + flutter_riverpod: ^1.0.3 + freezed_annotation: ^1.1.0 + collection: ^1.15.0 + tuple: ^2.0.0 + isolated_worker: ^0.1.0 + rive: ^0.8.1 + animated_text_kit: ^4.2.1 + image: ^3.1.1 + palette_generator: ^0.3.3 + path_provider: ^2.0.8 + image_picker: ^0.8.4+5 + font_awesome_flutter: ^9.2.0 + crop: ^0.5.2 + +dev_dependencies: + flutter_test: + sdk: flutter + freezed: ^1.1.1 + build_runner: ^2.1.7 + flutter_lints: ^1.0.0 +flutter: + uses-material-design: true + assets: + - assets/rive/dash.riv + - assets/images/default_image.png + - assets/images/puzzle_side_image.png + fonts: + - family: GoogleSans + fonts: + - asset: fonts/GoogleSans-Regular.ttf + - asset: fonts/GoogleSans-Medium.ttf + - asset: fonts/GoogleSans-Bold.ttf \ No newline at end of file diff --git a/release_notes.txt b/release_notes.txt new file mode 100644 index 0000000..fdf6fbe --- /dev/null +++ b/release_notes.txt @@ -0,0 +1 @@ +Web app link: https://flutterpuzzle.codemagic.app \ No newline at end of file diff --git a/screenshots/photo_mode.gif b/screenshots/photo_mode.gif new file mode 100644 index 0000000..681d93d Binary files /dev/null and b/screenshots/photo_mode.gif differ diff --git a/screenshots/puzzle_multiplayer.png b/screenshots/puzzle_multiplayer.png new file mode 100644 index 0000000..2b8ed96 Binary files /dev/null and b/screenshots/puzzle_multiplayer.png differ diff --git a/screenshots/puzzle_multiplayer_1.png b/screenshots/puzzle_multiplayer_1.png new file mode 100644 index 0000000..9ad7c7f Binary files /dev/null and b/screenshots/puzzle_multiplayer_1.png differ diff --git a/screenshots/puzzle_normal.png b/screenshots/puzzle_normal.png new file mode 100644 index 0000000..4f144ec Binary files /dev/null and b/screenshots/puzzle_normal.png differ diff --git a/screenshots/puzzle_photo.png b/screenshots/puzzle_photo.png new file mode 100644 index 0000000..2e836c4 Binary files /dev/null and b/screenshots/puzzle_photo.png differ diff --git a/test/my_queue_test.dart b/test/my_queue_test.dart new file mode 100644 index 0000000..30fae35 --- /dev/null +++ b/test/my_queue_test.dart @@ -0,0 +1,13 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:tuple/tuple.dart'; + +void main() { + test('queue test', () { + final queue = HeapPriorityQueue>((a, b) => a.item1.compareTo(b.item1)); + queue.add(const Tuple2(5, 515)); + queue.add(const Tuple2(3, 245)); + queue.add(const Tuple2(8, 645)); + queue.add(const Tuple2(2, 345)); + }); +} diff --git a/test/puzzle_solver_test.dart b/test/puzzle_solver_test.dart new file mode 100644 index 0000000..725a5c3 --- /dev/null +++ b/test/puzzle_solver_test.dart @@ -0,0 +1,16 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:my_flutter_puzzle/utils/puzzle_solver.dart'; + +void main() { + test('solver logic test', () { + final solver = PuzzleSolverClient(size: 4); + final board = solver.createRandomBoard(); + + final boardStates = solver.runner(board); + if (boardStates != null) { + for (var board in boardStates) { + print(board); + } + } + }); +} diff --git a/test/widget_test.dart b/test/widget_test.dart new file mode 100644 index 0000000..2b89594 --- /dev/null +++ b/test/widget_test.dart @@ -0,0 +1,29 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility that Flutter provides. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:my_flutter_puzzle/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/web/favicon.png b/web/favicon.png new file mode 100644 index 0000000..8aaa46a Binary files /dev/null and b/web/favicon.png differ diff --git a/web/icons/Icon-192.png b/web/icons/Icon-192.png new file mode 100644 index 0000000..b749bfe Binary files /dev/null and b/web/icons/Icon-192.png differ diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png new file mode 100644 index 0000000..88cfd48 Binary files /dev/null and b/web/icons/Icon-512.png differ diff --git a/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000..eb9b4d7 Binary files /dev/null and b/web/icons/Icon-maskable-192.png differ diff --git a/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000..d69c566 Binary files /dev/null and b/web/icons/Icon-maskable-512.png differ diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..396ca36 --- /dev/null +++ b/web/index.html @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + my_flutter_puzzle + + + + + + + diff --git a/web/manifest.json b/web/manifest.json new file mode 100644 index 0000000..6ceabb2 --- /dev/null +++ b/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "my_flutter_puzzle", + "short_name": "my_flutter_puzzle", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/windows/.gitignore b/windows/.gitignore new file mode 100644 index 0000000..d492d0d --- /dev/null +++ b/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt new file mode 100644 index 0000000..164e35f --- /dev/null +++ b/windows/CMakeLists.txt @@ -0,0 +1,95 @@ +cmake_minimum_required(VERSION 3.14) +project(my_flutter_puzzle LANGUAGES CXX) + +set(BINARY_NAME "my_flutter_puzzle") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() + +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build +add_subdirectory("runner") + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/windows/flutter/CMakeLists.txt b/windows/flutter/CMakeLists.txt new file mode 100644 index 0000000..b2e4bd8 --- /dev/null +++ b/windows/flutter/CMakeLists.txt @@ -0,0 +1,103 @@ +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + windows-x64 $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 0000000..8b6d468 --- /dev/null +++ b/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,11 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + + +void RegisterPlugins(flutter::PluginRegistry* registry) { +} diff --git a/windows/flutter/generated_plugin_registrant.h b/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000..dc139d8 --- /dev/null +++ b/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake new file mode 100644 index 0000000..b93c4c3 --- /dev/null +++ b/windows/flutter/generated_plugins.cmake @@ -0,0 +1,23 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/windows/runner/CMakeLists.txt b/windows/runner/CMakeLists.txt new file mode 100644 index 0000000..de2d891 --- /dev/null +++ b/windows/runner/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) +apply_standard_settings(${BINARY_NAME}) +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/windows/runner/Runner.rc b/windows/runner/Runner.rc new file mode 100644 index 0000000..f600485 --- /dev/null +++ b/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#ifdef FLUTTER_BUILD_NUMBER +#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#else +#define VERSION_AS_NUMBER 1,0,0 +#endif + +#ifdef FLUTTER_BUILD_NAME +#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.souvikbiswas" "\0" + VALUE "FileDescription", "A new Flutter project." "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "my_flutter_puzzle" "\0" + VALUE "LegalCopyright", "Copyright (C) 2022 com.souvikbiswas. All rights reserved." "\0" + VALUE "OriginalFilename", "my_flutter_puzzle.exe" "\0" + VALUE "ProductName", "my_flutter_puzzle" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/windows/runner/flutter_window.cpp b/windows/runner/flutter_window.cpp new file mode 100644 index 0000000..b43b909 --- /dev/null +++ b/windows/runner/flutter_window.cpp @@ -0,0 +1,61 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/windows/runner/flutter_window.h b/windows/runner/flutter_window.h new file mode 100644 index 0000000..6da0652 --- /dev/null +++ b/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/windows/runner/main.cpp b/windows/runner/main.cpp new file mode 100644 index 0000000..529ba05 --- /dev/null +++ b/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"my_flutter_puzzle", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/windows/runner/resource.h b/windows/runner/resource.h new file mode 100644 index 0000000..66a65d1 --- /dev/null +++ b/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/windows/runner/resources/app_icon.ico b/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000..c04e20c Binary files /dev/null and b/windows/runner/resources/app_icon.ico differ diff --git a/windows/runner/runner.exe.manifest b/windows/runner/runner.exe.manifest new file mode 100644 index 0000000..c977c4a --- /dev/null +++ b/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/windows/runner/utils.cpp b/windows/runner/utils.cpp new file mode 100644 index 0000000..d19bdbb --- /dev/null +++ b/windows/runner/utils.cpp @@ -0,0 +1,64 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr); + if (target_length == 0) { + return std::string(); + } + std::string utf8_string; + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/windows/runner/utils.h b/windows/runner/utils.h new file mode 100644 index 0000000..3879d54 --- /dev/null +++ b/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/windows/runner/win32_window.cpp b/windows/runner/win32_window.cpp new file mode 100644 index 0000000..c10f08d --- /dev/null +++ b/windows/runner/win32_window.cpp @@ -0,0 +1,245 @@ +#include "win32_window.h" + +#include + +#include "resource.h" + +namespace { + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + FreeLibrary(user32_module); + } +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + return OnCreate(); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} diff --git a/windows/runner/win32_window.h b/windows/runner/win32_window.h new file mode 100644 index 0000000..17ba431 --- /dev/null +++ b/windows/runner/win32_window.h @@ -0,0 +1,98 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates and shows a win32 window with |title| and position and size using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size to will treat the width height passed in to this function + // as logical pixels and scale to appropriate for the default monitor. Returns + // true if the window was created successfully. + bool CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responsponds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_