Moved to debian 12 + roadmap in README.md

This commit is contained in:
dan63047 2023-06-18 00:50:52 +03:00
parent eb8f9a9da6
commit b78d701ae2
101 changed files with 8334 additions and 8289 deletions

88
.gitignore vendored
View File

@ -1,44 +1,44 @@
# Miscellaneous # Miscellaneous
*.class *.class
*.log *.log
*.pyc *.pyc
*.swp *.swp
.DS_Store .DS_Store
.atom/ .atom/
.buildlog/ .buildlog/
.history .history
.svn/ .svn/
migrate_working_dir/ migrate_working_dir/
# IntelliJ related # IntelliJ related
*.iml *.iml
*.ipr *.ipr
*.iws *.iws
.idea/ .idea/
# The .vscode folder contains launch configuration and tasks you configure in # 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 # VS Code which you may wish to be included in version control, so this line
# is commented out by default. # is commented out by default.
.vscode/ .vscode/
# Flutter/Dart/Pub related # Flutter/Dart/Pub related
**/doc/api/ **/doc/api/
**/ios/Flutter/.last_build_id **/ios/Flutter/.last_build_id
.dart_tool/ .dart_tool/
.flutter-plugins .flutter-plugins
.flutter-plugins-dependencies .flutter-plugins-dependencies
.packages .packages
.pub-cache/ .pub-cache/
.pub/ .pub/
/build/ /build/
# Symbolication related # Symbolication related
app.*.symbols app.*.symbols
# Obfuscation related # Obfuscation related
app.*.map.json app.*.map.json
# Android Studio will place build artifacts here # Android Studio will place build artifacts here
/android/app/debug /android/app/debug
/android/app/profile /android/app/profile
/android/app/release /android/app/release

View File

@ -1,5 +1,30 @@
# tetra_stats # Tetra Stats
Track your and other players stats in TETR.IO <center>Track your and other players stats in TETR.IO</center>
## I just only started, idk when this gonna be done. ![Screenshot of the app](https://imgur.com/GGL0fux.png)
# Development Roadmap
- ~~Ability to fetch player~~
- ~~Serialization/Deserialization~~
- ~~Sqlite Database and service, that can work with it~~ *v0.0.2*
- ~~Ability to track player~~
- ~~Ability to compare 2 players~~ *v0.1.0, we are here*
- ~~Stats Calculator~~ *dev build are here*
- Ability to compare player with himself in past
- Tetra League matches history
- Tetra League historic charts for tracked players (maybe even same sh*t for 40l and blitz well see)
- Better UI with delta and hints for stats *that will be v0.2.0*
- Ability to compare player with APM-PPS-VS stats
- Ability to fetch Tetra League leaderboard
- Average stats for ranks
- Ability to compare player with avgRank
- UI Animations
- i18n, EN and RU locales
- Talk with osk about CORS and EndContext in TL matches
- RELEASE ???
---
Special thanks to kerrmunism for formulas
and to osk for TETR.IO

View File

@ -1,29 +1,29 @@
# This file configures the analyzer, which statically analyzes Dart code to # This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints. # check for errors, warnings, and lints.
# #
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled # 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 # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`. # invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps, # The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices. # packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml include: package:flutter_lints/flutter.yaml
linter: linter:
# The lint rules applied to this project can be customized in the # The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml` # 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 # included above or to enable additional rules. A list of all available lints
# and their documentation is published at # and their documentation is published at
# https://dart-lang.github.io/linter/lints/index.html. # https://dart-lang.github.io/linter/lints/index.html.
# #
# Instead of disabling a lint rule for the entire project in the # 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 # 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 # 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 # `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint. # producing the lint.
rules: rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule # avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at # Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options # https://dart.dev/guides/language/analysis-options

26
android/.gitignore vendored
View File

@ -1,13 +1,13 @@
gradle-wrapper.jar gradle-wrapper.jar
/.gradle /.gradle
/captures/ /captures/
/gradlew /gradlew
/gradlew.bat /gradlew.bat
/local.properties /local.properties
GeneratedPluginRegistrant.java GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore. # Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
key.properties key.properties
**/*.keystore **/*.keystore
**/*.jks **/*.jks

View File

@ -1,71 +1,71 @@
def localProperties = new Properties() def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties') def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) { if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader -> localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader) localProperties.load(reader)
} }
} }
def flutterRoot = localProperties.getProperty('flutter.sdk') def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) { if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
} }
def flutterVersionCode = localProperties.getProperty('flutter.versionCode') def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) { if (flutterVersionCode == null) {
flutterVersionCode = '1' flutterVersionCode = '1'
} }
def flutterVersionName = localProperties.getProperty('flutter.versionName') def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) { if (flutterVersionName == null) {
flutterVersionName = '1.0' flutterVersionName = '1.0'
} }
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android { android {
compileSdkVersion flutter.compileSdkVersion compileSdkVersion flutter.compileSdkVersion
ndkVersion flutter.ndkVersion ndkVersion flutter.ndkVersion
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
kotlinOptions { kotlinOptions {
jvmTarget = '1.8' jvmTarget = '1.8'
} }
sourceSets { sourceSets {
main.java.srcDirs += 'src/main/kotlin' main.java.srcDirs += 'src/main/kotlin'
} }
defaultConfig { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.dan63.tetra_stats" applicationId "com.dan63.tetra_stats"
// You can update the following values to match your application needs. // You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion 19 minSdkVersion 19
targetSdkVersion flutter.targetSdkVersion targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
versionName flutterVersionName versionName flutterVersionName
} }
buildTypes { buildTypes {
release { release {
// TODO: Add your own signing config for the release build. // TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works. // Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug signingConfig signingConfigs.debug
} }
} }
} }
flutter { flutter {
source '../..' source '../..'
} }
dependencies { dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
} }

View File

@ -1,8 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.dan63.tetrastats.tetra_stats"> package="com.dan63.tetrastats.tetra_stats">
<!-- The INTERNET permission is required for development. Specifically, <!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc. to allow setting breakpoints, to provide hot reload, etc.
--> -->
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
</manifest> </manifest>

View File

@ -1,35 +1,35 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.dan63.tetrastats.tetra_stats"> package="com.dan63.tetrastats.tetra_stats">
<application <application
android:label="Tetra Stats" android:label="Tetra Stats"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
android:launchMode="singleTop" android:launchMode="singleTop"
android:theme="@style/LaunchTheme" android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as <!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. --> to determine the Window background behind the Flutter UI. -->
<meta-data <meta-data
android:name="io.flutter.embedding.android.NormalTheme" android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" android:resource="@style/NormalTheme"
/> />
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter> </intent-filter>
</activity> </activity>
<!-- Don't delete the meta-data below. <!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data <meta-data
android:name="flutterEmbedding" android:name="flutterEmbedding"
android:value="2" /> android:value="2" />
</application> </application>
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
</manifest> </manifest>

View File

@ -1,6 +1,6 @@
package com.dan63.tetrastats.tetra_stats package com.dan63.tetrastats.tetra_stats
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() { class MainActivity: FlutterActivity() {
} }

View File

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen --> <!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" /> <item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here --> <!-- You can insert your own image assets here -->
<!-- <item> <!-- <item>
<bitmap <bitmap
android:gravity="center" android:gravity="center"
android:src="@mipmap/launch_image" /> android:src="@mipmap/launch_image" />
</item> --> </item> -->
</layer-list> </layer-list>

View File

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen --> <!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" /> <item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here --> <!-- You can insert your own image assets here -->
<!-- <item> <!-- <item>
<bitmap <bitmap
android:gravity="center" android:gravity="center"
android:src="@mipmap/launch_image" /> android:src="@mipmap/launch_image" />
</item> --> </item> -->
</layer-list> </layer-list>

View File

@ -1,18 +1,18 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on --> <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar"> <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when <!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame --> the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item> <item name="android:windowBackground">@drawable/launch_background</item>
</style> </style>
<!-- Theme applied to the Android Window as soon as the process has started. <!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its Flutter UI initializes, as well as behind your Flutter UI while its
running. running.
This Theme is only used starting with V2 of Flutter's Android embedding. --> This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar"> <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item> <item name="android:windowBackground">?android:colorBackground</item>
</style> </style>
</resources> </resources>

View File

@ -1,18 +1,18 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off --> <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar"> <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when <!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame --> the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item> <item name="android:windowBackground">@drawable/launch_background</item>
</style> </style>
<!-- Theme applied to the Android Window as soon as the process has started. <!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its Flutter UI initializes, as well as behind your Flutter UI while its
running. running.
This Theme is only used starting with V2 of Flutter's Android embedding. --> This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar"> <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item> <item name="android:windowBackground">?android:colorBackground</item>
</style> </style>
</resources> </resources>

View File

@ -1,8 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.dan63.tetrastats.tetra_stats"> package="com.dan63.tetrastats.tetra_stats">
<!-- The INTERNET permission is required for development. Specifically, <!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc. to allow setting breakpoints, to provide hot reload, etc.
--> -->
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
</manifest> </manifest>

View File

@ -1,31 +1,31 @@
buildscript { buildscript {
ext.kotlin_version = '1.7.10' ext.kotlin_version = '1.7.10'
repositories { repositories {
google() google()
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:7.2.0' classpath 'com.android.tools.build:gradle:7.2.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
} }
} }
allprojects { allprojects {
repositories { repositories {
google() google()
mavenCentral() mavenCentral()
} }
} }
rootProject.buildDir = '../build' rootProject.buildDir = '../build'
subprojects { subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}" project.buildDir = "${rootProject.buildDir}/${project.name}"
} }
subprojects { subprojects {
project.evaluationDependsOn(':app') project.evaluationDependsOn(':app')
} }
task clean(type: Delete) { task clean(type: Delete) {
delete rootProject.buildDir delete rootProject.buildDir
} }

View File

@ -1,3 +1,3 @@
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip

View File

@ -1,11 +1,11 @@
include ':app' include ':app'
def localPropertiesFile = new File(rootProject.projectDir, "local.properties") def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties() def properties = new Properties()
assert localPropertiesFile.exists() assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
def flutterSdkPath = properties.getProperty("flutter.sdk") def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties" assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"

68
ios/.gitignore vendored
View File

@ -1,34 +1,34 @@
**/dgph **/dgph
*.mode1v3 *.mode1v3
*.mode2v3 *.mode2v3
*.moved-aside *.moved-aside
*.pbxuser *.pbxuser
*.perspectivev3 *.perspectivev3
**/*sync/ **/*sync/
.sconsign.dblite .sconsign.dblite
.tags* .tags*
**/.vagrant/ **/.vagrant/
**/DerivedData/ **/DerivedData/
Icon? Icon?
**/Pods/ **/Pods/
**/.symlinks/ **/.symlinks/
profile profile
xcuserdata xcuserdata
**/.generated/ **/.generated/
Flutter/App.framework Flutter/App.framework
Flutter/Flutter.framework Flutter/Flutter.framework
Flutter/Flutter.podspec Flutter/Flutter.podspec
Flutter/Generated.xcconfig Flutter/Generated.xcconfig
Flutter/ephemeral/ Flutter/ephemeral/
Flutter/app.flx Flutter/app.flx
Flutter/app.zip Flutter/app.zip
Flutter/flutter_assets/ Flutter/flutter_assets/
Flutter/flutter_export_environment.sh Flutter/flutter_export_environment.sh
ServiceDefinitions.json ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.* Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules. # Exceptions to above rules.
!default.mode1v3 !default.mode1v3
!default.mode2v3 !default.mode2v3
!default.pbxuser !default.pbxuser
!default.perspectivev3 !default.perspectivev3

View File

@ -1,26 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>en</string> <string>en</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>App</string> <string>App</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string> <string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>App</string> <string>App</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>FMWK</string> <string>FMWK</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.0</string> <string>1.0</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1.0</string> <string>1.0</string>
<key>MinimumOSVersion</key> <key>MinimumOSVersion</key>
<string>11.0</string> <string>11.0</string>
</dict> </dict>
</plist> </plist>

View File

@ -1 +1 @@
#include "Generated.xcconfig" #include "Generated.xcconfig"

View File

@ -1 +1 @@
#include "Generated.xcconfig" #include "Generated.xcconfig"

View File

@ -1,483 +1,483 @@
// !$*UTF8*$! // !$*UTF8*$!
{ {
archiveVersion = 1; archiveVersion = 1;
classes = { classes = {
}; };
objectVersion = 54; objectVersion = 54;
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */ /* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = { 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase; isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
dstPath = ""; dstPath = "";
dstSubfolderSpec = 10; dstSubfolderSpec = 10;
files = ( files = (
); );
name = "Embed Frameworks"; name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXCopyFilesBuildPhase section */ /* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 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 = "<group>"; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = { 97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
9740EEB11CF90186004384FC /* Flutter */ = { 9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */, 9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */, 9740EEB31CF90195004384FC /* Generated.xcconfig */,
); );
name = Flutter; name = Flutter;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
97C146E51CF9000F007C117D = { 97C146E51CF9000F007C117D = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
9740EEB11CF90186004384FC /* Flutter */, 9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */, 97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */, 97C146EF1CF9000F007C117D /* Products */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
}; };
97C146EF1CF9000F007C117D /* Products */ = { 97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
97C146EE1CF9000F007C117D /* Runner.app */, 97C146EE1CF9000F007C117D /* Runner.app */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
97C146F01CF9000F007C117D /* Runner */ = { 97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */, 97C147021CF9000F007C117D /* Info.plist */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
); );
path = Runner; path = Runner;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
97C146ED1CF9000F007C117D /* Runner */ = { 97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = ( buildPhases = (
9740EEB61CF901F6004384FC /* Run Script */, 9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */, 97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */, 97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */, 97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */, 9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
); );
buildRules = ( buildRules = (
); );
dependencies = ( dependencies = (
); );
name = Runner; name = Runner;
productName = Runner; productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application"; productType = "com.apple.product-type.application";
}; };
/* End PBXNativeTarget section */ /* End PBXNativeTarget section */
/* Begin PBXProject section */ /* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = { 97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastUpgradeCheck = 1300; LastUpgradeCheck = 1300;
ORGANIZATIONNAME = ""; ORGANIZATIONNAME = "";
TargetAttributes = { TargetAttributes = {
97C146ED1CF9000F007C117D = { 97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1; CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100; LastSwiftMigration = 1100;
}; };
}; };
}; };
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3"; compatibilityVersion = "Xcode 9.3";
developmentRegion = en; developmentRegion = en;
hasScannedForEncodings = 0; hasScannedForEncodings = 0;
knownRegions = ( knownRegions = (
en, en,
Base, Base,
); );
mainGroup = 97C146E51CF9000F007C117D; mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */; productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = ""; projectDirPath = "";
projectRoot = ""; projectRoot = "";
targets = ( targets = (
97C146ED1CF9000F007C117D /* Runner */, 97C146ED1CF9000F007C117D /* Runner */,
); );
}; };
/* End PBXProject section */ /* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */ /* Begin PBXResourcesBuildPhase section */
97C146EC1CF9000F007C117D /* Resources */ = { 97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXResourcesBuildPhase section */ /* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1; alwaysOutOfDate = 1;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
); );
inputPaths = ( inputPaths = (
); );
name = "Thin Binary"; name = "Thin Binary";
outputPaths = ( outputPaths = (
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
}; };
9740EEB61CF901F6004384FC /* Run Script */ = { 9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1; alwaysOutOfDate = 1;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
); );
inputPaths = ( inputPaths = (
); );
name = "Run Script"; name = "Run Script";
outputPaths = ( outputPaths = (
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
}; };
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */
97C146EA1CF9000F007C117D /* Sources */ = { 97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXSourcesBuildPhase section */ /* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */ /* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = { 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup; isa = PBXVariantGroup;
children = ( children = (
97C146FB1CF9000F007C117D /* Base */, 97C146FB1CF9000F007C117D /* Base */,
); );
name = Main.storyboard; name = Main.storyboard;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup; isa = PBXVariantGroup;
children = ( children = (
97C147001CF9000F007C117D /* Base */, 97C147001CF9000F007C117D /* Base */,
); );
name = LaunchScreen.storyboard; name = LaunchScreen.storyboard;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
/* End PBXVariantGroup section */ /* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = { 249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES; CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO; ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99; GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0; IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES; VALIDATE_PRODUCT = YES;
}; };
name = Profile; name = Profile;
}; };
249021D4217E4FDB00AE95B9 /* Profile */ = { 249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.dan63.tetrastats.tetraStats; PRODUCT_BUNDLE_IDENTIFIER = com.dan63.tetrastats.tetraStats;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
}; };
name = Profile; name = Profile;
}; };
97C147031CF9000F007C117D /* Debug */ = { 97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES; CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES; ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99; GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO; GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0; GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = ( GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1", "DEBUG=1",
"$(inherited)", "$(inherited)",
); );
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0; IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Debug; name = Debug;
}; };
97C147041CF9000F007C117D /* Release */ = { 97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES; CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO; ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99; GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0; IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule; SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES; VALIDATE_PRODUCT = YES;
}; };
name = Release; name = Release;
}; };
97C147061CF9000F007C117D /* Debug */ = { 97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.dan63.tetrastats.tetraStats; PRODUCT_BUNDLE_IDENTIFIER = com.dan63.tetrastats.tetraStats;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
}; };
name = Debug; name = Debug;
}; };
97C147071CF9000F007C117D /* Release */ = { 97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.dan63.tetrastats.tetraStats; PRODUCT_BUNDLE_IDENTIFIER = com.dan63.tetrastats.tetraStats;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
}; };
name = Release; name = Release;
}; };
/* End XCBuildConfiguration section */ /* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */ /* Begin XCConfigurationList section */
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (
97C147031CF9000F007C117D /* Debug */, 97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */, 97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */, 249021D3217E4FDB00AE95B9 /* Profile */,
); );
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = Release;
}; };
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (
97C147061CF9000F007C117D /* Debug */, 97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */, 97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */, 249021D4217E4FDB00AE95B9 /* Profile */,
); );
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = Release;
}; };
/* End XCConfigurationList section */ /* End XCConfigurationList section */
}; };
rootObject = 97C146E61CF9000F007C117D /* Project object */; rootObject = 97C146E61CF9000F007C117D /* Project object */;
} }

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Workspace <Workspace
version = "1.0"> version = "1.0">
<FileRef <FileRef
location = "self:"> location = "self:">
</FileRef> </FileRef>
</Workspace> </Workspace>

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>IDEDidComputeMac32BitWarning</key> <key>IDEDidComputeMac32BitWarning</key>
<true/> <true/>
</dict> </dict>
</plist> </plist>

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>PreviewsEnabled</key> <key>PreviewsEnabled</key>
<false/> <false/>
</dict> </dict>
</plist> </plist>

View File

@ -1,87 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1300" LastUpgradeVersion = "1300"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"> buildImplicitDependencies = "YES">
<BuildActionEntries> <BuildActionEntries>
<BuildActionEntry <BuildActionEntry
buildForTesting = "YES" buildForTesting = "YES"
buildForRunning = "YES" buildForRunning = "YES"
buildForProfiling = "YES" buildForProfiling = "YES"
buildForArchiving = "YES" buildForArchiving = "YES"
buildForAnalyzing = "YES"> buildForAnalyzing = "YES">
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D" BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app" BuildableName = "Runner.app"
BlueprintName = "Runner" BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj"> ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildActionEntry> </BuildActionEntry>
</BuildActionEntries> </BuildActionEntries>
</BuildAction> </BuildAction>
<TestAction <TestAction
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion> <MacroExpansion>
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D" BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app" BuildableName = "Runner.app"
BlueprintName = "Runner" BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj"> ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference> </BuildableReference>
</MacroExpansion> </MacroExpansion>
<Testables> <Testables>
</Testables> </Testables>
</TestAction> </TestAction>
<LaunchAction <LaunchAction
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0" launchStyle = "0"
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO" ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES" debugDocumentVersioning = "YES"
debugServiceExtension = "internal" debugServiceExtension = "internal"
allowLocationSimulation = "YES"> allowLocationSimulation = "YES">
<BuildableProductRunnable <BuildableProductRunnable
runnableDebuggingMode = "0"> runnableDebuggingMode = "0">
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D" BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app" BuildableName = "Runner.app"
BlueprintName = "Runner" BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj"> ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildableProductRunnable> </BuildableProductRunnable>
</LaunchAction> </LaunchAction>
<ProfileAction <ProfileAction
buildConfiguration = "Profile" buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES" shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = "" savedToolIdentifier = ""
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"> debugDocumentVersioning = "YES">
<BuildableProductRunnable <BuildableProductRunnable
runnableDebuggingMode = "0"> runnableDebuggingMode = "0">
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D" BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app" BuildableName = "Runner.app"
BlueprintName = "Runner" BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj"> ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildableProductRunnable> </BuildableProductRunnable>
</ProfileAction> </ProfileAction>
<AnalyzeAction <AnalyzeAction
buildConfiguration = "Debug"> buildConfiguration = "Debug">
</AnalyzeAction> </AnalyzeAction>
<ArchiveAction <ArchiveAction
buildConfiguration = "Release" buildConfiguration = "Release"
revealArchiveInOrganizer = "YES"> revealArchiveInOrganizer = "YES">
</ArchiveAction> </ArchiveAction>
</Scheme> </Scheme>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Workspace <Workspace
version = "1.0"> version = "1.0">
<FileRef <FileRef
location = "group:Runner.xcodeproj"> location = "group:Runner.xcodeproj">
</FileRef> </FileRef>
</Workspace> </Workspace>

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>IDEDidComputeMac32BitWarning</key> <key>IDEDidComputeMac32BitWarning</key>
<true/> <true/>
</dict> </dict>
</plist> </plist>

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>PreviewsEnabled</key> <key>PreviewsEnabled</key>
<false/> <false/>
</dict> </dict>
</plist> </plist>

View File

@ -1,13 +1,13 @@
import UIKit import UIKit
import Flutter import Flutter
@UIApplicationMain @UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate { @objc class AppDelegate: FlutterAppDelegate {
override func application( override func application(
_ application: UIApplication, _ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool { ) -> Bool {
GeneratedPluginRegistrant.register(with: self) GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions) return super.application(application, didFinishLaunchingWithOptions: launchOptions)
} }
} }

View File

@ -1,122 +1,122 @@
{ {
"images" : [ "images" : [
{ {
"size" : "20x20", "size" : "20x20",
"idiom" : "iphone", "idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png", "filename" : "Icon-App-20x20@2x.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "20x20", "size" : "20x20",
"idiom" : "iphone", "idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png", "filename" : "Icon-App-20x20@3x.png",
"scale" : "3x" "scale" : "3x"
}, },
{ {
"size" : "29x29", "size" : "29x29",
"idiom" : "iphone", "idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png", "filename" : "Icon-App-29x29@1x.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"size" : "29x29", "size" : "29x29",
"idiom" : "iphone", "idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png", "filename" : "Icon-App-29x29@2x.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "29x29", "size" : "29x29",
"idiom" : "iphone", "idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png", "filename" : "Icon-App-29x29@3x.png",
"scale" : "3x" "scale" : "3x"
}, },
{ {
"size" : "40x40", "size" : "40x40",
"idiom" : "iphone", "idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png", "filename" : "Icon-App-40x40@2x.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "40x40", "size" : "40x40",
"idiom" : "iphone", "idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png", "filename" : "Icon-App-40x40@3x.png",
"scale" : "3x" "scale" : "3x"
}, },
{ {
"size" : "60x60", "size" : "60x60",
"idiom" : "iphone", "idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png", "filename" : "Icon-App-60x60@2x.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "60x60", "size" : "60x60",
"idiom" : "iphone", "idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png", "filename" : "Icon-App-60x60@3x.png",
"scale" : "3x" "scale" : "3x"
}, },
{ {
"size" : "20x20", "size" : "20x20",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png", "filename" : "Icon-App-20x20@1x.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"size" : "20x20", "size" : "20x20",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png", "filename" : "Icon-App-20x20@2x.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "29x29", "size" : "29x29",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png", "filename" : "Icon-App-29x29@1x.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"size" : "29x29", "size" : "29x29",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png", "filename" : "Icon-App-29x29@2x.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "40x40", "size" : "40x40",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png", "filename" : "Icon-App-40x40@1x.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"size" : "40x40", "size" : "40x40",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png", "filename" : "Icon-App-40x40@2x.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "76x76", "size" : "76x76",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png", "filename" : "Icon-App-76x76@1x.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"size" : "76x76", "size" : "76x76",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png", "filename" : "Icon-App-76x76@2x.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "83.5x83.5", "size" : "83.5x83.5",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png", "filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "1024x1024", "size" : "1024x1024",
"idiom" : "ios-marketing", "idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png", "filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x" "scale" : "1x"
} }
], ],
"info" : { "info" : {
"version" : 1, "version" : 1,
"author" : "xcode" "author" : "xcode"
} }
} }

View File

@ -1,23 +1,23 @@
{ {
"images" : [ "images" : [
{ {
"idiom" : "universal", "idiom" : "universal",
"filename" : "LaunchImage.png", "filename" : "LaunchImage.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"idiom" : "universal", "idiom" : "universal",
"filename" : "LaunchImage@2x.png", "filename" : "LaunchImage@2x.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"idiom" : "universal", "idiom" : "universal",
"filename" : "LaunchImage@3x.png", "filename" : "LaunchImage@3x.png",
"scale" : "3x" "scale" : "3x"
} }
], ],
"info" : { "info" : {
"version" : 1, "version" : 1,
"author" : "xcode" "author" : "xcode"
} }
} }

View File

@ -1,5 +1,5 @@
# Launch Screen Assets # Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory. 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. 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.

View File

@ -1,37 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies> </dependencies>
<scenes> <scenes>
<!--View Controller--> <!--View Controller-->
<scene sceneID="EHf-IW-A2E"> <scene sceneID="EHf-IW-A2E">
<objects> <objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController"> <viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides> <layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/> <viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/> <viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides> </layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3"> <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4"> <imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView> </imageView>
</subviews> </subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/> <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/> <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints> </constraints>
</view> </view>
</viewController> </viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects> </objects>
<point key="canvasLocation" x="53" y="375"/> <point key="canvasLocation" x="53" y="375"/>
</scene> </scene>
</scenes> </scenes>
<resources> <resources>
<image name="LaunchImage" width="168" height="185"/> <image name="LaunchImage" width="168" height="185"/>
</resources> </resources>
</document> </document>

View File

@ -1,26 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies> </dependencies>
<scenes> <scenes>
<!--Flutter View Controller--> <!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu"> <scene sceneID="tne-QT-ifu">
<objects> <objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController"> <viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides> <layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/> <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/> <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides> </layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC"> <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/> <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/> <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view> </view>
</viewController> </viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects> </objects>
</scene> </scene>
</scenes> </scenes>
</document> </document>

View File

@ -1,51 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string> <string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
<string>Tetra Stats</string> <string>Tetra Stats</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string> <string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>Tetra Stats</string> <string>Tetra Stats</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string> <string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string> <string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
<string>LaunchScreen</string> <string>LaunchScreen</string>
<key>UIMainStoryboardFile</key> <key>UIMainStoryboardFile</key>
<string>Main</string> <string>Main</string>
<key>UISupportedInterfaceOrientations</key> <key>UISupportedInterfaceOrientations</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string> <string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>UISupportedInterfaceOrientations~ipad</key> <key>UISupportedInterfaceOrientations~ipad</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string> <string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string> <string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>UIViewControllerBasedStatusBarAppearance</key> <key>UIViewControllerBasedStatusBarAppearance</key>
<false/> <false/>
<key>CADisableMinimumFrameDurationOnPhone</key> <key>CADisableMinimumFrameDurationOnPhone</key>
<true/> <true/>
<key>UIApplicationSupportsIndirectInputEvents</key> <key>UIApplicationSupportsIndirectInputEvents</key>
<true/> <true/>
</dict> </dict>
</plist> </plist>

View File

@ -1 +1 @@
#import "GeneratedPluginRegistrant.h" #import "GeneratedPluginRegistrant.h"

File diff suppressed because it is too large Load Diff

View File

@ -1,21 +1,21 @@
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart'; import 'package:sqflite_common_ffi/sqflite_ffi.dart';
import 'package:tetra_stats/views/main_view.dart'; import 'package:tetra_stats/views/main_view.dart';
import 'package:tetra_stats/views/settings_view.dart'; import 'package:tetra_stats/views/settings_view.dart';
import 'package:tetra_stats/views/tracked_players_view.dart'; import 'package:tetra_stats/views/tracked_players_view.dart';
import 'package:tetra_stats/views/calc_view.dart'; import 'package:tetra_stats/views/calc_view.dart';
void main() { void main() {
if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) { if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) {
sqfliteFfiInit(); sqfliteFfiInit();
databaseFactory = databaseFactoryFfi; databaseFactory = databaseFactoryFfi;
} }
runApp(MaterialApp( runApp(MaterialApp(
home: const MainView(), home: const MainView(),
routes: {"/settings": (context) => const SettingsView(), "/states": (context) => const TrackedPlayersView(), "/calc": (context) => const CalcView()}, routes: {"/settings": (context) => const SettingsView(), "/states": (context) => const TrackedPlayersView(), "/calc": (context) => const CalcView()},
theme: ThemeData( theme: ThemeData(
fontFamily: 'Eurostile Round', fontFamily: 'Eurostile Round',
colorScheme: const ColorScheme.dark(primary: Colors.cyanAccent, secondary: Colors.purpleAccent), colorScheme: const ColorScheme.dark(primary: Colors.cyanAccent, secondary: Colors.purpleAccent),
scaffoldBackgroundColor: Colors.black))); scaffoldBackgroundColor: Colors.black)));
} }

View File

@ -1,13 +1,13 @@
class DatabaseAlreadyOpen implements Exception {} class DatabaseAlreadyOpen implements Exception {}
class DatabaseIsNotOpen implements Exception {} class DatabaseIsNotOpen implements Exception {}
class UnableToGetDocuments implements Exception {} class UnableToGetDocuments implements Exception {}
class CouldNotDeletePlayer implements Exception {} class CouldNotDeletePlayer implements Exception {}
class CouldNotUpdatePlayer implements Exception {} class CouldNotUpdatePlayer implements Exception {}
class TetrioPlayerAlreadyExist implements Exception {} class TetrioPlayerAlreadyExist implements Exception {}
class TetrioPlayerNotExist implements Exception {} class TetrioPlayerNotExist implements Exception {}

View File

@ -1,54 +1,54 @@
import 'dart:async'; import 'dart:async';
import 'package:sqflite/sqflite.dart'; import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart' show MissingPlatformDirectoryException, getApplicationDocumentsDirectory; import 'package:path_provider/path_provider.dart' show MissingPlatformDirectoryException, getApplicationDocumentsDirectory;
import 'package:tetra_stats/services/crud_exceptions.dart'; import 'package:tetra_stats/services/crud_exceptions.dart';
import 'package:tetra_stats/services/tetrio_crud.dart'; import 'package:tetra_stats/services/tetrio_crud.dart';
import 'package:path/path.dart' show join; import 'package:path/path.dart' show join;
const String dbName = "TetraStats.db"; const String dbName = "TetraStats.db";
class DB { class DB {
Database? _db; Database? _db;
Future<void> open() async { Future<void> open() async {
if (_db != null) { if (_db != null) {
throw DatabaseAlreadyOpen(); throw DatabaseAlreadyOpen();
} }
try { try {
final docsPath = await getApplicationDocumentsDirectory(); final docsPath = await getApplicationDocumentsDirectory();
final dbPath = join(docsPath.path, dbName); final dbPath = join(docsPath.path, dbName);
final db = await openDatabase(dbPath); final db = await openDatabase(dbPath);
_db = db; _db = db;
await db.execute(createTetrioUsersTable); await db.execute(createTetrioUsersTable);
await db.execute(createTetrioUsersToTrack); await db.execute(createTetrioUsersToTrack);
} on MissingPlatformDirectoryException { } on MissingPlatformDirectoryException {
throw UnableToGetDocuments(); throw UnableToGetDocuments();
} }
} }
Future<void> close() async { Future<void> close() async {
final db = _db; final db = _db;
if (db == null) { if (db == null) {
throw DatabaseIsNotOpen(); throw DatabaseIsNotOpen();
} else { } else {
await db.close(); await db.close();
_db = null; _db = null;
} }
} }
Database getDatabaseOrThrow() { Database getDatabaseOrThrow() {
final db = _db; final db = _db;
if (db == null) { if (db == null) {
throw DatabaseIsNotOpen(); throw DatabaseIsNotOpen();
} else { } else {
return db; return db;
} }
} }
Future<void> ensureDbIsOpen() async { Future<void> ensureDbIsOpen() async {
try { try {
await open(); await open();
} on DatabaseAlreadyOpen { } on DatabaseAlreadyOpen {
// empty // empty
} }
} }
} }

View File

@ -1,224 +1,224 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:developer' as developer; import 'dart:developer' as developer;
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:tetra_stats/services/crud_exceptions.dart'; import 'package:tetra_stats/services/crud_exceptions.dart';
import 'package:tetra_stats/services/sqlite_db_controller.dart'; import 'package:tetra_stats/services/sqlite_db_controller.dart';
import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/data_objects/tetrio.dart';
const String dbName = "TetraStats.db"; const String dbName = "TetraStats.db";
const String tetrioUsersTable = "tetrioUsers"; const String tetrioUsersTable = "tetrioUsers";
const String tetrioUsersToTrackTable = "tetrioUsersToTrack"; const String tetrioUsersToTrackTable = "tetrioUsersToTrack";
const String idCol = "id"; const String idCol = "id";
const String nickCol = "nickname"; const String nickCol = "nickname";
const String statesCol = "jsonStates"; const String statesCol = "jsonStates";
const String createTetrioUsersTable = ''' const String createTetrioUsersTable = '''
CREATE TABLE IF NOT EXISTS "tetrioUsers" ( CREATE TABLE IF NOT EXISTS "tetrioUsers" (
"id" TEXT UNIQUE, "id" TEXT UNIQUE,
"nickname" TEXT, "nickname" TEXT,
"jsonStates" TEXT, "jsonStates" TEXT,
PRIMARY KEY("id") PRIMARY KEY("id")
);'''; );''';
const String createTetrioUsersToTrack = ''' const String createTetrioUsersToTrack = '''
CREATE TABLE IF NOT EXISTS "tetrioUsersToTrack" ( CREATE TABLE IF NOT EXISTS "tetrioUsersToTrack" (
"id" TEXT NOT NULL UNIQUE, "id" TEXT NOT NULL UNIQUE,
PRIMARY KEY("ID") PRIMARY KEY("ID")
) )
'''; ''';
class TetrioService extends DB { class TetrioService extends DB {
Map<String, List<TetrioPlayer>> _players = {}; Map<String, List<TetrioPlayer>> _players = {};
static final TetrioService _shared = TetrioService._sharedInstance(); static final TetrioService _shared = TetrioService._sharedInstance();
factory TetrioService() => _shared; factory TetrioService() => _shared;
late final StreamController<Map<String, List<TetrioPlayer>>> _tetrioStreamController; late final StreamController<Map<String, List<TetrioPlayer>>> _tetrioStreamController;
TetrioService._sharedInstance() { TetrioService._sharedInstance() {
_tetrioStreamController = StreamController<Map<String, List<TetrioPlayer>>>.broadcast(onListen: () { _tetrioStreamController = StreamController<Map<String, List<TetrioPlayer>>>.broadcast(onListen: () {
_tetrioStreamController.sink.add(_players); _tetrioStreamController.sink.add(_players);
}); });
} }
@override @override
Future<void> open() async { Future<void> open() async {
await super.open(); await super.open();
await _cachePlayers(); await _cachePlayers();
} }
Stream<Map<String, List<TetrioPlayer>>> get allPlayers => _tetrioStreamController.stream; Stream<Map<String, List<TetrioPlayer>>> get allPlayers => _tetrioStreamController.stream;
Future<void> _cachePlayers() async { Future<void> _cachePlayers() async {
final allPlayers = await getAllPlayers(); final allPlayers = await getAllPlayers();
_players = allPlayers.toList().first; // ??? _players = allPlayers.toList().first; // ???
_tetrioStreamController.add(_players); _tetrioStreamController.add(_players);
developer.log("_cachePlayers: $_players", name: "services/tetrio_crud"); developer.log("_cachePlayers: $_players", name: "services/tetrio_crud");
} }
Future<void> deletePlayer(String id) async { Future<void> deletePlayer(String id) async {
await ensureDbIsOpen(); await ensureDbIsOpen();
final db = getDatabaseOrThrow(); final db = getDatabaseOrThrow();
final deletedPlayer = await db.delete(tetrioUsersTable, where: '$idCol = ?', whereArgs: [id.toLowerCase()]); final deletedPlayer = await db.delete(tetrioUsersTable, where: '$idCol = ?', whereArgs: [id.toLowerCase()]);
if (deletedPlayer != 1) { if (deletedPlayer != 1) {
throw CouldNotDeletePlayer(); throw CouldNotDeletePlayer();
} else { } else {
_players.removeWhere((key, value) => key == id); _players.removeWhere((key, value) => key == id);
_tetrioStreamController.add(_players); _tetrioStreamController.add(_players);
} }
} }
Future<String> getNicknameByID(String id) async { Future<String> getNicknameByID(String id) async {
if (id.length <= 16) return id; if (id.length <= 16) return id;
TetrioPlayer player = await getPlayer(id).then((value) => value.last); TetrioPlayer player = await getPlayer(id).then((value) => value.last);
return player.username; return player.username;
} }
Future<void> createPlayer(TetrioPlayer tetrioPlayer) async { Future<void> createPlayer(TetrioPlayer tetrioPlayer) async {
ensureDbIsOpen(); ensureDbIsOpen();
final db = getDatabaseOrThrow(); final db = getDatabaseOrThrow();
final results = await db.query(tetrioUsersTable, limit: 1, where: '$idCol = ?', whereArgs: [tetrioPlayer.userId.toLowerCase()]); final results = await db.query(tetrioUsersTable, limit: 1, where: '$idCol = ?', whereArgs: [tetrioPlayer.userId.toLowerCase()]);
if (results.isNotEmpty) { if (results.isNotEmpty) {
throw TetrioPlayerAlreadyExist(); throw TetrioPlayerAlreadyExist();
} }
final Map<String, dynamic> statesJson = {tetrioPlayer.state.millisecondsSinceEpoch.toString(): tetrioPlayer.toJson()}; final Map<String, dynamic> statesJson = {tetrioPlayer.state.millisecondsSinceEpoch.toString(): tetrioPlayer.toJson()};
db.insert(tetrioUsersTable, {idCol: tetrioPlayer.userId, nickCol: tetrioPlayer.username, statesCol: jsonEncode(statesJson)}); db.insert(tetrioUsersTable, {idCol: tetrioPlayer.userId, nickCol: tetrioPlayer.username, statesCol: jsonEncode(statesJson)});
_players.addEntries({ _players.addEntries({
tetrioPlayer.userId: [tetrioPlayer] tetrioPlayer.userId: [tetrioPlayer]
}.entries); }.entries);
_tetrioStreamController.add(_players); _tetrioStreamController.add(_players);
} }
Future<void> addPlayerToTrack(TetrioPlayer tetrioPlayer) async { Future<void> addPlayerToTrack(TetrioPlayer tetrioPlayer) async {
ensureDbIsOpen(); ensureDbIsOpen();
final db = getDatabaseOrThrow(); final db = getDatabaseOrThrow();
final results = await db.query(tetrioUsersToTrackTable, where: '$idCol = ?', whereArgs: [tetrioPlayer.userId.toLowerCase()]); final results = await db.query(tetrioUsersToTrackTable, where: '$idCol = ?', whereArgs: [tetrioPlayer.userId.toLowerCase()]);
if (results.isNotEmpty) { if (results.isNotEmpty) {
throw TetrioPlayerAlreadyExist(); throw TetrioPlayerAlreadyExist();
} }
db.insert(tetrioUsersToTrackTable, {idCol: tetrioPlayer.userId}); db.insert(tetrioUsersToTrackTable, {idCol: tetrioPlayer.userId});
} }
Future<bool> isPlayerTracking(String id) async { Future<bool> isPlayerTracking(String id) async {
ensureDbIsOpen(); ensureDbIsOpen();
final db = getDatabaseOrThrow(); final db = getDatabaseOrThrow();
final results = await db.query(tetrioUsersToTrackTable, where: '$idCol = ?', whereArgs: [id.toLowerCase()]); final results = await db.query(tetrioUsersToTrackTable, where: '$idCol = ?', whereArgs: [id.toLowerCase()]);
if (results.isEmpty) { if (results.isEmpty) {
return false; return false;
} else { } else {
return true; return true;
} }
} }
Future<Iterable<String>> getAllPlayerToTrack() async { Future<Iterable<String>> getAllPlayerToTrack() async {
await ensureDbIsOpen(); await ensureDbIsOpen();
final db = getDatabaseOrThrow(); final db = getDatabaseOrThrow();
final players = await db.query(tetrioUsersToTrackTable); final players = await db.query(tetrioUsersToTrackTable);
developer.log("getAllPlayerToTrack: $players", name: "services/tetrio_crud"); developer.log("getAllPlayerToTrack: $players", name: "services/tetrio_crud");
return players.map((noteRow) => noteRow["id"].toString()); return players.map((noteRow) => noteRow["id"].toString());
} }
Future<void> deletePlayerToTrack(String id) async { Future<void> deletePlayerToTrack(String id) async {
await ensureDbIsOpen(); await ensureDbIsOpen();
final db = getDatabaseOrThrow(); final db = getDatabaseOrThrow();
final deletedPlayer = await db.delete(tetrioUsersToTrackTable, where: '$idCol = ?', whereArgs: [id.toLowerCase()]); final deletedPlayer = await db.delete(tetrioUsersToTrackTable, where: '$idCol = ?', whereArgs: [id.toLowerCase()]);
if (deletedPlayer != 1) { if (deletedPlayer != 1) {
throw CouldNotDeletePlayer(); throw CouldNotDeletePlayer();
} else { } else {
// _players.removeWhere((key, value) => key == id); // _players.removeWhere((key, value) => key == id);
// _tetrioStreamController.add(_players); // _tetrioStreamController.add(_players);
} }
} }
Future<void> storeState(TetrioPlayer tetrioPlayer) async { Future<void> storeState(TetrioPlayer tetrioPlayer) async {
ensureDbIsOpen(); ensureDbIsOpen();
final db = getDatabaseOrThrow(); final db = getDatabaseOrThrow();
late List<TetrioPlayer> states; late List<TetrioPlayer> states;
try { try {
states = await getPlayer(tetrioPlayer.userId); states = await getPlayer(tetrioPlayer.userId);
} on TetrioPlayerNotExist { } on TetrioPlayerNotExist {
await createPlayer(tetrioPlayer); await createPlayer(tetrioPlayer);
states = await getPlayer(tetrioPlayer.userId); states = await getPlayer(tetrioPlayer.userId);
} }
bool test = _players[tetrioPlayer.userId]!.last.isSameState(tetrioPlayer); bool test = _players[tetrioPlayer.userId]!.last.isSameState(tetrioPlayer);
if (test == false) states.add(tetrioPlayer); if (test == false) states.add(tetrioPlayer);
final Map<String, dynamic> statesJson = {}; final Map<String, dynamic> statesJson = {};
for (var e in states) { for (var e in states) {
statesJson.addEntries({e.state.millisecondsSinceEpoch.toString(): e.toJson()}.entries); statesJson.addEntries({e.state.millisecondsSinceEpoch.toString(): e.toJson()}.entries);
} }
db.update(tetrioUsersTable, {idCol: tetrioPlayer.userId, nickCol: tetrioPlayer.username, statesCol: jsonEncode(statesJson)}, db.update(tetrioUsersTable, {idCol: tetrioPlayer.userId, nickCol: tetrioPlayer.username, statesCol: jsonEncode(statesJson)},
where: '$idCol = ?', whereArgs: [tetrioPlayer.userId]); where: '$idCol = ?', whereArgs: [tetrioPlayer.userId]);
_players[tetrioPlayer.userId]!.add(tetrioPlayer); _players[tetrioPlayer.userId]!.add(tetrioPlayer);
_tetrioStreamController.add(_players); _tetrioStreamController.add(_players);
} }
Future<void> deleteState(TetrioPlayer tetrioPlayer) async { Future<void> deleteState(TetrioPlayer tetrioPlayer) async {
ensureDbIsOpen(); ensureDbIsOpen();
final db = getDatabaseOrThrow(); final db = getDatabaseOrThrow();
late List<TetrioPlayer> states; late List<TetrioPlayer> states;
states = await getPlayer(tetrioPlayer.userId); states = await getPlayer(tetrioPlayer.userId);
_players[tetrioPlayer.userId]!.removeWhere((element) => element.state == tetrioPlayer.state); _players[tetrioPlayer.userId]!.removeWhere((element) => element.state == tetrioPlayer.state);
states = _players[tetrioPlayer.userId]!; states = _players[tetrioPlayer.userId]!;
final Map<String, dynamic> statesJson = {}; final Map<String, dynamic> statesJson = {};
for (var e in states) { for (var e in states) {
statesJson.addEntries({e.state.millisecondsSinceEpoch.toString(): e.toJson()}.entries); statesJson.addEntries({e.state.millisecondsSinceEpoch.toString(): e.toJson()}.entries);
} }
db.update(tetrioUsersTable, {idCol: tetrioPlayer.userId, nickCol: tetrioPlayer.username, statesCol: jsonEncode(statesJson)}, db.update(tetrioUsersTable, {idCol: tetrioPlayer.userId, nickCol: tetrioPlayer.username, statesCol: jsonEncode(statesJson)},
where: '$idCol = ?', whereArgs: [tetrioPlayer.userId]); where: '$idCol = ?', whereArgs: [tetrioPlayer.userId]);
_players[tetrioPlayer.userId]!.add(tetrioPlayer); _players[tetrioPlayer.userId]!.add(tetrioPlayer);
_tetrioStreamController.add(_players); _tetrioStreamController.add(_players);
} }
Future<List<TetrioPlayer>> getPlayer(String id) async { Future<List<TetrioPlayer>> getPlayer(String id) async {
ensureDbIsOpen(); ensureDbIsOpen();
final db = getDatabaseOrThrow(); final db = getDatabaseOrThrow();
List<TetrioPlayer> states = []; List<TetrioPlayer> states = [];
final results = await db.query(tetrioUsersTable, limit: 1, where: '$idCol = ?', whereArgs: [id.toLowerCase()]); final results = await db.query(tetrioUsersTable, limit: 1, where: '$idCol = ?', whereArgs: [id.toLowerCase()]);
if (results.isEmpty) { if (results.isEmpty) {
throw TetrioPlayerNotExist(); throw TetrioPlayerNotExist();
} else { } else {
dynamic rawStates = results.first['jsonStates'] as String; dynamic rawStates = results.first['jsonStates'] as String;
rawStates = json.decode(rawStates); rawStates = json.decode(rawStates);
rawStates.forEach((k, v) => states.add(TetrioPlayer.fromJson(v, DateTime.fromMillisecondsSinceEpoch(int.parse(k)), false))); rawStates.forEach((k, v) => states.add(TetrioPlayer.fromJson(v, DateTime.fromMillisecondsSinceEpoch(int.parse(k)), false)));
_players.removeWhere((key, value) => key == id); _players.removeWhere((key, value) => key == id);
_players.addEntries({states.last.userId: states}.entries); _players.addEntries({states.last.userId: states}.entries);
_tetrioStreamController.add(_players); _tetrioStreamController.add(_players);
return states; return states;
} }
} }
Future<TetrioPlayer> fetchPlayer(String user, bool addToDB) async { Future<TetrioPlayer> fetchPlayer(String user, bool addToDB) async {
var url = Uri.https('ch.tetr.io', 'api/users/${user.toLowerCase().trim()}'); var url = Uri.https('ch.tetr.io', 'api/users/${user.toLowerCase().trim()}');
final response = await http.get(url); final response = await http.get(url);
if (response.statusCode == 200) { if (response.statusCode == 200) {
if (jsonDecode(response.body)['success']) { if (jsonDecode(response.body)['success']) {
TetrioPlayer player = TetrioPlayer.fromJson( TetrioPlayer player = TetrioPlayer.fromJson(
jsonDecode(response.body)['data']['user'], DateTime.fromMillisecondsSinceEpoch(jsonDecode(response.body)['cache']['cached_at'], isUtc: true), true); jsonDecode(response.body)['data']['user'], DateTime.fromMillisecondsSinceEpoch(jsonDecode(response.body)['cache']['cached_at'], isUtc: true), true);
if (addToDB) { if (addToDB) {
await ensureDbIsOpen(); await ensureDbIsOpen();
storeState(player); storeState(player);
} }
return player; return player;
} else { } else {
developer.log("fetchTetrioPlayer User dosen't exist", name: "services/tetrio_crud", error: response.body); developer.log("fetchTetrioPlayer User dosen't exist", name: "services/tetrio_crud", error: response.body);
throw Exception("User doesn't exist"); throw Exception("User doesn't exist");
} }
} else { } else {
developer.log("fetchTetrioPlayer Failed to fetch player", name: "services/tetrio_crud", error: response.statusCode); developer.log("fetchTetrioPlayer Failed to fetch player", name: "services/tetrio_crud", error: response.statusCode);
throw Exception('Failed to fetch player'); throw Exception('Failed to fetch player');
} }
} }
Future<Iterable<Map<String, List<TetrioPlayer>>>> getAllPlayers() async { Future<Iterable<Map<String, List<TetrioPlayer>>>> getAllPlayers() async {
await ensureDbIsOpen(); await ensureDbIsOpen();
final db = getDatabaseOrThrow(); final db = getDatabaseOrThrow();
final players = await db.query(tetrioUsersTable); final players = await db.query(tetrioUsersTable);
Map<String, List<TetrioPlayer>> data = {}; Map<String, List<TetrioPlayer>> data = {};
//developer.log("getAllPlayers: $players", name: "services/tetrio_crud"); //developer.log("getAllPlayers: $players", name: "services/tetrio_crud");
return players.map((row) { return players.map((row) {
// what the fuck am i doing here? // what the fuck am i doing here?
var test = json.decode(row['jsonStates'] as String); var test = json.decode(row['jsonStates'] as String);
List<TetrioPlayer> states = []; List<TetrioPlayer> states = [];
test.forEach((k, v) => states.add(TetrioPlayer.fromJson(v, DateTime.fromMillisecondsSinceEpoch(int.parse(k)), false))); test.forEach((k, v) => states.add(TetrioPlayer.fromJson(v, DateTime.fromMillisecondsSinceEpoch(int.parse(k)), false)));
data.addEntries({states.last.userId: states}.entries); data.addEntries({states.last.userId: states}.entries);
return data; return data;
}); });
} }
} }

View File

@ -1,302 +1,302 @@
import 'package:fl_chart/fl_chart.dart'; import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/data_objects/tetrio.dart';
double? apm; double? apm;
double? pps; double? pps;
double? vs; double? vs;
NerdStats? nerdStats; NerdStats? nerdStats;
EstTr? estTr; EstTr? estTr;
Playstyle? playstyle; Playstyle? playstyle;
class CalcView extends StatefulWidget { class CalcView extends StatefulWidget {
const CalcView({Key? key}) : super(key: key); const CalcView({Key? key}) : super(key: key);
@override @override
State<StatefulWidget> createState() => CalcState(); State<StatefulWidget> createState() => CalcState();
} }
class CalcState extends State<CalcView> { class CalcState extends State<CalcView> {
late ScrollController _scrollController; late ScrollController _scrollController;
TextEditingController ppsController = TextEditingController(); TextEditingController ppsController = TextEditingController();
TextEditingController apmController = TextEditingController(); TextEditingController apmController = TextEditingController();
TextEditingController vsController = TextEditingController(); TextEditingController vsController = TextEditingController();
@override @override
void initState() { void initState() {
_scrollController = ScrollController(); _scrollController = ScrollController();
super.initState(); super.initState();
} }
@override @override
void dispose() { void dispose() {
super.dispose(); super.dispose();
} }
void calc() { void calc() {
apm = double.tryParse(apmController.text); apm = double.tryParse(apmController.text);
pps = double.tryParse(ppsController.text); pps = double.tryParse(ppsController.text);
vs = double.tryParse(vsController.text); vs = double.tryParse(vsController.text);
if (apm != null && pps != null && vs != null) { if (apm != null && pps != null && vs != null) {
nerdStats = NerdStats(apm!, pps!, vs!); nerdStats = NerdStats(apm!, pps!, vs!);
estTr = EstTr(apm!, pps!, vs!, 60.9, nerdStats!.app, nerdStats!.dss, nerdStats!.dsp, nerdStats!.gbe); estTr = EstTr(apm!, pps!, vs!, 60.9, nerdStats!.app, nerdStats!.dss, nerdStats!.dsp, nerdStats!.gbe);
playstyle = Playstyle(apm!, pps!, nerdStats!.app, nerdStats!.vsapm, nerdStats!.dsp, nerdStats!.gbe, estTr!.srarea, estTr!.statrank); playstyle = Playstyle(apm!, pps!, nerdStats!.app, nerdStats!.vsapm, nerdStats!.dsp, nerdStats!.gbe, estTr!.srarea, estTr!.statrank);
setState(() {}); setState(() {});
} else { } else {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Please, enter valid values"))); ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Please, enter valid values")));
} }
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text("Stats Calculator"), title: const Text("Stats Calculator"),
), ),
backgroundColor: Colors.black, backgroundColor: Colors.black,
body: SafeArea( body: SafeArea(
child: NestedScrollView( child: NestedScrollView(
controller: _scrollController, controller: _scrollController,
headerSliverBuilder: (context, value) { headerSliverBuilder: (context, value) {
return [ return [
SliverToBoxAdapter( SliverToBoxAdapter(
child: Padding( child: Padding(
padding: const EdgeInsets.fromLTRB(14, 16, 16, 32), padding: const EdgeInsets.fromLTRB(14, 16, 16, 32),
child: Row( child: Row(
children: [ children: [
Expanded( Expanded(
child: Padding( child: Padding(
padding: const EdgeInsets.only(right: 12), padding: const EdgeInsets.only(right: 12),
child: TextField( child: TextField(
onSubmitted: (value) => calc(), onSubmitted: (value) => calc(),
controller: apmController, controller: apmController,
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
decoration: const InputDecoration(label: Text("APM"), alignLabelWithHint: true), decoration: const InputDecoration(label: Text("APM"), alignLabelWithHint: true),
), ),
)), )),
Expanded( Expanded(
child: TextField( child: TextField(
onSubmitted: (value) => calc(), onSubmitted: (value) => calc(),
controller: ppsController, controller: ppsController,
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
decoration: const InputDecoration(label: Text("PPS"), alignLabelWithHint: true), decoration: const InputDecoration(label: Text("PPS"), alignLabelWithHint: true),
)), )),
Expanded( Expanded(
child: Padding( child: Padding(
padding: const EdgeInsets.only(left: 12), padding: const EdgeInsets.only(left: 12),
child: TextField( child: TextField(
onSubmitted: (value) => calc(), onSubmitted: (value) => calc(),
controller: vsController, controller: vsController,
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
decoration: const InputDecoration(label: Text("VS"), alignLabelWithHint: true), decoration: const InputDecoration(label: Text("VS"), alignLabelWithHint: true),
), ),
)), )),
TextButton( TextButton(
onPressed: () => calc(), onPressed: () => calc(),
child: const Text("Calc"), child: const Text("Calc"),
), ),
], ],
), ),
), ),
), ),
const SliverToBoxAdapter( const SliverToBoxAdapter(
child: Divider(), child: Divider(),
) )
]; ];
}, },
body: nerdStats == null body: nerdStats == null
? const Text("Enter values to calculate the stats") ? const Text("Enter values to calculate the stats")
: ListView( : ListView(
children: [ children: [
_ListEntry(value: nerdStats!.app, label: "Attack Per Piece", fractionDigits: 3), _ListEntry(value: nerdStats!.app, label: "Attack Per Piece", fractionDigits: 3),
_ListEntry(value: nerdStats!.vsapm, label: "VS/APM", fractionDigits: 3), _ListEntry(value: nerdStats!.vsapm, label: "VS/APM", fractionDigits: 3),
_ListEntry(value: nerdStats!.dss, label: "Downstack Per Second", fractionDigits: 3), _ListEntry(value: nerdStats!.dss, label: "Downstack Per Second", fractionDigits: 3),
_ListEntry(value: nerdStats!.dsp, label: "Downstack Per Piece", fractionDigits: 3), _ListEntry(value: nerdStats!.dsp, label: "Downstack Per Piece", fractionDigits: 3),
_ListEntry(value: nerdStats!.appdsp, label: "APP + DS/P", fractionDigits: 3), _ListEntry(value: nerdStats!.appdsp, label: "APP + DS/P", fractionDigits: 3),
_ListEntry(value: nerdStats!.cheese, label: "Cheese Index", fractionDigits: 3), _ListEntry(value: nerdStats!.cheese, label: "Cheese Index", fractionDigits: 3),
_ListEntry(value: nerdStats!.gbe, label: "Garbage Efficiency", fractionDigits: 3), _ListEntry(value: nerdStats!.gbe, label: "Garbage Efficiency", fractionDigits: 3),
_ListEntry(value: nerdStats!.nyaapp, label: "Weighted APP", fractionDigits: 3), _ListEntry(value: nerdStats!.nyaapp, label: "Weighted APP", fractionDigits: 3),
_ListEntry(value: nerdStats!.area, label: "Area", fractionDigits: 3), _ListEntry(value: nerdStats!.area, label: "Area", fractionDigits: 3),
_ListEntry(value: estTr!.esttr, label: "Est. of TR", fractionDigits: 3), _ListEntry(value: estTr!.esttr, label: "Est. of TR", fractionDigits: 3),
Wrap( Wrap(
direction: Axis.horizontal, direction: Axis.horizontal,
alignment: WrapAlignment.spaceAround, alignment: WrapAlignment.spaceAround,
spacing: 25, spacing: 25,
crossAxisAlignment: WrapCrossAlignment.start, crossAxisAlignment: WrapCrossAlignment.start,
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.fromLTRB(20, 0, 20, 48), padding: const EdgeInsets.fromLTRB(20, 0, 20, 48),
child: SizedBox( child: SizedBox(
height: 300, height: 300,
width: 300, width: 300,
child: RadarChart( child: RadarChart(
RadarChartData( RadarChartData(
radarShape: RadarShape.polygon, radarShape: RadarShape.polygon,
tickCount: 4, tickCount: 4,
ticksTextStyle: const TextStyle(color: Colors.transparent, fontSize: 10), ticksTextStyle: const TextStyle(color: Colors.transparent, fontSize: 10),
radarBorderData: const BorderSide(color: Colors.transparent, width: 1), radarBorderData: const BorderSide(color: Colors.transparent, width: 1),
gridBorderData: const BorderSide(color: Colors.white24, width: 1), gridBorderData: const BorderSide(color: Colors.white24, width: 1),
tickBorderData: const BorderSide(color: Colors.transparent, width: 1), tickBorderData: const BorderSide(color: Colors.transparent, width: 1),
getTitle: (index, angle) { getTitle: (index, angle) {
switch (index) { switch (index) {
case 0: case 0:
return RadarChartTitle( return RadarChartTitle(
text: 'APM', text: 'APM',
angle: angle, angle: angle,
); );
case 1: case 1:
return RadarChartTitle( return RadarChartTitle(
text: 'PPS', text: 'PPS',
angle: angle, angle: angle,
); );
case 2: case 2:
return RadarChartTitle(text: 'VS', angle: angle); return RadarChartTitle(text: 'VS', angle: angle);
case 3: case 3:
return RadarChartTitle(text: 'APP', angle: angle + 180); return RadarChartTitle(text: 'APP', angle: angle + 180);
case 4: case 4:
return RadarChartTitle(text: 'DS/S', angle: angle + 180); return RadarChartTitle(text: 'DS/S', angle: angle + 180);
case 5: case 5:
return RadarChartTitle(text: 'DS/P', angle: angle + 180); return RadarChartTitle(text: 'DS/P', angle: angle + 180);
case 6: case 6:
return RadarChartTitle(text: 'APP+DS/P', angle: angle + 180); return RadarChartTitle(text: 'APP+DS/P', angle: angle + 180);
case 7: case 7:
return RadarChartTitle(text: 'VS/APM', angle: angle + 180); return RadarChartTitle(text: 'VS/APM', angle: angle + 180);
case 8: case 8:
return RadarChartTitle(text: 'Cheese', angle: angle); return RadarChartTitle(text: 'Cheese', angle: angle);
case 9: case 9:
return RadarChartTitle(text: 'Gb Eff.', angle: angle); return RadarChartTitle(text: 'Gb Eff.', angle: angle);
default: default:
return const RadarChartTitle(text: ''); return const RadarChartTitle(text: '');
} }
}, },
dataSets: [ dataSets: [
RadarDataSet( RadarDataSet(
dataEntries: [ dataEntries: [
RadarEntry(value: apm! * 1), RadarEntry(value: apm! * 1),
RadarEntry(value: pps! * 45), RadarEntry(value: pps! * 45),
RadarEntry(value: vs! * 0.444), RadarEntry(value: vs! * 0.444),
RadarEntry(value: nerdStats!.app * 185), RadarEntry(value: nerdStats!.app * 185),
RadarEntry(value: nerdStats!.dss * 175), RadarEntry(value: nerdStats!.dss * 175),
RadarEntry(value: nerdStats!.dsp * 450), RadarEntry(value: nerdStats!.dsp * 450),
RadarEntry(value: nerdStats!.appdsp * 140), RadarEntry(value: nerdStats!.appdsp * 140),
RadarEntry(value: nerdStats!.vsapm * 60), RadarEntry(value: nerdStats!.vsapm * 60),
RadarEntry(value: nerdStats!.cheese * 1.25), RadarEntry(value: nerdStats!.cheese * 1.25),
RadarEntry(value: nerdStats!.gbe * 315), RadarEntry(value: nerdStats!.gbe * 315),
], ],
), ),
RadarDataSet( RadarDataSet(
fillColor: Colors.transparent, fillColor: Colors.transparent,
borderColor: Colors.transparent, borderColor: Colors.transparent,
dataEntries: [ dataEntries: [
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 0), const RadarEntry(value: 0),
], ],
) )
], ],
), ),
swapAnimationDuration: const Duration(milliseconds: 150), // Optional swapAnimationDuration: const Duration(milliseconds: 150), // Optional
swapAnimationCurve: Curves.linear, // Optional swapAnimationCurve: Curves.linear, // Optional
), ),
), ),
), ),
Padding( Padding(
padding: const EdgeInsets.fromLTRB(20, 0, 20, 48), padding: const EdgeInsets.fromLTRB(20, 0, 20, 48),
child: SizedBox( child: SizedBox(
height: 300, height: 300,
width: 300, width: 300,
child: RadarChart( child: RadarChart(
RadarChartData( RadarChartData(
radarShape: RadarShape.polygon, radarShape: RadarShape.polygon,
tickCount: 4, tickCount: 4,
ticksTextStyle: const TextStyle(color: Colors.transparent, fontSize: 10), ticksTextStyle: const TextStyle(color: Colors.transparent, fontSize: 10),
radarBorderData: const BorderSide(color: Colors.transparent, width: 1), radarBorderData: const BorderSide(color: Colors.transparent, width: 1),
gridBorderData: const BorderSide(color: Colors.white24, width: 1), gridBorderData: const BorderSide(color: Colors.white24, width: 1),
tickBorderData: const BorderSide(color: Colors.transparent, width: 1), tickBorderData: const BorderSide(color: Colors.transparent, width: 1),
getTitle: (index, angle) { getTitle: (index, angle) {
switch (index) { switch (index) {
case 0: case 0:
return RadarChartTitle( return RadarChartTitle(
text: 'Opener', text: 'Opener',
angle: angle, angle: angle,
); );
case 1: case 1:
return RadarChartTitle( return RadarChartTitle(
text: 'Stride', text: 'Stride',
angle: angle, angle: angle,
); );
case 2: case 2:
return RadarChartTitle(text: 'Inf Ds', angle: angle + 180); return RadarChartTitle(text: 'Inf Ds', angle: angle + 180);
case 3: case 3:
return RadarChartTitle(text: 'Plonk', angle: angle); return RadarChartTitle(text: 'Plonk', angle: angle);
default: default:
return const RadarChartTitle(text: ''); return const RadarChartTitle(text: '');
} }
}, },
dataSets: [ dataSets: [
RadarDataSet( RadarDataSet(
dataEntries: [ dataEntries: [
RadarEntry(value: playstyle!.opener), RadarEntry(value: playstyle!.opener),
RadarEntry(value: playstyle!.stride), RadarEntry(value: playstyle!.stride),
RadarEntry(value: playstyle!.infds), RadarEntry(value: playstyle!.infds),
RadarEntry(value: playstyle!.plonk), RadarEntry(value: playstyle!.plonk),
], ],
), ),
RadarDataSet( RadarDataSet(
fillColor: Colors.transparent, fillColor: Colors.transparent,
borderColor: Colors.transparent, borderColor: Colors.transparent,
dataEntries: [ dataEntries: [
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 0), const RadarEntry(value: 0),
], ],
), ),
RadarDataSet( RadarDataSet(
fillColor: Colors.transparent, fillColor: Colors.transparent,
borderColor: Colors.transparent, borderColor: Colors.transparent,
dataEntries: [ dataEntries: [
const RadarEntry(value: 1), const RadarEntry(value: 1),
const RadarEntry(value: 1), const RadarEntry(value: 1),
const RadarEntry(value: 1), const RadarEntry(value: 1),
const RadarEntry(value: 1), const RadarEntry(value: 1),
], ],
) )
], ],
), ),
swapAnimationDuration: const Duration(milliseconds: 150), // Optional swapAnimationDuration: const Duration(milliseconds: 150), // Optional
swapAnimationCurve: Curves.linear, // Optional swapAnimationCurve: Curves.linear, // Optional
), ),
), ),
), ),
], ],
) )
], ],
)), )),
), ),
); );
} }
} }
class _ListEntry extends StatelessWidget { class _ListEntry extends StatelessWidget {
final double value; final double value;
final String label; final String label;
final int? fractionDigits; final int? fractionDigits;
const _ListEntry({required this.value, required this.label, this.fractionDigits}); const _ListEntry({required this.value, required this.label, this.fractionDigits});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var f = NumberFormat("#,###.##"); var f = NumberFormat("#,###.##");
f.maximumFractionDigits = fractionDigits ?? 0; f.maximumFractionDigits = fractionDigits ?? 0;
return ListTile(title: Text(label), trailing: Text(f.format(value), style: const TextStyle(fontSize: 22))); return ListTile(title: Text(label), trailing: Text(f.format(value), style: const TextStyle(fontSize: 22)));
} }
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,122 +1,122 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:tetra_stats/services/crud_exceptions.dart'; import 'package:tetra_stats/services/crud_exceptions.dart';
import 'package:tetra_stats/services/tetrio_crud.dart'; import 'package:tetra_stats/services/tetrio_crud.dart';
class SettingsView extends StatefulWidget { class SettingsView extends StatefulWidget {
const SettingsView({Key? key}) : super(key: key); const SettingsView({Key? key}) : super(key: key);
@override @override
State<StatefulWidget> createState() => SettingsState(); State<StatefulWidget> createState() => SettingsState();
} }
class SettingsState extends State<SettingsView> { class SettingsState extends State<SettingsView> {
PackageInfo _packageInfo = PackageInfo(appName: "TetraStats", packageName: "idk man", version: "some numbers", buildNumber: "anotherNumber"); PackageInfo _packageInfo = PackageInfo(appName: "TetraStats", packageName: "idk man", version: "some numbers", buildNumber: "anotherNumber");
late SharedPreferences prefs; late SharedPreferences prefs;
final TetrioService teto = TetrioService(); final TetrioService teto = TetrioService();
String defaultNickname = "Checking..."; String defaultNickname = "Checking...";
final TextEditingController _playertext = TextEditingController(); final TextEditingController _playertext = TextEditingController();
@override @override
void initState() { void initState() {
_initPackageInfo(); _initPackageInfo();
_getPreferences(); _getPreferences();
super.initState(); super.initState();
} }
Future<void> _initPackageInfo() async { Future<void> _initPackageInfo() async {
final info = await PackageInfo.fromPlatform(); final info = await PackageInfo.fromPlatform();
setState(() { setState(() {
_packageInfo = info; _packageInfo = info;
}); });
} }
Future<void> _getPreferences() async { Future<void> _getPreferences() async {
prefs = await SharedPreferences.getInstance(); prefs = await SharedPreferences.getInstance();
_setDefaultNickname(prefs.getString("player")); _setDefaultNickname(prefs.getString("player"));
} }
Future<void> _setDefaultNickname(String? n) async { Future<void> _setDefaultNickname(String? n) async {
if (n != null) { if (n != null) {
try { try {
defaultNickname = await teto.getNicknameByID(n); defaultNickname = await teto.getNicknameByID(n);
} on TetrioPlayerNotExist { } on TetrioPlayerNotExist {
defaultNickname = n; defaultNickname = n;
} }
} else { } else {
defaultNickname = "dan63047"; defaultNickname = "dan63047";
} }
setState(() {}); setState(() {});
} }
Future<void> _setPlayer(String player) async { Future<void> _setPlayer(String player) async {
await prefs.setString('player', player); await prefs.setString('player', player);
await _setDefaultNickname(player); await _setDefaultNickname(player);
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text("Settings"), title: const Text("Settings"),
), ),
backgroundColor: Colors.black, backgroundColor: Colors.black,
body: SafeArea( body: SafeArea(
child: ListView( child: ListView(
children: [ children: [
ListTile( ListTile(
title: const Text("So there you gonna be able to change some settings"), title: const Text("So there you gonna be able to change some settings"),
subtitle: const Text("Only \"Your TETR.IO account\" implemented yet. In the future you will able to import and export app sqlite database."), subtitle: const Text("Only \"Your TETR.IO account\" implemented yet. In the future you will able to import and export app sqlite database."),
trailing: Switch( trailing: Switch(
value: true, value: true,
onChanged: (bool value) {}, onChanged: (bool value) {},
), ),
), ),
ListTile( ListTile(
title: const Text("Your TETR.IO account"), title: const Text("Your TETR.IO account"),
trailing: Text(defaultNickname), trailing: Text(defaultNickname),
onTap: () => showDialog( onTap: () => showDialog(
context: context, context: context,
builder: (BuildContext context) => AlertDialog( builder: (BuildContext context) => AlertDialog(
title: const Text("Your TETR.IO account nickname or ID", style: TextStyle(fontFamily: "Eurostile Round Extended")), title: const Text("Your TETR.IO account nickname or ID", style: TextStyle(fontFamily: "Eurostile Round Extended")),
content: SingleChildScrollView( content: SingleChildScrollView(
child: ListBody(children: [ child: ListBody(children: [
const Text( const Text(
"Every time when app loads, stats of that player will be fetched. Please prefer ID over nickname because nickname can be changed."), "Every time when app loads, stats of that player will be fetched. Please prefer ID over nickname because nickname can be changed."),
TextField(controller: _playertext, maxLength: 25) TextField(controller: _playertext, maxLength: 25)
]), ]),
), ),
actions: <Widget>[ actions: <Widget>[
TextButton( TextButton(
child: const Text('Cancel'), child: const Text('Cancel'),
onPressed: () { onPressed: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
), ),
TextButton( TextButton(
child: const Text('Submit'), child: const Text('Submit'),
onPressed: () { onPressed: () {
_setPlayer(_playertext.text.toLowerCase().trim()); _setPlayer(_playertext.text.toLowerCase().trim());
Navigator.of(context).pop(); Navigator.of(context).pop();
setState(() {}); setState(() {});
}, },
) )
], ],
)), )),
), ),
const Divider(), const Divider(),
ListTile( ListTile(
title: const Text("About app"), title: const Text("About app"),
subtitle: Text(""" subtitle: Text("""
${_packageInfo.appName} (${_packageInfo.packageName}) Version ${_packageInfo.version} Build ${_packageInfo.buildNumber} ${_packageInfo.appName} (${_packageInfo.packageName}) Version ${_packageInfo.version} Build ${_packageInfo.buildNumber}
Developed by dan63047 Developed by dan63047
Formulas provided by kerrmunism Formulas provided by kerrmunism
"""), """),
), ),
], ],
)), )),
); );
} }
} }

View File

@ -1,42 +1,42 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:tetra_stats/widgets/tl_thingy.dart'; import 'package:tetra_stats/widgets/tl_thingy.dart';
import 'package:tetra_stats/widgets/user_thingy.dart'; import 'package:tetra_stats/widgets/user_thingy.dart';
class StateView extends StatefulWidget { class StateView extends StatefulWidget {
final TetrioPlayer state; final TetrioPlayer state;
const StateView({Key? key, required this.state}) : super(key: key); const StateView({Key? key, required this.state}) : super(key: key);
@override @override
State<StatefulWidget> createState() => StateState(); State<StatefulWidget> createState() => StateState();
} }
class StateState extends State<StateView> { class StateState extends State<StateView> {
late ScrollController _scrollController; late ScrollController _scrollController;
@override @override
void initState() { void initState() {
_scrollController = ScrollController(); _scrollController = ScrollController();
super.initState(); super.initState();
} }
void _justUpdate() { void _justUpdate() {
setState(() {}); setState(() {});
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text("${widget.state.username.toUpperCase()} account on ${widget.state.state}"), title: Text("${widget.state.username.toUpperCase()} account on ${widget.state.state}"),
), ),
backgroundColor: Colors.black, backgroundColor: Colors.black,
body: SafeArea( body: SafeArea(
child: NestedScrollView( child: NestedScrollView(
controller: _scrollController, controller: _scrollController,
headerSliverBuilder: (context, value) { headerSliverBuilder: (context, value) {
return [SliverToBoxAdapter(child: UserThingy(player: widget.state, showStateTimestamp: true, setState: _justUpdate))]; return [SliverToBoxAdapter(child: UserThingy(player: widget.state, showStateTimestamp: true, setState: _justUpdate))];
}, },
body: TLThingy(tl: widget.state.tlSeason1, userID: widget.state.userId)))); body: TLThingy(tl: widget.state.tlSeason1, userID: widget.state.userId))));
} }
} }

View File

@ -1,52 +1,52 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:tetra_stats/views/compare_view.dart'; import 'package:tetra_stats/views/compare_view.dart';
import 'package:tetra_stats/views/state_view.dart'; import 'package:tetra_stats/views/state_view.dart';
class StatesView extends StatefulWidget { class StatesView extends StatefulWidget {
final List<TetrioPlayer> states; final List<TetrioPlayer> states;
const StatesView({Key? key, required this.states}) : super(key: key); const StatesView({Key? key, required this.states}) : super(key: key);
@override @override
State<StatefulWidget> createState() => StatesState(); State<StatefulWidget> createState() => StatesState();
} }
final DateFormat dateFormat = DateFormat.yMMMd().add_Hms(); final DateFormat dateFormat = DateFormat.yMMMd().add_Hms();
class StatesState extends State<StatesView> { class StatesState extends State<StatesView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text("${widget.states.length} states of ${widget.states.last.username.toUpperCase()} account"), title: Text("${widget.states.length} states of ${widget.states.last.username.toUpperCase()} account"),
), ),
backgroundColor: Colors.black, backgroundColor: Colors.black,
body: SafeArea( body: SafeArea(
child: ListView.builder( child: ListView.builder(
itemCount: widget.states.length, itemCount: widget.states.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return ListTile( return ListTile(
title: Text("On ${dateFormat.format(widget.states[index].state)}"), title: Text("On ${dateFormat.format(widget.states[index].state)}"),
subtitle: Text("Level ${widget.states[index].level.toStringAsFixed(2)} level, ${widget.states[index].gameTime} of gametime"), subtitle: Text("Level ${widget.states[index].level.toStringAsFixed(2)} level, ${widget.states[index].gameTime} of gametime"),
trailing: IconButton( trailing: IconButton(
icon: const Icon(Icons.delete_forever), icon: const Icon(Icons.delete_forever),
onPressed: () { onPressed: () {
DateTime nn = widget.states[index].state; DateTime nn = widget.states[index].state;
teto.deleteState(widget.states[index]).then((value) => setState(() { teto.deleteState(widget.states[index]).then((value) => setState(() {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("${dateFormat.format(nn)} state was removed from database!"))); ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("${dateFormat.format(nn)} state was removed from database!")));
})); }));
}, },
), ),
onTap: () { onTap: () {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => StateView(state: widget.states[index]), builder: (context) => StateView(state: widget.states[index]),
), ),
); );
}, },
); );
}))); })));
} }
} }

View File

@ -1,94 +1,94 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:tetra_stats/services/tetrio_crud.dart'; import 'package:tetra_stats/services/tetrio_crud.dart';
import 'package:tetra_stats/views/states_view.dart'; import 'package:tetra_stats/views/states_view.dart';
final TetrioService teto = TetrioService(); final TetrioService teto = TetrioService();
class TrackedPlayersView extends StatefulWidget { class TrackedPlayersView extends StatefulWidget {
const TrackedPlayersView({Key? key}) : super(key: key); const TrackedPlayersView({Key? key}) : super(key: key);
@override @override
State<StatefulWidget> createState() => TrackedPlayersState(); State<StatefulWidget> createState() => TrackedPlayersState();
} }
final DateFormat dateFormat = DateFormat.yMMMd().add_Hms(); final DateFormat dateFormat = DateFormat.yMMMd().add_Hms();
class TrackedPlayersState extends State<TrackedPlayersView> { class TrackedPlayersState extends State<TrackedPlayersView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text("Stored data"), title: const Text("Stored data"),
), ),
backgroundColor: Colors.black, backgroundColor: Colors.black,
body: SafeArea( body: SafeArea(
child: StreamBuilder( child: StreamBuilder(
stream: teto.allPlayers, stream: teto.allPlayers,
builder: (context, snapshot) { builder: (context, snapshot) {
switch (snapshot.connectionState) { switch (snapshot.connectionState) {
case ConnectionState.none: case ConnectionState.none:
return const Center(child: Text('none case of StreamBuilder')); return const Center(child: Text('none case of StreamBuilder'));
case ConnectionState.waiting: case ConnectionState.waiting:
case ConnectionState.active: case ConnectionState.active:
final allPlayers = (snapshot.data != null) ? snapshot.data as Map<String, List<TetrioPlayer>> : <String, List<TetrioPlayer>>{}; final allPlayers = (snapshot.data != null) ? snapshot.data as Map<String, List<TetrioPlayer>> : <String, List<TetrioPlayer>>{};
List<String> keys = allPlayers.keys.toList(); List<String> keys = allPlayers.keys.toList();
return NestedScrollView( return NestedScrollView(
headerSliverBuilder: (context, value) { headerSliverBuilder: (context, value) {
String howManyPlayers(int numberOfPlayers) => Intl.plural( String howManyPlayers(int numberOfPlayers) => Intl.plural(
numberOfPlayers, numberOfPlayers,
zero: 'Empty list. Press "Track" button in previous view to add current player here', zero: 'Empty list. Press "Track" button in previous view to add current player here',
one: 'There is only one player', one: 'There is only one player',
other: 'There are $numberOfPlayers players', other: 'There are $numberOfPlayers players',
name: 'howManyPeople', name: 'howManyPeople',
args: [numberOfPlayers], args: [numberOfPlayers],
desc: 'Description of how many people are seen in a place.', desc: 'Description of how many people are seen in a place.',
examples: const {'numberOfPeople': 3}, examples: const {'numberOfPeople': 3},
); );
return [ return [
SliverToBoxAdapter( SliverToBoxAdapter(
child: Padding( child: Padding(
padding: const EdgeInsets.only(left: 16), padding: const EdgeInsets.only(left: 16),
child: Text( child: Text(
howManyPlayers(allPlayers.length), howManyPlayers(allPlayers.length),
style: const TextStyle(color: Colors.white, fontSize: 25), style: const TextStyle(color: Colors.white, fontSize: 25),
), ),
)), )),
const SliverToBoxAdapter(child: Divider()) const SliverToBoxAdapter(child: Divider())
]; ];
}, },
body: ListView.builder( body: ListView.builder(
itemCount: allPlayers.length, itemCount: allPlayers.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return ListTile( return ListTile(
title: Text("${allPlayers[keys[index]]?.last.username}: ${allPlayers[keys[index]]?.length} states"), title: Text("${allPlayers[keys[index]]?.last.username}: ${allPlayers[keys[index]]?.length} states"),
subtitle: Text( subtitle: Text(
"From ${dateFormat.format(allPlayers[keys[index]]!.first.state)} until ${dateFormat.format(allPlayers[keys[index]]!.last.state)}"), "From ${dateFormat.format(allPlayers[keys[index]]!.first.state)} until ${dateFormat.format(allPlayers[keys[index]]!.last.state)}"),
trailing: IconButton( trailing: IconButton(
icon: const Icon(Icons.delete_forever), icon: const Icon(Icons.delete_forever),
onPressed: () { onPressed: () {
String nn = allPlayers[keys[index]]!.last.username; String nn = allPlayers[keys[index]]!.last.username;
teto.deletePlayer(keys[index]); teto.deletePlayer(keys[index]);
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("$nn states was removed from database!"))); ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("$nn states was removed from database!")));
}, },
), ),
onTap: () { onTap: () {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => StatesView(states: allPlayers[keys[index]]!), builder: (context) => StatesView(states: allPlayers[keys[index]]!),
), ),
); );
}, },
); );
})); }));
case ConnectionState.done: case ConnectionState.done:
return const Center( return const Center(
child: Text('done case of StreamBuilder', child: Text('done case of StreamBuilder',
style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 42), textAlign: TextAlign.center)); style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 42), textAlign: TextAlign.center));
} }
})), })),
); );
} }
} }

View File

@ -1,50 +1,50 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
class StatCellNum extends StatelessWidget { class StatCellNum extends StatelessWidget {
const StatCellNum({super.key, required this.playerStat, required this.playerStatLabel, required this.isScreenBig, this.snackBar, this.fractionDigits}); const StatCellNum({super.key, required this.playerStat, required this.playerStatLabel, required this.isScreenBig, this.snackBar, this.fractionDigits});
final num playerStat; final num playerStat;
final String playerStatLabel; final String playerStatLabel;
final bool isScreenBig; final bool isScreenBig;
final String? snackBar; final String? snackBar;
final int? fractionDigits; final int? fractionDigits;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
NumberFormat f = NumberFormat.decimalPatternDigits(decimalDigits: fractionDigits ?? 0); NumberFormat f = NumberFormat.decimalPatternDigits(decimalDigits: fractionDigits ?? 0);
return Column( return Column(
children: [ children: [
Text( Text(
f.format(playerStat), f.format(playerStat),
style: TextStyle( style: TextStyle(
fontFamily: "Eurostile Round Extended", fontFamily: "Eurostile Round Extended",
fontSize: isScreenBig ? 32 : 24, fontSize: isScreenBig ? 32 : 24,
), ),
), ),
snackBar == null snackBar == null
? Text( ? Text(
playerStatLabel, playerStatLabel,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: const TextStyle( style: const TextStyle(
fontFamily: "Eurostile Round", fontFamily: "Eurostile Round",
fontSize: 16, fontSize: 16,
), ),
) )
: TextButton( : TextButton(
onPressed: () { onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(snackBar!))); ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(snackBar!)));
}, },
style: ButtonStyle(padding: MaterialStateProperty.all(EdgeInsets.zero)), style: ButtonStyle(padding: MaterialStateProperty.all(EdgeInsets.zero)),
child: Text( child: Text(
playerStatLabel, playerStatLabel,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: const TextStyle( style: const TextStyle(
fontFamily: "Eurostile Round", fontFamily: "Eurostile Round",
fontSize: 16, fontSize: 16,
), ),
)), )),
], ],
); );
} }
} }

View File

@ -1,317 +1,317 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:fl_chart/fl_chart.dart'; import 'package:fl_chart/fl_chart.dart';
import 'package:tetra_stats/widgets/stat_sell_num.dart'; import 'package:tetra_stats/widgets/stat_sell_num.dart';
var fDiff = NumberFormat("+#,###.###;-#,###.###"); var fDiff = NumberFormat("+#,###.###;-#,###.###");
final NumberFormat f2 = NumberFormat.decimalPatternDigits(decimalDigits: 2); final NumberFormat f2 = NumberFormat.decimalPatternDigits(decimalDigits: 2);
class TLThingy extends StatelessWidget { class TLThingy extends StatelessWidget {
final TetraLeagueAlpha tl; final TetraLeagueAlpha tl;
final String userID; final String userID;
const TLThingy({Key? key, required this.tl, required this.userID}) : super(key: key); const TLThingy({Key? key, required this.tl, required this.userID}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) { return LayoutBuilder(builder: (context, constraints) {
bool bigScreen = constraints.maxWidth > 768; bool bigScreen = constraints.maxWidth > 768;
return ListView.builder( return ListView.builder(
physics: const ClampingScrollPhysics(), physics: const ClampingScrollPhysics(),
itemCount: 1, itemCount: 1,
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
return Column( return Column(
children: (tl.gamesPlayed > 0) children: (tl.gamesPlayed > 0)
? [ ? [
Text("Tetra League", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), Text("Tetra League", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
if (tl.gamesPlayed >= 10) if (tl.gamesPlayed >= 10)
Wrap( Wrap(
direction: Axis.horizontal, direction: Axis.horizontal,
alignment: WrapAlignment.spaceAround, alignment: WrapAlignment.spaceAround,
crossAxisAlignment: WrapCrossAlignment.center, crossAxisAlignment: WrapCrossAlignment.center,
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
children: [ children: [
userID == "5e32fc85ab319c2ab1beb07c" // he love her so much, you can't even imagine userID == "5e32fc85ab319c2ab1beb07c" // he love her so much, you can't even imagine
? Image.asset("res/icons/kagari.png", height: 128) // Btw why she wearing Kazamatsuri high school uniform? ? Image.asset("res/icons/kagari.png", height: 128) // Btw why she wearing Kazamatsuri high school uniform?
: Image.asset("res/tetrio_tl_alpha_ranks/${tl.rank}.png", height: 128), : Image.asset("res/tetrio_tl_alpha_ranks/${tl.rank}.png", height: 128),
Column( Column(
children: [ children: [
Text("${f2.format(tl.rating)} TR", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), Text("${f2.format(tl.rating)} TR", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
Text( Text(
"Top ${f2.format(tl.percentile * 100)}% (${tl.percentileRank.toUpperCase()}) • Top Rank: ${tl.bestRank.toUpperCase()} • Glicko: ${f2.format(tl.glicko!)}±${f2.format(tl.rd!)}${tl.decaying ? ' • Decaying' : ''}", "Top ${f2.format(tl.percentile * 100)}% (${tl.percentileRank.toUpperCase()}) • Top Rank: ${tl.bestRank.toUpperCase()} • Glicko: ${f2.format(tl.glicko!)}±${f2.format(tl.rd!)}${tl.decaying ? ' • Decaying' : ''}",
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
], ],
), ),
], ],
) )
else else
Text("${10 - tl.gamesPlayed} games until being ranked", Text("${10 - tl.gamesPlayed} games until being ranked",
softWrap: true, softWrap: true,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(
fontFamily: "Eurostile Round Extended", fontFamily: "Eurostile Round Extended",
fontSize: bigScreen ? 42 : 28, fontSize: bigScreen ? 42 : 28,
overflow: TextOverflow.visible, overflow: TextOverflow.visible,
)), )),
Padding( Padding(
padding: const EdgeInsets.fromLTRB(0, 16, 0, 48), padding: const EdgeInsets.fromLTRB(0, 16, 0, 48),
child: Wrap( child: Wrap(
direction: Axis.horizontal, direction: Axis.horizontal,
alignment: WrapAlignment.center, alignment: WrapAlignment.center,
spacing: 25, spacing: 25,
crossAxisAlignment: WrapCrossAlignment.start, crossAxisAlignment: WrapCrossAlignment.start,
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
children: [ children: [
if (tl.apm != null) if (tl.apm != null)
StatCellNum(playerStat: tl.apm!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Attack\nPer Minute"), StatCellNum(playerStat: tl.apm!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Attack\nPer Minute"),
if (tl.pps != null) if (tl.pps != null)
StatCellNum(playerStat: tl.pps!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Pieces\nPer Second"), StatCellNum(playerStat: tl.pps!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Pieces\nPer Second"),
if (tl.apm != null) StatCellNum(playerStat: tl.vs!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Versus\nScore"), if (tl.apm != null) StatCellNum(playerStat: tl.vs!, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Versus\nScore"),
if (tl.standing > 0) StatCellNum(playerStat: tl.standing, isScreenBig: bigScreen, playerStatLabel: "Leaderboard\nplacement"), if (tl.standing > 0) StatCellNum(playerStat: tl.standing, isScreenBig: bigScreen, playerStatLabel: "Leaderboard\nplacement"),
if (tl.standingLocal > 0) StatCellNum(playerStat: tl.standingLocal, isScreenBig: bigScreen, playerStatLabel: "Country LB\nplacement"), if (tl.standingLocal > 0) StatCellNum(playerStat: tl.standingLocal, isScreenBig: bigScreen, playerStatLabel: "Country LB\nplacement"),
StatCellNum(playerStat: tl.gamesPlayed, isScreenBig: bigScreen, playerStatLabel: "Games\nplayed"), StatCellNum(playerStat: tl.gamesPlayed, isScreenBig: bigScreen, playerStatLabel: "Games\nplayed"),
StatCellNum(playerStat: tl.gamesWon, isScreenBig: bigScreen, playerStatLabel: "Games\nwon"), StatCellNum(playerStat: tl.gamesWon, isScreenBig: bigScreen, playerStatLabel: "Games\nwon"),
StatCellNum(playerStat: tl.winrate * 100, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Winrate\nprecentage"), StatCellNum(playerStat: tl.winrate * 100, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Winrate\nprecentage"),
], ],
), ),
), ),
if (tl.nerdStats != null) if (tl.nerdStats != null)
Column( Column(
children: [ children: [
Text("Nerd Stats", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), Text("Nerd Stats", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
Padding( Padding(
padding: const EdgeInsets.fromLTRB(0, 16, 0, 48), padding: const EdgeInsets.fromLTRB(0, 16, 0, 48),
child: Wrap( child: Wrap(
direction: Axis.horizontal, direction: Axis.horizontal,
alignment: WrapAlignment.center, alignment: WrapAlignment.center,
spacing: 25, spacing: 25,
crossAxisAlignment: WrapCrossAlignment.start, crossAxisAlignment: WrapCrossAlignment.start,
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
children: [ children: [
StatCellNum(playerStat: tl.nerdStats!.app, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "Attack\nPer Piece"), StatCellNum(playerStat: tl.nerdStats!.app, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "Attack\nPer Piece"),
StatCellNum(playerStat: tl.nerdStats!.vsapm, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "VS/APM"), StatCellNum(playerStat: tl.nerdStats!.vsapm, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "VS/APM"),
StatCellNum( StatCellNum(
playerStat: tl.nerdStats!.dss, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "Downstack\nPer Second"), playerStat: tl.nerdStats!.dss, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "Downstack\nPer Second"),
StatCellNum( StatCellNum(
playerStat: tl.nerdStats!.dsp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "Downstack\nPer Piece"), playerStat: tl.nerdStats!.dsp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "Downstack\nPer Piece"),
StatCellNum(playerStat: tl.nerdStats!.appdsp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "APP + DS/P"), StatCellNum(playerStat: tl.nerdStats!.appdsp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "APP + DS/P"),
StatCellNum(playerStat: tl.nerdStats!.cheese, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Cheese\nIndex"), StatCellNum(playerStat: tl.nerdStats!.cheese, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: "Cheese\nIndex"),
StatCellNum(playerStat: tl.nerdStats!.gbe, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "Garbage\nEfficiency"), StatCellNum(playerStat: tl.nerdStats!.gbe, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "Garbage\nEfficiency"),
StatCellNum(playerStat: tl.nerdStats!.nyaapp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "Weighted\nAPP"), StatCellNum(playerStat: tl.nerdStats!.nyaapp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: "Weighted\nAPP"),
StatCellNum(playerStat: tl.nerdStats!.area, isScreenBig: bigScreen, fractionDigits: 1, playerStatLabel: "Area") StatCellNum(playerStat: tl.nerdStats!.area, isScreenBig: bigScreen, fractionDigits: 1, playerStatLabel: "Area")
]), ]),
) )
], ],
), ),
if (tl.estTr != null) if (tl.estTr != null)
Padding( Padding(
padding: const EdgeInsets.fromLTRB(0, 16, 0, 48), padding: const EdgeInsets.fromLTRB(0, 16, 0, 48),
child: SizedBox( child: SizedBox(
width: bigScreen ? MediaQuery.of(context).size.width * 0.4 : MediaQuery.of(context).size.width * 0.85, width: bigScreen ? MediaQuery.of(context).size.width * 0.4 : MediaQuery.of(context).size.width * 0.85,
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
const Text( const Text(
"Est. of TR:", "Est. of TR:",
style: TextStyle(fontSize: 24), style: TextStyle(fontSize: 24),
), ),
Text( Text(
f2.format(tl.estTr!.esttr), f2.format(tl.estTr!.esttr),
style: const TextStyle(fontSize: 24), style: const TextStyle(fontSize: 24),
), ),
], ],
), ),
if (tl.rating >= 0) if (tl.rating >= 0)
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
const Text( const Text(
"Accuracy:", "Accuracy:",
style: TextStyle(fontSize: 24), style: TextStyle(fontSize: 24),
), ),
Text( Text(
fDiff.format(tl.esttracc!), fDiff.format(tl.esttracc!),
style: const TextStyle(fontSize: 24), style: const TextStyle(fontSize: 24),
), ),
], ],
), ),
], ],
), ),
), ),
), ),
if (tl.nerdStats != null) if (tl.nerdStats != null)
Wrap( Wrap(
direction: Axis.horizontal, direction: Axis.horizontal,
alignment: WrapAlignment.spaceAround, alignment: WrapAlignment.spaceAround,
spacing: 25, spacing: 25,
crossAxisAlignment: WrapCrossAlignment.start, crossAxisAlignment: WrapCrossAlignment.start,
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.fromLTRB(20, 0, 20, 48), padding: const EdgeInsets.fromLTRB(20, 0, 20, 48),
child: SizedBox( child: SizedBox(
height: 300, height: 300,
width: 300, width: 300,
child: RadarChart( child: RadarChart(
RadarChartData( RadarChartData(
radarShape: RadarShape.polygon, radarShape: RadarShape.polygon,
tickCount: 4, tickCount: 4,
ticksTextStyle: const TextStyle(color: Colors.transparent, fontSize: 10), ticksTextStyle: const TextStyle(color: Colors.transparent, fontSize: 10),
radarBorderData: const BorderSide(color: Colors.transparent, width: 1), radarBorderData: const BorderSide(color: Colors.transparent, width: 1),
gridBorderData: const BorderSide(color: Colors.white24, width: 1), gridBorderData: const BorderSide(color: Colors.white24, width: 1),
tickBorderData: const BorderSide(color: Colors.transparent, width: 1), tickBorderData: const BorderSide(color: Colors.transparent, width: 1),
getTitle: (index, angle) { getTitle: (index, angle) {
switch (index) { switch (index) {
case 0: case 0:
return RadarChartTitle( return RadarChartTitle(
text: 'APM', text: 'APM',
angle: angle, angle: angle,
); );
case 1: case 1:
return RadarChartTitle( return RadarChartTitle(
text: 'PPS', text: 'PPS',
angle: angle, angle: angle,
); );
case 2: case 2:
return RadarChartTitle(text: 'VS', angle: angle); return RadarChartTitle(text: 'VS', angle: angle);
case 3: case 3:
return RadarChartTitle(text: 'APP', angle: angle + 180); return RadarChartTitle(text: 'APP', angle: angle + 180);
case 4: case 4:
return RadarChartTitle(text: 'DS/S', angle: angle + 180); return RadarChartTitle(text: 'DS/S', angle: angle + 180);
case 5: case 5:
return RadarChartTitle(text: 'DS/P', angle: angle + 180); return RadarChartTitle(text: 'DS/P', angle: angle + 180);
case 6: case 6:
return RadarChartTitle(text: 'APP+DS/P', angle: angle + 180); return RadarChartTitle(text: 'APP+DS/P', angle: angle + 180);
case 7: case 7:
return RadarChartTitle(text: 'VS/APM', angle: angle + 180); return RadarChartTitle(text: 'VS/APM', angle: angle + 180);
case 8: case 8:
return RadarChartTitle(text: 'Cheese', angle: angle); return RadarChartTitle(text: 'Cheese', angle: angle);
case 9: case 9:
return RadarChartTitle(text: 'Gb Eff.', angle: angle); return RadarChartTitle(text: 'Gb Eff.', angle: angle);
default: default:
return const RadarChartTitle(text: ''); return const RadarChartTitle(text: '');
} }
}, },
dataSets: [ dataSets: [
RadarDataSet( RadarDataSet(
dataEntries: [ dataEntries: [
RadarEntry(value: tl.apm! * 1), RadarEntry(value: tl.apm! * 1),
RadarEntry(value: tl.pps! * 45), RadarEntry(value: tl.pps! * 45),
RadarEntry(value: tl.vs! * 0.444), RadarEntry(value: tl.vs! * 0.444),
RadarEntry(value: tl.nerdStats!.app * 185), RadarEntry(value: tl.nerdStats!.app * 185),
RadarEntry(value: tl.nerdStats!.dss * 175), RadarEntry(value: tl.nerdStats!.dss * 175),
RadarEntry(value: tl.nerdStats!.dsp * 450), RadarEntry(value: tl.nerdStats!.dsp * 450),
RadarEntry(value: tl.nerdStats!.appdsp * 140), RadarEntry(value: tl.nerdStats!.appdsp * 140),
RadarEntry(value: tl.nerdStats!.vsapm * 60), RadarEntry(value: tl.nerdStats!.vsapm * 60),
RadarEntry(value: tl.nerdStats!.cheese * 1.25), RadarEntry(value: tl.nerdStats!.cheese * 1.25),
RadarEntry(value: tl.nerdStats!.gbe * 315), RadarEntry(value: tl.nerdStats!.gbe * 315),
], ],
), ),
RadarDataSet( RadarDataSet(
fillColor: Colors.transparent, fillColor: Colors.transparent,
borderColor: Colors.transparent, borderColor: Colors.transparent,
dataEntries: [ dataEntries: [
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 0), const RadarEntry(value: 0),
], ],
) )
], ],
), ),
swapAnimationDuration: const Duration(milliseconds: 150), // Optional swapAnimationDuration: const Duration(milliseconds: 150), // Optional
swapAnimationCurve: Curves.linear, // Optional swapAnimationCurve: Curves.linear, // Optional
), ),
), ),
), ),
Padding( Padding(
padding: const EdgeInsets.fromLTRB(20, 0, 20, 48), padding: const EdgeInsets.fromLTRB(20, 0, 20, 48),
child: SizedBox( child: SizedBox(
height: 300, height: 300,
width: 300, width: 300,
child: RadarChart( child: RadarChart(
RadarChartData( RadarChartData(
radarShape: RadarShape.polygon, radarShape: RadarShape.polygon,
tickCount: 4, tickCount: 4,
ticksTextStyle: const TextStyle(color: Colors.transparent, fontSize: 10), ticksTextStyle: const TextStyle(color: Colors.transparent, fontSize: 10),
radarBorderData: const BorderSide(color: Colors.transparent, width: 1), radarBorderData: const BorderSide(color: Colors.transparent, width: 1),
gridBorderData: const BorderSide(color: Colors.white24, width: 1), gridBorderData: const BorderSide(color: Colors.white24, width: 1),
tickBorderData: const BorderSide(color: Colors.transparent, width: 1), tickBorderData: const BorderSide(color: Colors.transparent, width: 1),
getTitle: (index, angle) { getTitle: (index, angle) {
switch (index) { switch (index) {
case 0: case 0:
return RadarChartTitle( return RadarChartTitle(
text: 'Opener', text: 'Opener',
angle: angle, angle: angle,
); );
case 1: case 1:
return RadarChartTitle( return RadarChartTitle(
text: 'Stride', text: 'Stride',
angle: angle, angle: angle,
); );
case 2: case 2:
return RadarChartTitle(text: 'Inf Ds', angle: angle + 180); return RadarChartTitle(text: 'Inf Ds', angle: angle + 180);
case 3: case 3:
return RadarChartTitle(text: 'Plonk', angle: angle); return RadarChartTitle(text: 'Plonk', angle: angle);
default: default:
return const RadarChartTitle(text: ''); return const RadarChartTitle(text: '');
} }
}, },
dataSets: [ dataSets: [
RadarDataSet( RadarDataSet(
dataEntries: [ dataEntries: [
RadarEntry(value: tl.playstyle!.opener), RadarEntry(value: tl.playstyle!.opener),
RadarEntry(value: tl.playstyle!.stride), RadarEntry(value: tl.playstyle!.stride),
RadarEntry(value: tl.playstyle!.infds), RadarEntry(value: tl.playstyle!.infds),
RadarEntry(value: tl.playstyle!.plonk), RadarEntry(value: tl.playstyle!.plonk),
], ],
), ),
RadarDataSet( RadarDataSet(
fillColor: Colors.transparent, fillColor: Colors.transparent,
borderColor: Colors.transparent, borderColor: Colors.transparent,
dataEntries: [ dataEntries: [
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 0), const RadarEntry(value: 0),
], ],
), ),
RadarDataSet( RadarDataSet(
fillColor: Colors.transparent, fillColor: Colors.transparent,
borderColor: Colors.transparent, borderColor: Colors.transparent,
dataEntries: [ dataEntries: [
const RadarEntry(value: 1), const RadarEntry(value: 1),
const RadarEntry(value: 1), const RadarEntry(value: 1),
const RadarEntry(value: 1), const RadarEntry(value: 1),
const RadarEntry(value: 1), const RadarEntry(value: 1),
], ],
) )
], ],
), ),
swapAnimationDuration: const Duration(milliseconds: 150), // Optional swapAnimationDuration: const Duration(milliseconds: 150), // Optional
swapAnimationCurve: Curves.linear, // Optional swapAnimationCurve: Curves.linear, // Optional
), ),
), ),
), ),
], ],
) )
] ]
: [ : [
Text("That user never played Tetra League", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), Text("That user never played Tetra League", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
], ],
); );
}, },
); );
}); });
} }
} }

View File

@ -1,286 +1,286 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:tetra_stats/views/compare_view.dart'; import 'package:tetra_stats/views/compare_view.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'dart:developer' as developer; import 'dart:developer' as developer;
import 'package:tetra_stats/widgets/stat_sell_num.dart'; import 'package:tetra_stats/widgets/stat_sell_num.dart';
extension StringExtension on String { extension StringExtension on String {
String capitalize() { String capitalize() {
return "${this[0].toUpperCase()}${substring(1).toLowerCase()}"; return "${this[0].toUpperCase()}${substring(1).toLowerCase()}";
} }
} }
Future<void> copyToClipboard(String text) async { Future<void> copyToClipboard(String text) async {
await Clipboard.setData(ClipboardData(text: text)); await Clipboard.setData(ClipboardData(text: text));
} }
final DateFormat dateFormat = DateFormat.yMMMd().add_Hms(); final DateFormat dateFormat = DateFormat.yMMMd().add_Hms();
class UserThingy extends StatelessWidget { class UserThingy extends StatelessWidget {
final TetrioPlayer player; final TetrioPlayer player;
final bool showStateTimestamp; final bool showStateTimestamp;
final Function setState; final Function setState;
const UserThingy({Key? key, required this.player, required this.showStateTimestamp, required this.setState}) : super(key: key); const UserThingy({Key? key, required this.player, required this.showStateTimestamp, required this.setState}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) { return LayoutBuilder(builder: (context, constraints) {
bool bigScreen = constraints.maxWidth > 768; bool bigScreen = constraints.maxWidth > 768;
double bannerHeight = bigScreen ? 240 : 120; double bannerHeight = bigScreen ? 240 : 120;
double pfpHeight = 128; double pfpHeight = 128;
return Column( return Column(
children: [ children: [
Flex( Flex(
direction: Axis.vertical, direction: Axis.vertical,
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Stack( Stack(
alignment: Alignment.topCenter, alignment: Alignment.topCenter,
children: [ children: [
if (player.bannerRevision != null) if (player.bannerRevision != null)
Image.network( Image.network(
"https://tetr.io/user-content/banners/${player.userId}.jpg?rv=${player.bannerRevision}", "https://tetr.io/user-content/banners/${player.userId}.jpg?rv=${player.bannerRevision}",
fit: BoxFit.cover, fit: BoxFit.cover,
height: bannerHeight, height: bannerHeight,
errorBuilder: (context, error, stackTrace) { errorBuilder: (context, error, stackTrace) {
developer.log("Error with building banner image", name: "main_view", error: error, stackTrace: stackTrace); developer.log("Error with building banner image", name: "main_view", error: error, stackTrace: stackTrace);
return const Placeholder( return const Placeholder(
color: Colors.black, color: Colors.black,
); );
}, },
), ),
Container( Container(
padding: EdgeInsets.fromLTRB(0, player.bannerRevision != null ? bannerHeight / 1.4 : pfpHeight, 0, 0), padding: EdgeInsets.fromLTRB(0, player.bannerRevision != null ? bannerHeight / 1.4 : pfpHeight, 0, 0),
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(1000), borderRadius: BorderRadius.circular(1000),
child: player.role == "banned" child: player.role == "banned"
? Image.asset( ? Image.asset(
"res/avatars/tetrio_banned.png", "res/avatars/tetrio_banned.png",
fit: BoxFit.fitHeight, fit: BoxFit.fitHeight,
height: pfpHeight, height: pfpHeight,
) )
: player.avatarRevision != null : player.avatarRevision != null
? Image.network("https://tetr.io/user-content/avatars/${player.userId}.jpg?rv=${player.avatarRevision}", ? Image.network("https://tetr.io/user-content/avatars/${player.userId}.jpg?rv=${player.avatarRevision}",
fit: BoxFit.fitHeight, height: 128, errorBuilder: (context, error, stackTrace) { fit: BoxFit.fitHeight, height: 128, errorBuilder: (context, error, stackTrace) {
developer.log("Error with building profile picture", name: "main_view", error: error, stackTrace: stackTrace); developer.log("Error with building profile picture", name: "main_view", error: error, stackTrace: stackTrace);
return Image.asset( return Image.asset(
"res/avatars/tetrio_anon.png", "res/avatars/tetrio_anon.png",
fit: BoxFit.fitHeight, fit: BoxFit.fitHeight,
height: pfpHeight, height: pfpHeight,
); );
}) })
: Image.asset( : Image.asset(
"res/avatars/tetrio_anon.png", "res/avatars/tetrio_anon.png",
fit: BoxFit.fitHeight, fit: BoxFit.fitHeight,
height: pfpHeight, height: pfpHeight,
), ),
), ),
), ),
if (player.verified) if (player.verified)
Padding( Padding(
padding: EdgeInsets.fromLTRB( padding: EdgeInsets.fromLTRB(
pfpHeight - 22, pfpHeight - 22,
bigScreen // verified icon top padding: bigScreen // verified icon top padding:
? (player.bannerRevision != null ? bannerHeight + pfpHeight - 96 : pfpHeight + pfpHeight - 32) // for big screen ? (player.bannerRevision != null ? bannerHeight + pfpHeight - 96 : pfpHeight + pfpHeight - 32) // for big screen
: (player.bannerRevision != null ? bannerHeight + pfpHeight - 58 : pfpHeight + pfpHeight - 32), // for small screen : (player.bannerRevision != null ? bannerHeight + pfpHeight - 58 : pfpHeight + pfpHeight - 32), // for small screen
0, 0,
0), 0),
child: const Icon(Icons.verified), child: const Icon(Icons.verified),
) )
], ],
), ),
Flexible( Flexible(
child: Column( child: Column(
children: [ children: [
Text(player.username, style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), Text(player.username, style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
TextButton( TextButton(
child: Text(player.userId, style: const TextStyle(fontFamily: "Eurostile Round Condensed", fontSize: 14)), child: Text(player.userId, style: const TextStyle(fontFamily: "Eurostile Round Condensed", fontSize: 14)),
onPressed: () { onPressed: () {
copyToClipboard(player.userId); copyToClipboard(player.userId);
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Copied to clipboard!"))); ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Copied to clipboard!")));
}), }),
], ],
)), )),
showStateTimestamp showStateTimestamp
? Text("Fetched ${dateFormat.format(player.state)}") ? Text("Fetched ${dateFormat.format(player.state)}")
: Wrap(direction: Axis.horizontal, alignment: WrapAlignment.center, spacing: 25, crossAxisAlignment: WrapCrossAlignment.start, children: [ : Wrap(direction: Axis.horizontal, alignment: WrapAlignment.center, spacing: 25, crossAxisAlignment: WrapCrossAlignment.start, children: [
FutureBuilder( FutureBuilder(
future: teto.isPlayerTracking(player.userId), future: teto.isPlayerTracking(player.userId),
builder: (context, snapshot) { builder: (context, snapshot) {
switch (snapshot.connectionState) { switch (snapshot.connectionState) {
case ConnectionState.none: case ConnectionState.none:
case ConnectionState.waiting: case ConnectionState.waiting:
case ConnectionState.active: case ConnectionState.active:
case ConnectionState.done: case ConnectionState.done:
if (snapshot.data != null && snapshot.data!) { if (snapshot.data != null && snapshot.data!) {
return Column( return Column(
children: [ children: [
IconButton( IconButton(
icon: const Icon(Icons.person_remove), icon: const Icon(Icons.person_remove),
onPressed: () { onPressed: () {
teto.deletePlayerToTrack(player.userId).then((value) => setState()); teto.deletePlayerToTrack(player.userId).then((value) => setState());
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Removed from tracking list!"))); ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Removed from tracking list!")));
}, },
), ),
const Text("Stop tracking") const Text("Stop tracking")
], ],
); );
} else { } else {
return Column( return Column(
children: [ children: [
IconButton( IconButton(
icon: const Icon(Icons.person_add), icon: const Icon(Icons.person_add),
onPressed: () { onPressed: () {
teto.addPlayerToTrack(player).then((value) => setState()); teto.addPlayerToTrack(player).then((value) => setState());
teto.storeState(player); teto.storeState(player);
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Added to tracking list!"))); ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Added to tracking list!")));
}, },
), ),
const Text("Track") const Text("Track")
], ],
); );
} }
} }
}), }),
Column( Column(
children: [ children: [
IconButton( IconButton(
icon: const Icon(Icons.balance), icon: const Icon(Icons.balance),
onPressed: () { onPressed: () {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => CompareView(greenSide: player, redSide: null), builder: (context) => CompareView(greenSide: player, redSide: null),
), ),
); );
}, },
), ),
const Text("Compare") const Text("Compare")
], ],
) )
]), ]),
], ],
), ),
(player.role != "banned") (player.role != "banned")
? Wrap( ? Wrap(
direction: Axis.horizontal, direction: Axis.horizontal,
alignment: WrapAlignment.center, alignment: WrapAlignment.center,
spacing: 25, spacing: 25,
crossAxisAlignment: WrapCrossAlignment.start, crossAxisAlignment: WrapCrossAlignment.start,
clipBehavior: Clip.hardEdge, // hard WHAT??? clipBehavior: Clip.hardEdge, // hard WHAT???
children: [ children: [
StatCellNum( StatCellNum(
playerStat: player.level, playerStat: player.level,
playerStatLabel: "XP Level", playerStatLabel: "XP Level",
isScreenBig: bigScreen, isScreenBig: bigScreen,
snackBar: "${player.xp.floor().toString()} XP, ${((player.level - player.level.floor()) * 100).toStringAsFixed(2)} % until next level", snackBar: "${player.xp.floor().toString()} XP, ${((player.level - player.level.floor()) * 100).toStringAsFixed(2)} % until next level",
), ),
if (player.gameTime >= Duration.zero) if (player.gameTime >= Duration.zero)
StatCellNum( StatCellNum(
playerStat: player.gameTime.inHours, playerStatLabel: "Hours\nPlayed", isScreenBig: bigScreen, snackBar: player.gameTime.toString()), playerStat: player.gameTime.inHours, playerStatLabel: "Hours\nPlayed", isScreenBig: bigScreen, snackBar: player.gameTime.toString()),
if (player.gamesPlayed >= 0) StatCellNum(playerStat: player.gamesPlayed, isScreenBig: bigScreen, playerStatLabel: "Online\nGames"), if (player.gamesPlayed >= 0) StatCellNum(playerStat: player.gamesPlayed, isScreenBig: bigScreen, playerStatLabel: "Online\nGames"),
if (player.gamesWon >= 0) StatCellNum(playerStat: player.gamesWon, isScreenBig: bigScreen, playerStatLabel: "Games\nWon"), if (player.gamesWon >= 0) StatCellNum(playerStat: player.gamesWon, isScreenBig: bigScreen, playerStatLabel: "Games\nWon"),
if (player.friendCount > 0) StatCellNum(playerStat: player.friendCount, isScreenBig: bigScreen, playerStatLabel: "Friends"), if (player.friendCount > 0) StatCellNum(playerStat: player.friendCount, isScreenBig: bigScreen, playerStatLabel: "Friends"),
], ],
) )
: Text( : Text(
"BANNED", "BANNED",
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(
fontFamily: "Eurostile Round Extended", fontFamily: "Eurostile Round Extended",
fontWeight: FontWeight.w900, fontWeight: FontWeight.w900,
color: Colors.red, color: Colors.red,
fontSize: bigScreen ? 60 : 45, fontSize: bigScreen ? 60 : 45,
), ),
), ),
if (player.badstanding != null && player.badstanding!) if (player.badstanding != null && player.badstanding!)
Text( Text(
"BAD STANDING", "BAD STANDING",
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(
fontFamily: "Eurostile Round Extended", fontFamily: "Eurostile Round Extended",
fontWeight: FontWeight.w900, fontWeight: FontWeight.w900,
color: Colors.red, color: Colors.red,
fontSize: bigScreen ? 60 : 45, fontSize: bigScreen ? 60 : 45,
), ),
), ),
Row( Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Expanded( Expanded(
child: Text( child: Text(
"${player.country != null ? "${player.country?.toUpperCase()}" : ""}${player.role.capitalize()} account ${player.registrationTime == null ? "that was from very beginning" : 'created ${dateFormat.format(player.registrationTime!)}'}${player.botmaster != null ? " by ${player.botmaster}" : ""} ${player.supporterTier == 0 ? "Not a supporter" : "Supporter tier ${player.supporterTier}"}", "${player.country != null ? "${player.country?.toUpperCase()}" : ""}${player.role.capitalize()} account ${player.registrationTime == null ? "that was from very beginning" : 'created ${dateFormat.format(player.registrationTime!)}'}${player.botmaster != null ? " by ${player.botmaster}" : ""} ${player.supporterTier == 0 ? "Not a supporter" : "Supporter tier ${player.supporterTier}"}",
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: const TextStyle( style: const TextStyle(
fontFamily: "Eurostile Round", fontFamily: "Eurostile Round",
fontSize: 16, fontSize: 16,
)), )),
) )
], ],
), ),
Wrap( Wrap(
direction: Axis.horizontal, direction: Axis.horizontal,
alignment: WrapAlignment.center, alignment: WrapAlignment.center,
spacing: 25, spacing: 25,
crossAxisAlignment: WrapCrossAlignment.start, crossAxisAlignment: WrapCrossAlignment.start,
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
children: [ children: [
for (var badge in player.badges) for (var badge in player.badges)
IconButton( IconButton(
onPressed: () => showDialog<void>( onPressed: () => showDialog<void>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(
title: Text( title: Text(
badge.label, badge.label,
style: const TextStyle(fontFamily: "Eurostile Round Extended"), style: const TextStyle(fontFamily: "Eurostile Round Extended"),
), ),
content: SingleChildScrollView( content: SingleChildScrollView(
child: ListBody( child: ListBody(
children: [ children: [
Wrap( Wrap(
direction: Axis.horizontal, direction: Axis.horizontal,
alignment: WrapAlignment.center, alignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center, crossAxisAlignment: WrapCrossAlignment.center,
spacing: 25, spacing: 25,
children: [ children: [
Image.asset("res/tetrio_badges/${badge.badgeId}.png"), Image.asset("res/tetrio_badges/${badge.badgeId}.png"),
Text(badge.ts != null Text(badge.ts != null
? "Obtained ${dateFormat.format(badge.ts!)}" ? "Obtained ${dateFormat.format(badge.ts!)}"
: "That badge was assigned manualy by TETR.IO admins"), : "That badge was assigned manualy by TETR.IO admins"),
], ],
) )
], ],
), ),
), ),
actions: <Widget>[ actions: <Widget>[
TextButton( TextButton(
child: const Text('OK'), child: const Text('OK'),
onPressed: () { onPressed: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
), ),
], ],
); );
}, },
), ),
tooltip: badge.label, tooltip: badge.label,
icon: Image.asset( icon: Image.asset(
"res/tetrio_badges/${badge.badgeId}.png", "res/tetrio_badges/${badge.badgeId}.png",
height: 64, height: 64,
width: 64, width: 64,
errorBuilder: (context, error, stackTrace) { errorBuilder: (context, error, stackTrace) {
developer.log("Error with building $badge", name: "main_view", error: error, stackTrace: stackTrace); developer.log("Error with building $badge", name: "main_view", error: error, stackTrace: stackTrace);
return Image.asset("res/icons/kagari.png", height: 64, width: 64); return Image.asset("res/icons/kagari.png", height: 64, width: 64);
}, },
)) ))
], ],
), ),
], ],
); );
}); });
} }
} }

2
linux/.gitignore vendored
View File

@ -1 +1 @@
flutter/ephemeral flutter/ephemeral

View File

@ -1,138 +1,138 @@
# Project-level configuration. # Project-level configuration.
cmake_minimum_required(VERSION 3.10) cmake_minimum_required(VERSION 3.10)
project(runner LANGUAGES CXX) project(runner LANGUAGES CXX)
# The name of the executable created for the application. Change this to change # The name of the executable created for the application. Change this to change
# the on-disk name of your application. # the on-disk name of your application.
set(BINARY_NAME "Tetra Stats") set(BINARY_NAME "tetra_stats")
# The unique GTK application identifier for this application. See: # The unique GTK application identifier for this application. See:
# https://wiki.gnome.org/HowDoI/ChooseApplicationID # https://wiki.gnome.org/HowDoI/ChooseApplicationID
set(APPLICATION_ID "com.dan63.tetrastats.tetra_stats") set(APPLICATION_ID "com.dan63.tetrastats.tetra_stats")
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent # Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake. # versions of CMake.
cmake_policy(SET CMP0063 NEW) cmake_policy(SET CMP0063 NEW)
# Load bundled libraries from the lib/ directory relative to the binary. # Load bundled libraries from the lib/ directory relative to the binary.
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
# Root filesystem for cross-building. # Root filesystem for cross-building.
if(FLUTTER_TARGET_PLATFORM_SYSROOT) if(FLUTTER_TARGET_PLATFORM_SYSROOT)
set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
endif() endif()
# Define build configuration options. # Define build configuration options.
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE "Debug" CACHE set(CMAKE_BUILD_TYPE "Debug" CACHE
STRING "Flutter build mode" FORCE) STRING "Flutter build mode" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Profile" "Release") "Debug" "Profile" "Release")
endif() endif()
# Compilation settings that should be applied to most targets. # Compilation settings that should be applied to most targets.
# #
# Be cautious about adding new options here, as plugins use this function by # Be cautious about adding new options here, as plugins use this function by
# default. In most cases, you should add new options to specific targets instead # default. In most cases, you should add new options to specific targets instead
# of modifying this function. # of modifying this function.
function(APPLY_STANDARD_SETTINGS TARGET) function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_14) target_compile_features(${TARGET} PUBLIC cxx_std_14)
target_compile_options(${TARGET} PRIVATE -Wall -Werror) target_compile_options(${TARGET} PRIVATE -Wall -Werror)
target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>") target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>") target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
endfunction() endfunction()
# Flutter library and tool build rules. # Flutter library and tool build rules.
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
add_subdirectory(${FLUTTER_MANAGED_DIR}) add_subdirectory(${FLUTTER_MANAGED_DIR})
# System-level dependencies. # System-level dependencies.
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
# Define the application target. To change its name, change BINARY_NAME above, # Define the application target. To change its name, change BINARY_NAME above,
# not the value here, or `flutter run` will no longer work. # not the value here, or `flutter run` will no longer work.
# #
# Any new source files that you add to the application should be added here. # Any new source files that you add to the application should be added here.
add_executable(${BINARY_NAME} add_executable(${BINARY_NAME}
"main.cc" "main.cc"
"my_application.cc" "my_application.cc"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
) )
# Apply the standard set of build settings. This can be removed for applications # Apply the standard set of build settings. This can be removed for applications
# that need different build settings. # that need different build settings.
apply_standard_settings(${BINARY_NAME}) apply_standard_settings(${BINARY_NAME})
# Add dependency libraries. Add any application-specific dependencies here. # Add dependency libraries. Add any application-specific dependencies here.
target_link_libraries(${BINARY_NAME} PRIVATE flutter) target_link_libraries(${BINARY_NAME} PRIVATE flutter)
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
# Run the Flutter tool portions of the build. This must not be removed. # Run the Flutter tool portions of the build. This must not be removed.
add_dependencies(${BINARY_NAME} flutter_assemble) add_dependencies(${BINARY_NAME} flutter_assemble)
# Only the install-generated bundle's copy of the executable will launch # Only the install-generated bundle's copy of the executable will launch
# correctly, since the resources must in the right relative locations. To avoid # 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 # people trying to run the unbundled copy, put it in a subdirectory instead of
# the default top-level location. # the default top-level location.
set_target_properties(${BINARY_NAME} set_target_properties(${BINARY_NAME}
PROPERTIES PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
) )
# Generated plugin build rules, which manage building the plugins and adding # Generated plugin build rules, which manage building the plugins and adding
# them to the application. # them to the application.
include(flutter/generated_plugins.cmake) include(flutter/generated_plugins.cmake)
# === Installation === # === Installation ===
# By default, "installing" just makes a relocatable bundle in the build # By default, "installing" just makes a relocatable bundle in the build
# directory. # directory.
set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
endif() endif()
# Start with a clean build bundle directory every time. # Start with a clean build bundle directory every time.
install(CODE " install(CODE "
file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
" COMPONENT Runtime) " COMPONENT Runtime)
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
COMPONENT Runtime) COMPONENT Runtime)
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
COMPONENT Runtime) COMPONENT Runtime)
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime) COMPONENT Runtime)
foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
install(FILES "${bundled_library}" install(FILES "${bundled_library}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime) COMPONENT Runtime)
endforeach(bundled_library) endforeach(bundled_library)
# Fully re-copy the assets directory on each build to avoid having stale files # Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install. # from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets") set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
install(CODE " install(CODE "
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
" COMPONENT Runtime) " COMPONENT Runtime)
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
# Install the AOT library on non-Debug builds only. # Install the AOT library on non-Debug builds only.
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime) COMPONENT Runtime)
endif() endif()

View File

@ -1,88 +1,88 @@
# This file controls Flutter-level build steps. It should not be edited. # This file controls Flutter-level build steps. It should not be edited.
cmake_minimum_required(VERSION 3.10) cmake_minimum_required(VERSION 3.10)
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
# Configuration provided via flutter tool. # Configuration provided via flutter tool.
include(${EPHEMERAL_DIR}/generated_config.cmake) include(${EPHEMERAL_DIR}/generated_config.cmake)
# TODO: Move the rest of this into files in ephemeral. See # TODO: Move the rest of this into files in ephemeral. See
# https://github.com/flutter/flutter/issues/57146. # https://github.com/flutter/flutter/issues/57146.
# Serves the same purpose as list(TRANSFORM ... PREPEND ...), # Serves the same purpose as list(TRANSFORM ... PREPEND ...),
# which isn't available in 3.10. # which isn't available in 3.10.
function(list_prepend LIST_NAME PREFIX) function(list_prepend LIST_NAME PREFIX)
set(NEW_LIST "") set(NEW_LIST "")
foreach(element ${${LIST_NAME}}) foreach(element ${${LIST_NAME}})
list(APPEND NEW_LIST "${PREFIX}${element}") list(APPEND NEW_LIST "${PREFIX}${element}")
endforeach(element) endforeach(element)
set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
endfunction() endfunction()
# === Flutter Library === # === Flutter Library ===
# System-level dependencies. # System-level dependencies.
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
# Published to parent scope for install step. # Published to parent scope for install step.
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
list(APPEND FLUTTER_LIBRARY_HEADERS list(APPEND FLUTTER_LIBRARY_HEADERS
"fl_basic_message_channel.h" "fl_basic_message_channel.h"
"fl_binary_codec.h" "fl_binary_codec.h"
"fl_binary_messenger.h" "fl_binary_messenger.h"
"fl_dart_project.h" "fl_dart_project.h"
"fl_engine.h" "fl_engine.h"
"fl_json_message_codec.h" "fl_json_message_codec.h"
"fl_json_method_codec.h" "fl_json_method_codec.h"
"fl_message_codec.h" "fl_message_codec.h"
"fl_method_call.h" "fl_method_call.h"
"fl_method_channel.h" "fl_method_channel.h"
"fl_method_codec.h" "fl_method_codec.h"
"fl_method_response.h" "fl_method_response.h"
"fl_plugin_registrar.h" "fl_plugin_registrar.h"
"fl_plugin_registry.h" "fl_plugin_registry.h"
"fl_standard_message_codec.h" "fl_standard_message_codec.h"
"fl_standard_method_codec.h" "fl_standard_method_codec.h"
"fl_string_codec.h" "fl_string_codec.h"
"fl_value.h" "fl_value.h"
"fl_view.h" "fl_view.h"
"flutter_linux.h" "flutter_linux.h"
) )
list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
add_library(flutter INTERFACE) add_library(flutter INTERFACE)
target_include_directories(flutter INTERFACE target_include_directories(flutter INTERFACE
"${EPHEMERAL_DIR}" "${EPHEMERAL_DIR}"
) )
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
target_link_libraries(flutter INTERFACE target_link_libraries(flutter INTERFACE
PkgConfig::GTK PkgConfig::GTK
PkgConfig::GLIB PkgConfig::GLIB
PkgConfig::GIO PkgConfig::GIO
) )
add_dependencies(flutter flutter_assemble) add_dependencies(flutter flutter_assemble)
# === Flutter tool backend === # === Flutter tool backend ===
# _phony_ is a non-existent file to force this command to run every time, # _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 # since currently there's no way to get a full input/output list from the
# flutter tool. # flutter tool.
add_custom_command( add_custom_command(
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
${CMAKE_CURRENT_BINARY_DIR}/_phony_ ${CMAKE_CURRENT_BINARY_DIR}/_phony_
COMMAND ${CMAKE_COMMAND} -E env COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT} ${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
VERBATIM VERBATIM
) )
add_custom_target(flutter_assemble DEPENDS add_custom_target(flutter_assemble DEPENDS
"${FLUTTER_LIBRARY}" "${FLUTTER_LIBRARY}"
${FLUTTER_LIBRARY_HEADERS} ${FLUTTER_LIBRARY_HEADERS}
) )

View File

@ -6,6 +6,10 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) { void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) sqlite3_flutter_libs_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "Sqlite3FlutterLibsPlugin");
sqlite3_flutter_libs_plugin_register_with_registrar(sqlite3_flutter_libs_registrar);
} }

View File

@ -3,6 +3,7 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
sqlite3_flutter_libs
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST

View File

@ -1,6 +1,6 @@
#include "my_application.h" #include "my_application.h"
int main(int argc, char** argv) { int main(int argc, char** argv) {
g_autoptr(MyApplication) app = my_application_new(); g_autoptr(MyApplication) app = my_application_new();
return g_application_run(G_APPLICATION(app), argc, argv); return g_application_run(G_APPLICATION(app), argc, argv);
} }

View File

@ -1,104 +1,104 @@
#include "my_application.h" #include "my_application.h"
#include <flutter_linux/flutter_linux.h> #include <flutter_linux/flutter_linux.h>
#ifdef GDK_WINDOWING_X11 #ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h> #include <gdk/gdkx.h>
#endif #endif
#include "flutter/generated_plugin_registrant.h" #include "flutter/generated_plugin_registrant.h"
struct _MyApplication { struct _MyApplication {
GtkApplication parent_instance; GtkApplication parent_instance;
char** dart_entrypoint_arguments; char** dart_entrypoint_arguments;
}; };
G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
// Implements GApplication::activate. // Implements GApplication::activate.
static void my_application_activate(GApplication* application) { static void my_application_activate(GApplication* application) {
MyApplication* self = MY_APPLICATION(application); MyApplication* self = MY_APPLICATION(application);
GtkWindow* window = GtkWindow* window =
GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
// Use a header bar when running in GNOME as this is the common style used // 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 // by applications and is the setup most users will be using (e.g. Ubuntu
// desktop). // desktop).
// If running on X and not using GNOME then just use a traditional title bar // 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. // 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 running on Wayland assume the header bar will work (may need changing
// if future cases occur). // if future cases occur).
gboolean use_header_bar = TRUE; gboolean use_header_bar = TRUE;
#ifdef GDK_WINDOWING_X11 #ifdef GDK_WINDOWING_X11
GdkScreen* screen = gtk_window_get_screen(window); GdkScreen* screen = gtk_window_get_screen(window);
if (GDK_IS_X11_SCREEN(screen)) { if (GDK_IS_X11_SCREEN(screen)) {
const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
if (g_strcmp0(wm_name, "GNOME Shell") != 0) { if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
use_header_bar = FALSE; use_header_bar = FALSE;
} }
} }
#endif #endif
if (use_header_bar) { if (use_header_bar) {
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
gtk_widget_show(GTK_WIDGET(header_bar)); gtk_widget_show(GTK_WIDGET(header_bar));
gtk_header_bar_set_title(header_bar, "tetra_stats"); gtk_header_bar_set_title(header_bar, "tetra_stats");
gtk_header_bar_set_show_close_button(header_bar, TRUE); gtk_header_bar_set_show_close_button(header_bar, TRUE);
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
} else { } else {
gtk_window_set_title(window, "tetra_stats"); gtk_window_set_title(window, "tetra_stats");
} }
gtk_window_set_default_size(window, 1280, 720); gtk_window_set_default_size(window, 1280, 720);
gtk_widget_show(GTK_WIDGET(window)); gtk_widget_show(GTK_WIDGET(window));
g_autoptr(FlDartProject) project = fl_dart_project_new(); g_autoptr(FlDartProject) project = fl_dart_project_new();
fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
FlView* view = fl_view_new(project); FlView* view = fl_view_new(project);
gtk_widget_show(GTK_WIDGET(view)); gtk_widget_show(GTK_WIDGET(view));
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
fl_register_plugins(FL_PLUGIN_REGISTRY(view)); fl_register_plugins(FL_PLUGIN_REGISTRY(view));
gtk_widget_grab_focus(GTK_WIDGET(view)); gtk_widget_grab_focus(GTK_WIDGET(view));
} }
// Implements GApplication::local_command_line. // Implements GApplication::local_command_line.
static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) {
MyApplication* self = MY_APPLICATION(application); MyApplication* self = MY_APPLICATION(application);
// Strip out the first argument as it is the binary name. // Strip out the first argument as it is the binary name.
self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
g_autoptr(GError) error = nullptr; g_autoptr(GError) error = nullptr;
if (!g_application_register(application, nullptr, &error)) { if (!g_application_register(application, nullptr, &error)) {
g_warning("Failed to register: %s", error->message); g_warning("Failed to register: %s", error->message);
*exit_status = 1; *exit_status = 1;
return TRUE; return TRUE;
} }
g_application_activate(application); g_application_activate(application);
*exit_status = 0; *exit_status = 0;
return TRUE; return TRUE;
} }
// Implements GObject::dispose. // Implements GObject::dispose.
static void my_application_dispose(GObject* object) { static void my_application_dispose(GObject* object) {
MyApplication* self = MY_APPLICATION(object); MyApplication* self = MY_APPLICATION(object);
g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
G_OBJECT_CLASS(my_application_parent_class)->dispose(object); G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
} }
static void my_application_class_init(MyApplicationClass* klass) { static void my_application_class_init(MyApplicationClass* klass) {
G_APPLICATION_CLASS(klass)->activate = my_application_activate; G_APPLICATION_CLASS(klass)->activate = my_application_activate;
G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;
G_OBJECT_CLASS(klass)->dispose = my_application_dispose; G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
} }
static void my_application_init(MyApplication* self) {} static void my_application_init(MyApplication* self) {}
MyApplication* my_application_new() { MyApplication* my_application_new() {
return MY_APPLICATION(g_object_new(my_application_get_type(), return MY_APPLICATION(g_object_new(my_application_get_type(),
"application-id", APPLICATION_ID, "application-id", APPLICATION_ID,
"flags", G_APPLICATION_NON_UNIQUE, "flags", G_APPLICATION_NON_UNIQUE,
nullptr)); nullptr));
} }

View File

@ -1,18 +1,18 @@
#ifndef FLUTTER_MY_APPLICATION_H_ #ifndef FLUTTER_MY_APPLICATION_H_
#define FLUTTER_MY_APPLICATION_H_ #define FLUTTER_MY_APPLICATION_H_
#include <gtk/gtk.h> #include <gtk/gtk.h>
G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
GtkApplication) GtkApplication)
/** /**
* my_application_new: * my_application_new:
* *
* Creates a new Flutter-based application. * Creates a new Flutter-based application.
* *
* Returns: a new #MyApplication. * Returns: a new #MyApplication.
*/ */
MyApplication* my_application_new(); MyApplication* my_application_new();
#endif // FLUTTER_MY_APPLICATION_H_ #endif // FLUTTER_MY_APPLICATION_H_

14
macos/.gitignore vendored
View File

@ -1,7 +1,7 @@
# Flutter-related # Flutter-related
**/Flutter/ephemeral/ **/Flutter/ephemeral/
**/Pods/ **/Pods/
# Xcode-related # Xcode-related
**/dgph **/dgph
**/xcuserdata/ **/xcuserdata/

View File

@ -1 +1 @@
#include "ephemeral/Flutter-Generated.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig"

View File

@ -1 +1 @@
#include "ephemeral/Flutter-Generated.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig"

View File

@ -9,10 +9,12 @@ import package_info_plus
import path_provider_foundation import path_provider_foundation
import shared_preferences_foundation import shared_preferences_foundation
import sqflite import sqflite
import sqlite3_flutter_libs
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin"))
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>IDEDidComputeMac32BitWarning</key> <key>IDEDidComputeMac32BitWarning</key>
<true/> <true/>
</dict> </dict>
</plist> </plist>

View File

@ -1,87 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1300" LastUpgradeVersion = "1300"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"> buildImplicitDependencies = "YES">
<BuildActionEntries> <BuildActionEntries>
<BuildActionEntry <BuildActionEntry
buildForTesting = "YES" buildForTesting = "YES"
buildForRunning = "YES" buildForRunning = "YES"
buildForProfiling = "YES" buildForProfiling = "YES"
buildForArchiving = "YES" buildForArchiving = "YES"
buildForAnalyzing = "YES"> buildForAnalyzing = "YES">
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045" BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "tetra_stats.app" BuildableName = "tetra_stats.app"
BlueprintName = "Runner" BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj"> ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildActionEntry> </BuildActionEntry>
</BuildActionEntries> </BuildActionEntries>
</BuildAction> </BuildAction>
<TestAction <TestAction
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion> <MacroExpansion>
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045" BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "tetra_stats.app" BuildableName = "tetra_stats.app"
BlueprintName = "Runner" BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj"> ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference> </BuildableReference>
</MacroExpansion> </MacroExpansion>
<Testables> <Testables>
</Testables> </Testables>
</TestAction> </TestAction>
<LaunchAction <LaunchAction
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0" launchStyle = "0"
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO" ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES" debugDocumentVersioning = "YES"
debugServiceExtension = "internal" debugServiceExtension = "internal"
allowLocationSimulation = "YES"> allowLocationSimulation = "YES">
<BuildableProductRunnable <BuildableProductRunnable
runnableDebuggingMode = "0"> runnableDebuggingMode = "0">
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045" BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "tetra_stats.app" BuildableName = "tetra_stats.app"
BlueprintName = "Runner" BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj"> ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildableProductRunnable> </BuildableProductRunnable>
</LaunchAction> </LaunchAction>
<ProfileAction <ProfileAction
buildConfiguration = "Profile" buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES" shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = "" savedToolIdentifier = ""
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"> debugDocumentVersioning = "YES">
<BuildableProductRunnable <BuildableProductRunnable
runnableDebuggingMode = "0"> runnableDebuggingMode = "0">
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045" BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "tetra_stats.app" BuildableName = "tetra_stats.app"
BlueprintName = "Runner" BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj"> ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildableProductRunnable> </BuildableProductRunnable>
</ProfileAction> </ProfileAction>
<AnalyzeAction <AnalyzeAction
buildConfiguration = "Debug"> buildConfiguration = "Debug">
</AnalyzeAction> </AnalyzeAction>
<ArchiveAction <ArchiveAction
buildConfiguration = "Release" buildConfiguration = "Release"
revealArchiveInOrganizer = "YES"> revealArchiveInOrganizer = "YES">
</ArchiveAction> </ArchiveAction>
</Scheme> </Scheme>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Workspace <Workspace
version = "1.0"> version = "1.0">
<FileRef <FileRef
location = "group:Runner.xcodeproj"> location = "group:Runner.xcodeproj">
</FileRef> </FileRef>
</Workspace> </Workspace>

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>IDEDidComputeMac32BitWarning</key> <key>IDEDidComputeMac32BitWarning</key>
<true/> <true/>
</dict> </dict>
</plist> </plist>

View File

@ -1,9 +1,9 @@
import Cocoa import Cocoa
import FlutterMacOS import FlutterMacOS
@NSApplicationMain @NSApplicationMain
class AppDelegate: FlutterAppDelegate { class AppDelegate: FlutterAppDelegate {
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true return true
} }
} }

View File

@ -1,68 +1,68 @@
{ {
"images" : [ "images" : [
{ {
"size" : "16x16", "size" : "16x16",
"idiom" : "mac", "idiom" : "mac",
"filename" : "app_icon_16.png", "filename" : "app_icon_16.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"size" : "16x16", "size" : "16x16",
"idiom" : "mac", "idiom" : "mac",
"filename" : "app_icon_32.png", "filename" : "app_icon_32.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "32x32", "size" : "32x32",
"idiom" : "mac", "idiom" : "mac",
"filename" : "app_icon_32.png", "filename" : "app_icon_32.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"size" : "32x32", "size" : "32x32",
"idiom" : "mac", "idiom" : "mac",
"filename" : "app_icon_64.png", "filename" : "app_icon_64.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "128x128", "size" : "128x128",
"idiom" : "mac", "idiom" : "mac",
"filename" : "app_icon_128.png", "filename" : "app_icon_128.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"size" : "128x128", "size" : "128x128",
"idiom" : "mac", "idiom" : "mac",
"filename" : "app_icon_256.png", "filename" : "app_icon_256.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "256x256", "size" : "256x256",
"idiom" : "mac", "idiom" : "mac",
"filename" : "app_icon_256.png", "filename" : "app_icon_256.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"size" : "256x256", "size" : "256x256",
"idiom" : "mac", "idiom" : "mac",
"filename" : "app_icon_512.png", "filename" : "app_icon_512.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "512x512", "size" : "512x512",
"idiom" : "mac", "idiom" : "mac",
"filename" : "app_icon_512.png", "filename" : "app_icon_512.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"size" : "512x512", "size" : "512x512",
"idiom" : "mac", "idiom" : "mac",
"filename" : "app_icon_1024.png", "filename" : "app_icon_1024.png",
"scale" : "2x" "scale" : "2x"
} }
], ],
"info" : { "info" : {
"version" : 1, "version" : 1,
"author" : "xcode" "author" : "xcode"
} }
} }

View File

@ -1,343 +1,343 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct"> <document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies> <dependencies>
<deployment identifier="macosx"/> <deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/> <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<objects> <objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication"> <customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
<connections> <connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/> <outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
</connections> </connections>
</customObject> </customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/> <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/> <customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="Runner" customModuleProvider="target"> <customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="Runner" customModuleProvider="target">
<connections> <connections>
<outlet property="applicationMenu" destination="uQy-DD-JDr" id="XBo-yE-nKs"/> <outlet property="applicationMenu" destination="uQy-DD-JDr" id="XBo-yE-nKs"/>
<outlet property="mainFlutterWindow" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/> <outlet property="mainFlutterWindow" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
</connections> </connections>
</customObject> </customObject>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/> <customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6"> <menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items> <items>
<menuItem title="APP_NAME" id="1Xt-HY-uBw"> <menuItem title="APP_NAME" id="1Xt-HY-uBw">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="APP_NAME" systemMenu="apple" id="uQy-DD-JDr"> <menu key="submenu" title="APP_NAME" systemMenu="apple" id="uQy-DD-JDr">
<items> <items>
<menuItem title="About APP_NAME" id="5kV-Vb-QxS"> <menuItem title="About APP_NAME" id="5kV-Vb-QxS">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<connections> <connections>
<action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/> <action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/> <menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/> <menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/> <menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
<menuItem title="Services" id="NMo-om-nkz"> <menuItem title="Services" id="NMo-om-nkz">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/> <menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
</menuItem> </menuItem>
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/> <menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
<menuItem title="Hide APP_NAME" keyEquivalent="h" id="Olw-nP-bQN"> <menuItem title="Hide APP_NAME" keyEquivalent="h" id="Olw-nP-bQN">
<connections> <connections>
<action selector="hide:" target="-1" id="PnN-Uc-m68"/> <action selector="hide:" target="-1" id="PnN-Uc-m68"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO"> <menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/> <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections> <connections>
<action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/> <action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Show All" id="Kd2-mp-pUS"> <menuItem title="Show All" id="Kd2-mp-pUS">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<connections> <connections>
<action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/> <action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/> <menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
<menuItem title="Quit APP_NAME" keyEquivalent="q" id="4sb-4s-VLi"> <menuItem title="Quit APP_NAME" keyEquivalent="q" id="4sb-4s-VLi">
<connections> <connections>
<action selector="terminate:" target="-1" id="Te7-pn-YzF"/> <action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
</connections> </connections>
</menuItem> </menuItem>
</items> </items>
</menu> </menu>
</menuItem> </menuItem>
<menuItem title="Edit" id="5QF-Oa-p0T"> <menuItem title="Edit" id="5QF-Oa-p0T">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Edit" id="W48-6f-4Dl"> <menu key="submenu" title="Edit" id="W48-6f-4Dl">
<items> <items>
<menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg"> <menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
<connections> <connections>
<action selector="undo:" target="-1" id="M6e-cu-g7V"/> <action selector="undo:" target="-1" id="M6e-cu-g7V"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam"> <menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
<connections> <connections>
<action selector="redo:" target="-1" id="oIA-Rs-6OD"/> <action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/> <menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
<menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG"> <menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
<connections> <connections>
<action selector="cut:" target="-1" id="YJe-68-I9s"/> <action selector="cut:" target="-1" id="YJe-68-I9s"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU"> <menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
<connections> <connections>
<action selector="copy:" target="-1" id="G1f-GL-Joy"/> <action selector="copy:" target="-1" id="G1f-GL-Joy"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL"> <menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
<connections> <connections>
<action selector="paste:" target="-1" id="UvS-8e-Qdg"/> <action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk"> <menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/> <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections> <connections>
<action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/> <action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Delete" id="pa3-QI-u2k"> <menuItem title="Delete" id="pa3-QI-u2k">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<connections> <connections>
<action selector="delete:" target="-1" id="0Mk-Ml-PaM"/> <action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m"> <menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
<connections> <connections>
<action selector="selectAll:" target="-1" id="VNm-Mi-diN"/> <action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/> <menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
<menuItem title="Find" id="4EN-yA-p0u"> <menuItem title="Find" id="4EN-yA-p0u">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Find" id="1b7-l0-nxx"> <menu key="submenu" title="Find" id="1b7-l0-nxx">
<items> <items>
<menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W"> <menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
<connections> <connections>
<action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/> <action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz"> <menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/> <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections> <connections>
<action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/> <action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye"> <menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
<connections> <connections>
<action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/> <action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV"> <menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
<connections> <connections>
<action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/> <action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt"> <menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
<connections> <connections>
<action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/> <action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd"> <menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
<connections> <connections>
<action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/> <action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
</connections> </connections>
</menuItem> </menuItem>
</items> </items>
</menu> </menu>
</menuItem> </menuItem>
<menuItem title="Spelling and Grammar" id="Dv1-io-Yv7"> <menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Spelling" id="3IN-sU-3Bg"> <menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
<items> <items>
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI"> <menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
<connections> <connections>
<action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/> <action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7"> <menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
<connections> <connections>
<action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/> <action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem isSeparatorItem="YES" id="bNw-od-mp5"/> <menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
<menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN"> <menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<connections> <connections>
<action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/> <action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG"> <menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<connections> <connections>
<action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/> <action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Correct Spelling Automatically" id="78Y-hA-62v"> <menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<connections> <connections>
<action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/> <action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
</connections> </connections>
</menuItem> </menuItem>
</items> </items>
</menu> </menu>
</menuItem> </menuItem>
<menuItem title="Substitutions" id="9ic-FL-obx"> <menuItem title="Substitutions" id="9ic-FL-obx">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Substitutions" id="FeM-D8-WVr"> <menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
<items> <items>
<menuItem title="Show Substitutions" id="z6F-FW-3nz"> <menuItem title="Show Substitutions" id="z6F-FW-3nz">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<connections> <connections>
<action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/> <action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/> <menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
<menuItem title="Smart Copy/Paste" id="9yt-4B-nSM"> <menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<connections> <connections>
<action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/> <action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Smart Quotes" id="hQb-2v-fYv"> <menuItem title="Smart Quotes" id="hQb-2v-fYv">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<connections> <connections>
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/> <action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Smart Dashes" id="rgM-f4-ycn"> <menuItem title="Smart Dashes" id="rgM-f4-ycn">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<connections> <connections>
<action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/> <action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Smart Links" id="cwL-P1-jid"> <menuItem title="Smart Links" id="cwL-P1-jid">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<connections> <connections>
<action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/> <action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Data Detectors" id="tRr-pd-1PS"> <menuItem title="Data Detectors" id="tRr-pd-1PS">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<connections> <connections>
<action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/> <action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Text Replacement" id="HFQ-gK-NFA"> <menuItem title="Text Replacement" id="HFQ-gK-NFA">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<connections> <connections>
<action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/> <action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
</connections> </connections>
</menuItem> </menuItem>
</items> </items>
</menu> </menu>
</menuItem> </menuItem>
<menuItem title="Transformations" id="2oI-Rn-ZJC"> <menuItem title="Transformations" id="2oI-Rn-ZJC">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Transformations" id="c8a-y6-VQd"> <menu key="submenu" title="Transformations" id="c8a-y6-VQd">
<items> <items>
<menuItem title="Make Upper Case" id="vmV-6d-7jI"> <menuItem title="Make Upper Case" id="vmV-6d-7jI">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<connections> <connections>
<action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/> <action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Make Lower Case" id="d9M-CD-aMd"> <menuItem title="Make Lower Case" id="d9M-CD-aMd">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<connections> <connections>
<action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/> <action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Capitalize" id="UEZ-Bs-lqG"> <menuItem title="Capitalize" id="UEZ-Bs-lqG">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<connections> <connections>
<action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/> <action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
</connections> </connections>
</menuItem> </menuItem>
</items> </items>
</menu> </menu>
</menuItem> </menuItem>
<menuItem title="Speech" id="xrE-MZ-jX0"> <menuItem title="Speech" id="xrE-MZ-jX0">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Speech" id="3rS-ZA-NoH"> <menu key="submenu" title="Speech" id="3rS-ZA-NoH">
<items> <items>
<menuItem title="Start Speaking" id="Ynk-f8-cLZ"> <menuItem title="Start Speaking" id="Ynk-f8-cLZ">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<connections> <connections>
<action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/> <action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Stop Speaking" id="Oyz-dy-DGm"> <menuItem title="Stop Speaking" id="Oyz-dy-DGm">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<connections> <connections>
<action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/> <action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
</connections> </connections>
</menuItem> </menuItem>
</items> </items>
</menu> </menu>
</menuItem> </menuItem>
</items> </items>
</menu> </menu>
</menuItem> </menuItem>
<menuItem title="View" id="H8h-7b-M4v"> <menuItem title="View" id="H8h-7b-M4v">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="View" id="HyV-fh-RgO"> <menu key="submenu" title="View" id="HyV-fh-RgO">
<items> <items>
<menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa"> <menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/> <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections> <connections>
<action selector="toggleFullScreen:" target="-1" id="dU3-MA-1Rq"/> <action selector="toggleFullScreen:" target="-1" id="dU3-MA-1Rq"/>
</connections> </connections>
</menuItem> </menuItem>
</items> </items>
</menu> </menu>
</menuItem> </menuItem>
<menuItem title="Window" id="aUF-d1-5bR"> <menuItem title="Window" id="aUF-d1-5bR">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo"> <menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
<items> <items>
<menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV"> <menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
<connections> <connections>
<action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/> <action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Zoom" id="R4o-n2-Eq4"> <menuItem title="Zoom" id="R4o-n2-Eq4">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<connections> <connections>
<action selector="performZoom:" target="-1" id="DIl-cC-cCs"/> <action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/> <menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
<menuItem title="Bring All to Front" id="LE2-aR-0XJ"> <menuItem title="Bring All to Front" id="LE2-aR-0XJ">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<connections> <connections>
<action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/> <action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
</connections> </connections>
</menuItem> </menuItem>
</items> </items>
</menu> </menu>
</menuItem> </menuItem>
<menuItem title="Help" id="EPT-qC-fAb"> <menuItem title="Help" id="EPT-qC-fAb">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="rJ0-wn-3NY"/> <menu key="submenu" title="Help" systemMenu="help" id="rJ0-wn-3NY"/>
</menuItem> </menuItem>
</items> </items>
<point key="canvasLocation" x="142" y="-258"/> <point key="canvasLocation" x="142" y="-258"/>
</menu> </menu>
<window title="APP_NAME" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="MainFlutterWindow" customModule="Runner" customModuleProvider="target"> <window title="APP_NAME" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="MainFlutterWindow" customModule="Runner" customModuleProvider="target">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/> <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<rect key="contentRect" x="335" y="390" width="800" height="600"/> <rect key="contentRect" x="335" y="390" width="800" height="600"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1577"/> <rect key="screenRect" x="0.0" y="0.0" width="2560" height="1577"/>
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ"> <view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="800" height="600"/> <rect key="frame" x="0.0" y="0.0" width="800" height="600"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
</view> </view>
</window> </window>
</objects> </objects>
</document> </document>

View File

@ -1,14 +1,14 @@
// Application-level settings for the Runner target. // Application-level settings for the Runner target.
// //
// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the // 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 // future. If not, the values below would default to using the project name when this becomes a
// 'flutter create' template. // 'flutter create' template.
// The application's name. By default this is also the title of the Flutter window. // The application's name. By default this is also the title of the Flutter window.
PRODUCT_NAME = tetra_stats PRODUCT_NAME = tetra_stats
// The application's bundle identifier // The application's bundle identifier
PRODUCT_BUNDLE_IDENTIFIER = com.dan63.tetrastats.tetraStats PRODUCT_BUNDLE_IDENTIFIER = com.dan63.tetrastats.tetraStats
// The copyright displayed in application information // The copyright displayed in application information
PRODUCT_COPYRIGHT = Copyright © 2023 com.dan63.tetrastats. All rights reserved. PRODUCT_COPYRIGHT = Copyright © 2023 com.dan63.tetrastats. All rights reserved.

View File

@ -1,2 +1,2 @@
#include "../../Flutter/Flutter-Debug.xcconfig" #include "../../Flutter/Flutter-Debug.xcconfig"
#include "Warnings.xcconfig" #include "Warnings.xcconfig"

View File

@ -1,2 +1,2 @@
#include "../../Flutter/Flutter-Release.xcconfig" #include "../../Flutter/Flutter-Release.xcconfig"
#include "Warnings.xcconfig" #include "Warnings.xcconfig"

View File

@ -1,13 +1,13 @@
WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings
GCC_WARN_UNDECLARED_SELECTOR = YES GCC_WARN_UNDECLARED_SELECTOR = YES
CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
CLANG_WARN_PRAGMA_PACK = YES CLANG_WARN_PRAGMA_PACK = YES
CLANG_WARN_STRICT_PROTOTYPES = YES CLANG_WARN_STRICT_PROTOTYPES = YES
CLANG_WARN_COMMA = YES CLANG_WARN_COMMA = YES
GCC_WARN_STRICT_SELECTOR_MATCH = YES GCC_WARN_STRICT_SELECTOR_MATCH = YES
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
GCC_WARN_SHADOW = YES GCC_WARN_SHADOW = YES
CLANG_WARN_UNREACHABLE_CODE = YES CLANG_WARN_UNREACHABLE_CODE = YES

View File

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>com.apple.security.app-sandbox</key> <key>com.apple.security.app-sandbox</key>
<true/> <true/>
<key>com.apple.security.cs.allow-jit</key> <key>com.apple.security.cs.allow-jit</key>
<true/> <true/>
<key>com.apple.security.network.server</key> <key>com.apple.security.network.server</key>
<true/> <true/>
</dict> </dict>
</plist> </plist>

View File

@ -1,32 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string> <string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string> <string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIconFile</key> <key>CFBundleIconFile</key>
<string></string> <string></string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string> <string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string> <string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string> <string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string> <string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>
<string>$(PRODUCT_COPYRIGHT)</string> <string>$(PRODUCT_COPYRIGHT)</string>
<key>NSMainNibFile</key> <key>NSMainNibFile</key>
<string>MainMenu</string> <string>MainMenu</string>
<key>NSPrincipalClass</key> <key>NSPrincipalClass</key>
<string>NSApplication</string> <string>NSApplication</string>
</dict> </dict>
</plist> </plist>

View File

@ -1,15 +1,15 @@
import Cocoa import Cocoa
import FlutterMacOS import FlutterMacOS
class MainFlutterWindow: NSWindow { class MainFlutterWindow: NSWindow {
override func awakeFromNib() { override func awakeFromNib() {
let flutterViewController = FlutterViewController.init() let flutterViewController = FlutterViewController.init()
let windowFrame = self.frame let windowFrame = self.frame
self.contentViewController = flutterViewController self.contentViewController = flutterViewController
self.setFrame(windowFrame, display: true) self.setFrame(windowFrame, display: true)
RegisterGeneratedPlugins(registry: flutterViewController) RegisterGeneratedPlugins(registry: flutterViewController)
super.awakeFromNib() super.awakeFromNib()
} }
} }

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>com.apple.security.app-sandbox</key> <key>com.apple.security.app-sandbox</key>
<true/> <true/>
</dict> </dict>
</plist> </plist>

View File

@ -5,10 +5,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: async name: async
sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.10.0" version: "2.11.0"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
@ -21,10 +21,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: characters name: characters
sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.1" version: "1.3.0"
clock: clock:
dependency: transitive dependency: transitive
description: description:
@ -37,10 +37,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: collection name: collection
sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.17.0" version: "1.17.1"
cupertino_icons: cupertino_icons:
dependency: "direct main" dependency: "direct main"
description: description:
@ -116,10 +116,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: http name: http
sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" sha256: "4c3f04bfb64d3efd508d06b41b825542f08122d30bda4933fb95c069d22a4fa3"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.13.6" version: "1.0.0"
http_parser: http_parser:
dependency: transitive dependency: transitive
description: description:
@ -140,26 +140,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: js name: js
sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.5" version: "0.6.7"
lints: lints:
dependency: transitive dependency: transitive
description: description:
name: lints name: lints
sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.1" version: "2.1.1"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:
name: matcher name: matcher
sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.12.13" version: "0.12.15"
material_color_utilities: material_color_utilities:
dependency: transitive dependency: transitive
description: description:
@ -172,10 +172,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.8.0" version: "1.9.1"
package_info_plus: package_info_plus:
dependency: "direct main" dependency: "direct main"
description: description:
@ -196,10 +196,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: path name: path
sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.8.2" version: "1.8.3"
path_provider: path_provider:
dependency: "direct main" dependency: "direct main"
description: description:
@ -353,10 +353,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: sqflite_common name: sqflite_common
sha256: e77abf6ff961d69dfef41daccbb66b51e9983cdd5cb35bf30733598057401555 sha256: "8f7603f3f8f126740bc55c4ca2d1027aab4b74a1267a3e31ce51fe40e3b65b8f"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.5" version: "2.4.5+1"
sqflite_common_ffi: sqflite_common_ffi:
dependency: "direct main" dependency: "direct main"
description: description:
@ -373,6 +373,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.2" version: "1.11.2"
sqlite3_flutter_libs:
dependency: "direct main"
description:
name: sqlite3_flutter_libs
sha256: "1e20a88d5c7ae8400e009f38ddbe8b001800a6dffa37832481a86a219bc904c7"
url: "https://pub.dev"
source: hosted
version: "0.5.15"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:
@ -417,10 +425,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.4.16" version: "0.5.1"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
@ -441,10 +449,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: win32 name: win32
sha256: "5a751eddf9db89b3e5f9d50c20ab8612296e4e8db69009788d6c8b060a84191c" sha256: "7dacfda1edcca378031db9905ad7d7bd56b29fd1a90b0908b71a52a12c41e36b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.1.4" version: "5.0.3"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:
@ -454,5 +462,5 @@ packages:
source: hosted source: hosted
version: "1.0.0" version: "1.0.0"
sdks: sdks:
dart: ">=2.19.6 <3.0.0" dart: ">=3.0.0 <4.0.0"
flutter: ">=3.3.0" flutter: ">=3.3.0"

View File

@ -1,189 +1,190 @@
name: tetra_stats name: tetra_stats
description: Track your and other player stats in TETR.IO description: Track your and other player stats in TETR.IO
publish_to: 'none' publish_to: 'none'
# The following defines the version and build number for your application. # The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43 # A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +. # followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter # Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively. # build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode. # In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. # In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 0.1.0+3 version: 0.1.0+3
environment: environment:
sdk: '>=2.19.6 <3.0.0' sdk: '>=2.19.6 <3.0.0'
# Dependencies specify other packages that your package needs in order to work. # Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions # To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively, # consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to # dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer # the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`. # versions available, run `flutter pub outdated`.
dependencies: dependencies:
http: http:
flutter: flutter:
sdk: flutter sdk: flutter
cupertino_icons: ^1.0.2 cupertino_icons: ^1.0.2
vector_math: any vector_math: any
sqflite: ^2.2.8+2 sqflite: ^2.2.8+2
sqflite_common_ffi: any sqflite_common_ffi: any
path_provider: ^2.0.15 sqlite3_flutter_libs: ^0.5.15
path: ^1.8.2 path_provider: ^2.0.15
fl_chart: ^0.62.0 path: ^1.8.2
package_info_plus: ^4.0.2 fl_chart: ^0.62.0
shared_preferences: ^2.1.1 package_info_plus: ^4.0.2
intl: ^0.18.1 shared_preferences: ^2.1.1
intl: ^0.18.1
dev_dependencies:
flutter_test: dev_dependencies:
sdk: flutter flutter_test:
flutter_lints: ^2.0.0 sdk: flutter
flutter_lints: ^2.0.0
flutter:
uses-material-design: true flutter:
assets: uses-material-design: true
- res/avatars/tetrio_anon.png assets:
- res/avatars/tetrio_banned.png - res/avatars/tetrio_anon.png
- res/icons/kagari.png - res/avatars/tetrio_banned.png
- res/tetrio_tl_alpha_ranks/x.png - res/icons/kagari.png
- res/tetrio_tl_alpha_ranks/u.png - res/tetrio_tl_alpha_ranks/x.png
- res/tetrio_tl_alpha_ranks/ss.png - res/tetrio_tl_alpha_ranks/u.png
- res/tetrio_tl_alpha_ranks/s+.png - res/tetrio_tl_alpha_ranks/ss.png
- res/tetrio_tl_alpha_ranks/s.png - res/tetrio_tl_alpha_ranks/s+.png
- res/tetrio_tl_alpha_ranks/s-.png - res/tetrio_tl_alpha_ranks/s.png
- res/tetrio_tl_alpha_ranks/a+.png - res/tetrio_tl_alpha_ranks/s-.png
- res/tetrio_tl_alpha_ranks/a.png - res/tetrio_tl_alpha_ranks/a+.png
- res/tetrio_tl_alpha_ranks/a-.png - res/tetrio_tl_alpha_ranks/a.png
- res/tetrio_tl_alpha_ranks/b+.png - res/tetrio_tl_alpha_ranks/a-.png
- res/tetrio_tl_alpha_ranks/b.png - res/tetrio_tl_alpha_ranks/b+.png
- res/tetrio_tl_alpha_ranks/b-.png - res/tetrio_tl_alpha_ranks/b.png
- res/tetrio_tl_alpha_ranks/c+.png - res/tetrio_tl_alpha_ranks/b-.png
- res/tetrio_tl_alpha_ranks/c.png - res/tetrio_tl_alpha_ranks/c+.png
- res/tetrio_tl_alpha_ranks/c-.png - res/tetrio_tl_alpha_ranks/c.png
- res/tetrio_tl_alpha_ranks/d+.png - res/tetrio_tl_alpha_ranks/c-.png
- res/tetrio_tl_alpha_ranks/d.png - res/tetrio_tl_alpha_ranks/d+.png
- res/tetrio_tl_alpha_ranks/z.png - res/tetrio_tl_alpha_ranks/d.png
- res/tetrio_tl_alpha_ranks/z.png
- res/tetrio_badges/5mblast_1.png
- res/tetrio_badges/5mblast_10.png - res/tetrio_badges/5mblast_1.png
- res/tetrio_badges/5mblast_100.png - res/tetrio_badges/5mblast_10.png
- res/tetrio_badges/5mblast_1000.png - res/tetrio_badges/5mblast_100.png
- res/tetrio_badges/20tsd.png - res/tetrio_badges/5mblast_1000.png
- res/tetrio_badges/100player.png - res/tetrio_badges/20tsd.png
- res/tetrio_badges/allclear.png - res/tetrio_badges/100player.png
- res/tetrio_badges/bugbounty.png - res/tetrio_badges/allclear.png
- res/tetrio_badges/cometopen_1.png - res/tetrio_badges/bugbounty.png
- res/tetrio_badges/cometopen_2.png - res/tetrio_badges/cometopen_1.png
- res/tetrio_badges/cometopen_3.png - res/tetrio_badges/cometopen_2.png
- res/tetrio_badges/early-supporter.png - res/tetrio_badges/cometopen_3.png
- res/tetrio_badges/founder.png - res/tetrio_badges/early-supporter.png
- res/tetrio_badges/galactic2x2_1.png - res/tetrio_badges/founder.png
- res/tetrio_badges/ggc_2.png - res/tetrio_badges/galactic2x2_1.png
- res/tetrio_badges/heart.png - res/tetrio_badges/ggc_2.png
- res/tetrio_badges/hnprism_1.png - res/tetrio_badges/heart.png
- res/tetrio_badges/hnprism_2.png - res/tetrio_badges/hnprism_1.png
- res/tetrio_badges/hnprism_3.png - res/tetrio_badges/hnprism_2.png
- res/tetrio_badges/ift_1.png - res/tetrio_badges/hnprism_3.png
- res/tetrio_badges/ift_2.png - res/tetrio_badges/ift_1.png
- res/tetrio_badges/ift_3.png - res/tetrio_badges/ift_2.png
- res/tetrio_badges/infdev.png - res/tetrio_badges/ift_3.png
- res/tetrio_badges/kod_by_founder.png - res/tetrio_badges/infdev.png
- res/tetrio_badges/kod_founder.png - res/tetrio_badges/kod_by_founder.png
- res/tetrio_badges/leaderboard1.png - res/tetrio_badges/kod_founder.png
- res/tetrio_badges/mmc_tabi_1.png - res/tetrio_badges/leaderboard1.png
- res/tetrio_badges/mmc_tabi_2.png - res/tetrio_badges/mmc_tabi_1.png
- res/tetrio_badges/mmc_tabi_3.png - res/tetrio_badges/mmc_tabi_2.png
- res/tetrio_badges/mmc_tabi_superlobby.png - res/tetrio_badges/mmc_tabi_3.png
- res/tetrio_badges/mmc_tabi_superlobby2.png - res/tetrio_badges/mmc_tabi_superlobby.png
- res/tetrio_badges/mmc_tabi_superlobby3.png - res/tetrio_badges/mmc_tabi_superlobby2.png
- res/tetrio_badges/redgevo_1.png - res/tetrio_badges/mmc_tabi_superlobby3.png
- res/tetrio_badges/redgevo_2.png - res/tetrio_badges/redgevo_1.png
- res/tetrio_badges/redgevo_3.png - res/tetrio_badges/redgevo_2.png
- res/tetrio_badges/rengervl_1.png - res/tetrio_badges/redgevo_3.png
- res/tetrio_badges/rengervl_2.png - res/tetrio_badges/rengervl_1.png
- res/tetrio_badges/rengervl_3.png - res/tetrio_badges/rengervl_2.png
- res/tetrio_badges/sakurablend_1.png - res/tetrio_badges/rengervl_3.png
- res/tetrio_badges/sakurablend_2.png - res/tetrio_badges/sakurablend_1.png
- res/tetrio_badges/sakurablend_3.png - res/tetrio_badges/sakurablend_2.png
- res/tetrio_badges/scuncapped_1.png - res/tetrio_badges/sakurablend_3.png
- res/tetrio_badges/scuncapped_2.png - res/tetrio_badges/scuncapped_1.png
- res/tetrio_badges/scuncapped_3.png - res/tetrio_badges/scuncapped_2.png
- res/tetrio_badges/secretgrade.png - res/tetrio_badges/scuncapped_3.png
- res/tetrio_badges/sfu_raccoon_1.png - res/tetrio_badges/secretgrade.png
- res/tetrio_badges/sfu_raccoon_2.png - res/tetrio_badges/sfu_raccoon_1.png
- res/tetrio_badges/sfu_raccoon_3.png - res/tetrio_badges/sfu_raccoon_2.png
- res/tetrio_badges/superlobby.png - res/tetrio_badges/sfu_raccoon_3.png
- res/tetrio_badges/superlobby2.png - res/tetrio_badges/superlobby.png
- res/tetrio_badges/taws_u50_1.png - res/tetrio_badges/superlobby2.png
- res/tetrio_badges/taws_u50_2.png - res/tetrio_badges/taws_u50_1.png
- res/tetrio_badges/taws_u50_3.png - res/tetrio_badges/taws_u50_2.png
- res/tetrio_badges/tawshdsl_capped.png - res/tetrio_badges/taws_u50_3.png
- res/tetrio_badges/tawshdsl_uncapped.png - res/tetrio_badges/tawshdsl_capped.png
- res/tetrio_badges/tawsignite_expert.png - res/tetrio_badges/tawshdsl_uncapped.png
- res/tetrio_badges/tawslg.png - res/tetrio_badges/tawsignite_expert.png
- res/tetrio_badges/tetralympic_masters.png - res/tetrio_badges/tawslg.png
- res/tetrio_badges/ttsdtc_1.png - res/tetrio_badges/tetralympic_masters.png
- res/tetrio_badges/ttsdtc_2.png - res/tetrio_badges/ttsdtc_1.png
- res/tetrio_badges/ttsdtc_3.png - res/tetrio_badges/ttsdtc_2.png
- res/tetrio_badges/ubcea_1.png - res/tetrio_badges/ttsdtc_3.png
- res/tetrio_badges/ubcea_2.png - res/tetrio_badges/ubcea_1.png
- res/tetrio_badges/ubcea_3.png - res/tetrio_badges/ubcea_2.png
- res/tetrio_badges/underdog_1.png - res/tetrio_badges/ubcea_3.png
- res/tetrio_badges/underdog_2.png - res/tetrio_badges/underdog_1.png
- res/tetrio_badges/underdog_3.png - res/tetrio_badges/underdog_2.png
- res/tetrio_badges/underdog_predict.png - res/tetrio_badges/underdog_3.png
- res/tetrio_badges/wpl_1.png - res/tetrio_badges/underdog_predict.png
- res/tetrio_badges/wpl_2.png - res/tetrio_badges/wpl_1.png
- res/tetrio_badges/wpl_3.png - res/tetrio_badges/wpl_2.png
- res/tetrio_badges/wplc_1.png - res/tetrio_badges/wpl_3.png
- res/tetrio_badges/wplc_2.png - res/tetrio_badges/wplc_1.png
- res/tetrio_badges/wplc_3.png - res/tetrio_badges/wplc_2.png
- res/tetrio_badges/wplc_participation.png - res/tetrio_badges/wplc_3.png
- res/tetrio_badges/wplc_participation.png
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware # An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages # For details regarding adding assets from package dependencies, see
fonts: # https://flutter.dev/assets-and-images/#from-packages
- family: Eurostile Round fonts:
fonts: - family: Eurostile Round
- asset: res/fonts/EurostileRound-Black.ttf fonts:
- asset: res/fonts/EurostileRound-BlackItalic.ttf - asset: res/fonts/EurostileRound-Black.ttf
- asset: res/fonts/EurostileRound-Bold.ttf - asset: res/fonts/EurostileRound-BlackItalic.ttf
- asset: res/fonts/EurostileRound-BoldItalic.ttf - asset: res/fonts/EurostileRound-Bold.ttf
- asset: res/fonts/EurostileRound-Heavy.ttf - asset: res/fonts/EurostileRound-BoldItalic.ttf
weight: 900 - asset: res/fonts/EurostileRound-Heavy.ttf
- asset: res/fonts/EurostileRound-HeavyItalic.ttf weight: 900
weight: 900 - asset: res/fonts/EurostileRound-HeavyItalic.ttf
style: italic weight: 900
- asset: res/fonts/EurostileRound-Italic.ttf style: italic
style: italic - asset: res/fonts/EurostileRound-Italic.ttf
- asset: res/fonts/EurostileRound-Medium.ttf style: italic
- asset: res/fonts/EurostileRound-MediumItalic.ttf - asset: res/fonts/EurostileRound-Medium.ttf
weight: 500 - asset: res/fonts/EurostileRound-MediumItalic.ttf
style: italic weight: 500
- asset: res/fonts/EurostileRound-Regular.ttf style: italic
- family: Eurostile Round Condensed - asset: res/fonts/EurostileRound-Regular.ttf
fonts: - family: Eurostile Round Condensed
- asset: res/fonts/EurostileRoundCondensed-Heavy.ttf fonts:
- asset: res/fonts/EurostileRoundCondensed-HeavyItalic.ttf - asset: res/fonts/EurostileRoundCondensed-Heavy.ttf
- asset: res/fonts/EurostileRoundCondensed-Italic.ttf - asset: res/fonts/EurostileRoundCondensed-HeavyItalic.ttf
- asset: res/fonts/EurostileRoundCondensed-Regular.ttf - asset: res/fonts/EurostileRoundCondensed-Italic.ttf
- family: Eurostile Round Extended - asset: res/fonts/EurostileRoundCondensed-Regular.ttf
fonts: - family: Eurostile Round Extended
- asset: res/fonts/EurostileRoundExtended-Black.ttf fonts:
- asset: res/fonts/EurostileRoundExtended-BlackItalic.ttf - asset: res/fonts/EurostileRoundExtended-Black.ttf
weight: 900 - asset: res/fonts/EurostileRoundExtended-BlackItalic.ttf
style: italic weight: 900
- asset: res/fonts/EurostileRoundExtended-Italic.ttf style: italic
style: italic - asset: res/fonts/EurostileRoundExtended-Italic.ttf
- asset: res/fonts/EurostileRoundExtended-Medium.ttf style: italic
weight: 500 - asset: res/fonts/EurostileRoundExtended-Medium.ttf
- asset: res/fonts/EurostileRoundExtended-Regular.ttf weight: 500
- asset: res/fonts/EurostileRoundExtended-Regular.ttf

View File

@ -1,30 +1,30 @@
// This is a basic Flutter widget test. // This is a basic Flutter widget test.
// //
// To perform an interaction with a widget in your test, use the WidgetTester // To perform an interaction with a widget in your test, use the WidgetTester
// utility in the flutter_test package. For example, you can send tap and scroll // utility in the flutter_test package. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget // 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. // tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:tetra_stats/main.dart'; import 'package:tetra_stats/main.dart';
void main() { void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async { testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame. // Build our app and trigger a frame.
// await tester.pumpWidget(const MyApp()); // await tester.pumpWidget(const MyApp());
// //
// // Verify that our counter starts at 0. // // Verify that our counter starts at 0.
// expect(find.text('0'), findsOneWidget); // expect(find.text('0'), findsOneWidget);
// expect(find.text('1'), findsNothing); // expect(find.text('1'), findsNothing);
// //
// // Tap the '+' icon and trigger a frame. // // Tap the '+' icon and trigger a frame.
// await tester.tap(find.byIcon(Icons.add)); // await tester.tap(find.byIcon(Icons.add));
// await tester.pump(); // await tester.pump();
// //
// // Verify that our counter has incremented. // // Verify that our counter has incremented.
// expect(find.text('0'), findsNothing); // expect(find.text('0'), findsNothing);
// expect(find.text('1'), findsOneWidget); // expect(find.text('1'), findsOneWidget);
}); });
} }

View File

@ -1,59 +1,59 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<!-- <!--
If you are serving your web app in a path other than the root, change the If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from. href value below to reflect the base path you are serving from.
The path provided below has to start and end with a slash "/" in order for The path provided below has to start and end with a slash "/" in order for
it to work correctly. it to work correctly.
For more details: For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
This is a placeholder for base href that will be replaced by the value of This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`. the `--base-href` argument provided to `flutter build`.
--> -->
<base href="$FLUTTER_BASE_HREF"> <base href="$FLUTTER_BASE_HREF">
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible"> <meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="Track your and other player stats in TETR.IO"> <meta name="description" content="Track your and other player stats in TETR.IO">
<!-- iOS meta tags & icons --> <!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="tetra_stats"> <meta name="apple-mobile-web-app-title" content="tetra_stats">
<link rel="apple-touch-icon" href="icons/Icon-192.png"> <link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon --> <!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/> <link rel="icon" type="image/png" href="favicon.png"/>
<title>Tetra Stats</title> <title>Tetra Stats</title>
<link rel="manifest" href="manifest.json"> <link rel="manifest" href="manifest.json">
<script> <script>
// The value below is injected by flutter build, do not touch. // The value below is injected by flutter build, do not touch.
var serviceWorkerVersion = null; var serviceWorkerVersion = null;
</script> </script>
<!-- This script adds the flutter initialization JS code --> <!-- This script adds the flutter initialization JS code -->
<script src="flutter.js" defer></script> <script src="flutter.js" defer></script>
</head> </head>
<body> <body>
<script> <script>
window.addEventListener('load', function(ev) { window.addEventListener('load', function(ev) {
// Download main.dart.js // Download main.dart.js
_flutter.loader.loadEntrypoint({ _flutter.loader.loadEntrypoint({
serviceWorker: { serviceWorker: {
serviceWorkerVersion: serviceWorkerVersion, serviceWorkerVersion: serviceWorkerVersion,
}, },
onEntrypointLoaded: function(engineInitializer) { onEntrypointLoaded: function(engineInitializer) {
engineInitializer.initializeEngine().then(function(appRunner) { engineInitializer.initializeEngine().then(function(appRunner) {
appRunner.runApp(); appRunner.runApp();
}); });
} }
}); });
}); });
</script> </script>
</body> </body>
</html> </html>

View File

@ -1,35 +1,35 @@
{ {
"name": "tetra_stats", "name": "tetra_stats",
"short_name": "tetra_stats", "short_name": "tetra_stats",
"start_url": ".", "start_url": ".",
"display": "standalone", "display": "standalone",
"background_color": "#0175C2", "background_color": "#0175C2",
"theme_color": "#0175C2", "theme_color": "#0175C2",
"description": "Track your and other player stats in TETR.IO", "description": "Track your and other player stats in TETR.IO",
"orientation": "portrait-primary", "orientation": "portrait-primary",
"prefer_related_applications": false, "prefer_related_applications": false,
"icons": [ "icons": [
{ {
"src": "icons/Icon-192.png", "src": "icons/Icon-192.png",
"sizes": "192x192", "sizes": "192x192",
"type": "image/png" "type": "image/png"
}, },
{ {
"src": "icons/Icon-512.png", "src": "icons/Icon-512.png",
"sizes": "512x512", "sizes": "512x512",
"type": "image/png" "type": "image/png"
}, },
{ {
"src": "icons/Icon-maskable-192.png", "src": "icons/Icon-maskable-192.png",
"sizes": "192x192", "sizes": "192x192",
"type": "image/png", "type": "image/png",
"purpose": "maskable" "purpose": "maskable"
}, },
{ {
"src": "icons/Icon-maskable-512.png", "src": "icons/Icon-maskable-512.png",
"sizes": "512x512", "sizes": "512x512",
"type": "image/png", "type": "image/png",
"purpose": "maskable" "purpose": "maskable"
} }
] ]
} }

34
windows/.gitignore vendored
View File

@ -1,17 +1,17 @@
flutter/ephemeral/ flutter/ephemeral/
# Visual Studio user-specific files. # Visual Studio user-specific files.
*.suo *.suo
*.user *.user
*.userosscache *.userosscache
*.sln.docstates *.sln.docstates
# Visual Studio build-related files. # Visual Studio build-related files.
x64/ x64/
x86/ x86/
# Visual Studio cache files # Visual Studio cache files
# files ending in .cache can be ignored # files ending in .cache can be ignored
*.[Cc]ache *.[Cc]ache
# but keep track of directories ending in .cache # but keep track of directories ending in .cache
!*.[Cc]ache/ !*.[Cc]ache/

View File

@ -1,101 +1,101 @@
# Project-level configuration. # Project-level configuration.
cmake_minimum_required(VERSION 3.14) cmake_minimum_required(VERSION 3.14)
project(tetra_stats LANGUAGES CXX) project(tetra_stats LANGUAGES CXX)
# The name of the executable created for the application. Change this to change # The name of the executable created for the application. Change this to change
# the on-disk name of your application. # the on-disk name of your application.
set(BINARY_NAME "tetra_stats") set(BINARY_NAME "tetra_stats")
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent # Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake. # versions of CMake.
cmake_policy(SET CMP0063 NEW) cmake_policy(SET CMP0063 NEW)
# Define build configuration option. # Define build configuration option.
get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(IS_MULTICONFIG) if(IS_MULTICONFIG)
set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
CACHE STRING "" FORCE) CACHE STRING "" FORCE)
else() else()
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE "Debug" CACHE set(CMAKE_BUILD_TYPE "Debug" CACHE
STRING "Flutter build mode" FORCE) STRING "Flutter build mode" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Profile" "Release") "Debug" "Profile" "Release")
endif() endif()
endif() endif()
# Define settings for the Profile build mode. # Define settings for the Profile build mode.
set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_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_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
# Use Unicode for all projects. # Use Unicode for all projects.
add_definitions(-DUNICODE -D_UNICODE) add_definitions(-DUNICODE -D_UNICODE)
# Compilation settings that should be applied to most targets. # Compilation settings that should be applied to most targets.
# #
# Be cautious about adding new options here, as plugins use this function by # Be cautious about adding new options here, as plugins use this function by
# default. In most cases, you should add new options to specific targets instead # default. In most cases, you should add new options to specific targets instead
# of modifying this function. # of modifying this function.
function(APPLY_STANDARD_SETTINGS TARGET) function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_17) target_compile_features(${TARGET} PUBLIC cxx_std_17)
target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
target_compile_options(${TARGET} PRIVATE /EHsc) target_compile_options(${TARGET} PRIVATE /EHsc)
target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
target_compile_definitions(${TARGET} PRIVATE "$<$<CONFIG:Debug>:_DEBUG>") target_compile_definitions(${TARGET} PRIVATE "$<$<CONFIG:Debug>:_DEBUG>")
endfunction() endfunction()
# Flutter library and tool build rules. # Flutter library and tool build rules.
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
add_subdirectory(${FLUTTER_MANAGED_DIR}) add_subdirectory(${FLUTTER_MANAGED_DIR})
# Application build; see runner/CMakeLists.txt. # Application build; see runner/CMakeLists.txt.
add_subdirectory("runner") add_subdirectory("runner")
# Generated plugin build rules, which manage building the plugins and adding # Generated plugin build rules, which manage building the plugins and adding
# them to the application. # them to the application.
include(flutter/generated_plugins.cmake) include(flutter/generated_plugins.cmake)
# === Installation === # === Installation ===
# Support files are copied into place next to the executable, so that it can # 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) # 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. # so that building and running from within Visual Studio will work.
set(BUILD_BUNDLE_DIR "$<TARGET_FILE_DIR:${BINARY_NAME}>") set(BUILD_BUNDLE_DIR "$<TARGET_FILE_DIR:${BINARY_NAME}>")
# Make the "install" step default, as it's required to run. # Make the "install" step default, as it's required to run.
set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
endif() endif()
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
COMPONENT Runtime) COMPONENT Runtime)
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
COMPONENT Runtime) COMPONENT Runtime)
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime) COMPONENT Runtime)
if(PLUGIN_BUNDLED_LIBRARIES) if(PLUGIN_BUNDLED_LIBRARIES)
install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime) COMPONENT Runtime)
endif() endif()
# Fully re-copy the assets directory on each build to avoid having stale files # Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install. # from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets") set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
install(CODE " install(CODE "
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
" COMPONENT Runtime) " COMPONENT Runtime)
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
# Install the AOT library on non-Debug builds only. # Install the AOT library on non-Debug builds only.
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
CONFIGURATIONS Profile;Release CONFIGURATIONS Profile;Release
COMPONENT Runtime) COMPONENT Runtime)

View File

@ -1,104 +1,104 @@
# This file controls Flutter-level build steps. It should not be edited. # This file controls Flutter-level build steps. It should not be edited.
cmake_minimum_required(VERSION 3.14) cmake_minimum_required(VERSION 3.14)
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
# Configuration provided via flutter tool. # Configuration provided via flutter tool.
include(${EPHEMERAL_DIR}/generated_config.cmake) include(${EPHEMERAL_DIR}/generated_config.cmake)
# TODO: Move the rest of this into files in ephemeral. See # TODO: Move the rest of this into files in ephemeral. See
# https://github.com/flutter/flutter/issues/57146. # https://github.com/flutter/flutter/issues/57146.
set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
# === Flutter Library === # === Flutter Library ===
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
# Published to parent scope for install step. # Published to parent scope for install step.
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
list(APPEND FLUTTER_LIBRARY_HEADERS list(APPEND FLUTTER_LIBRARY_HEADERS
"flutter_export.h" "flutter_export.h"
"flutter_windows.h" "flutter_windows.h"
"flutter_messenger.h" "flutter_messenger.h"
"flutter_plugin_registrar.h" "flutter_plugin_registrar.h"
"flutter_texture_registrar.h" "flutter_texture_registrar.h"
) )
list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
add_library(flutter INTERFACE) add_library(flutter INTERFACE)
target_include_directories(flutter INTERFACE target_include_directories(flutter INTERFACE
"${EPHEMERAL_DIR}" "${EPHEMERAL_DIR}"
) )
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
add_dependencies(flutter flutter_assemble) add_dependencies(flutter flutter_assemble)
# === Wrapper === # === Wrapper ===
list(APPEND CPP_WRAPPER_SOURCES_CORE list(APPEND CPP_WRAPPER_SOURCES_CORE
"core_implementations.cc" "core_implementations.cc"
"standard_codec.cc" "standard_codec.cc"
) )
list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
list(APPEND CPP_WRAPPER_SOURCES_PLUGIN list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
"plugin_registrar.cc" "plugin_registrar.cc"
) )
list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
list(APPEND CPP_WRAPPER_SOURCES_APP list(APPEND CPP_WRAPPER_SOURCES_APP
"flutter_engine.cc" "flutter_engine.cc"
"flutter_view_controller.cc" "flutter_view_controller.cc"
) )
list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
# Wrapper sources needed for a plugin. # Wrapper sources needed for a plugin.
add_library(flutter_wrapper_plugin STATIC add_library(flutter_wrapper_plugin STATIC
${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_PLUGIN} ${CPP_WRAPPER_SOURCES_PLUGIN}
) )
apply_standard_settings(flutter_wrapper_plugin) apply_standard_settings(flutter_wrapper_plugin)
set_target_properties(flutter_wrapper_plugin PROPERTIES set_target_properties(flutter_wrapper_plugin PROPERTIES
POSITION_INDEPENDENT_CODE ON) POSITION_INDEPENDENT_CODE ON)
set_target_properties(flutter_wrapper_plugin PROPERTIES set_target_properties(flutter_wrapper_plugin PROPERTIES
CXX_VISIBILITY_PRESET hidden) CXX_VISIBILITY_PRESET hidden)
target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
target_include_directories(flutter_wrapper_plugin PUBLIC target_include_directories(flutter_wrapper_plugin PUBLIC
"${WRAPPER_ROOT}/include" "${WRAPPER_ROOT}/include"
) )
add_dependencies(flutter_wrapper_plugin flutter_assemble) add_dependencies(flutter_wrapper_plugin flutter_assemble)
# Wrapper sources needed for the runner. # Wrapper sources needed for the runner.
add_library(flutter_wrapper_app STATIC add_library(flutter_wrapper_app STATIC
${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_APP} ${CPP_WRAPPER_SOURCES_APP}
) )
apply_standard_settings(flutter_wrapper_app) apply_standard_settings(flutter_wrapper_app)
target_link_libraries(flutter_wrapper_app PUBLIC flutter) target_link_libraries(flutter_wrapper_app PUBLIC flutter)
target_include_directories(flutter_wrapper_app PUBLIC target_include_directories(flutter_wrapper_app PUBLIC
"${WRAPPER_ROOT}/include" "${WRAPPER_ROOT}/include"
) )
add_dependencies(flutter_wrapper_app flutter_assemble) add_dependencies(flutter_wrapper_app flutter_assemble)
# === Flutter tool backend === # === Flutter tool backend ===
# _phony_ is a non-existent file to force this command to run every time, # _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 # since currently there's no way to get a full input/output list from the
# flutter tool. # flutter tool.
set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
add_custom_command( add_custom_command(
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
${CPP_WRAPPER_SOURCES_APP} ${CPP_WRAPPER_SOURCES_APP}
${PHONY_OUTPUT} ${PHONY_OUTPUT}
COMMAND ${CMAKE_COMMAND} -E env COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT} ${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
windows-x64 $<CONFIG> windows-x64 $<CONFIG>
VERBATIM VERBATIM
) )
add_custom_target(flutter_assemble DEPENDS add_custom_target(flutter_assemble DEPENDS
"${FLUTTER_LIBRARY}" "${FLUTTER_LIBRARY}"
${FLUTTER_LIBRARY_HEADERS} ${FLUTTER_LIBRARY_HEADERS}
${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_PLUGIN} ${CPP_WRAPPER_SOURCES_PLUGIN}
${CPP_WRAPPER_SOURCES_APP} ${CPP_WRAPPER_SOURCES_APP}
) )

View File

@ -6,6 +6,9 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
Sqlite3FlutterLibsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin"));
} }

View File

@ -3,6 +3,7 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
sqlite3_flutter_libs
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST

View File

@ -1,40 +1,40 @@
cmake_minimum_required(VERSION 3.14) cmake_minimum_required(VERSION 3.14)
project(runner LANGUAGES CXX) project(runner LANGUAGES CXX)
# Define the application target. To change its name, change BINARY_NAME in the # Define the application target. To change its name, change BINARY_NAME in the
# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer
# work. # work.
# #
# Any new source files that you add to the application should be added here. # Any new source files that you add to the application should be added here.
add_executable(${BINARY_NAME} WIN32 add_executable(${BINARY_NAME} WIN32
"flutter_window.cpp" "flutter_window.cpp"
"main.cpp" "main.cpp"
"utils.cpp" "utils.cpp"
"win32_window.cpp" "win32_window.cpp"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
"Runner.rc" "Runner.rc"
"runner.exe.manifest" "runner.exe.manifest"
) )
# Apply the standard set of build settings. This can be removed for applications # Apply the standard set of build settings. This can be removed for applications
# that need different build settings. # that need different build settings.
apply_standard_settings(${BINARY_NAME}) apply_standard_settings(${BINARY_NAME})
# Add preprocessor definitions for the build version. # Add preprocessor definitions for the build version.
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"")
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}")
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}")
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}")
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}")
# Disable Windows macros that collide with C++ standard library functions. # Disable Windows macros that collide with C++ standard library functions.
target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
# Add dependency libraries and include directories. Add any application-specific # Add dependency libraries and include directories. Add any application-specific
# dependencies here. # dependencies here.
target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib")
target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
# Run the Flutter tool portions of the build. This must not be removed. # Run the Flutter tool portions of the build. This must not be removed.
add_dependencies(${BINARY_NAME} flutter_assemble) add_dependencies(${BINARY_NAME} flutter_assemble)

View File

@ -1,121 +1,121 @@
// Microsoft Visual C++ generated resource script. // Microsoft Visual C++ generated resource script.
// //
#pragma code_page(65001) #pragma code_page(65001)
#include "resource.h" #include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS #define APSTUDIO_READONLY_SYMBOLS
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// //
// Generated from the TEXTINCLUDE 2 resource. // Generated from the TEXTINCLUDE 2 resource.
// //
#include "winres.h" #include "winres.h"
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS #undef APSTUDIO_READONLY_SYMBOLS
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// English (United States) resources // English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#ifdef APSTUDIO_INVOKED #ifdef APSTUDIO_INVOKED
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// //
// TEXTINCLUDE // TEXTINCLUDE
// //
1 TEXTINCLUDE 1 TEXTINCLUDE
BEGIN BEGIN
"resource.h\0" "resource.h\0"
END END
2 TEXTINCLUDE 2 TEXTINCLUDE
BEGIN BEGIN
"#include ""winres.h""\r\n" "#include ""winres.h""\r\n"
"\0" "\0"
END END
3 TEXTINCLUDE 3 TEXTINCLUDE
BEGIN BEGIN
"\r\n" "\r\n"
"\0" "\0"
END END
#endif // APSTUDIO_INVOKED #endif // APSTUDIO_INVOKED
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// //
// Icon // Icon
// //
// Icon with lowest ID value placed first to ensure application icon // Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems. // remains consistent on all systems.
IDI_APP_ICON ICON "resources\\app_icon.ico" IDI_APP_ICON ICON "resources\\app_icon.ico"
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// //
// Version // Version
// //
#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) #if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD)
#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD #define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD
#else #else
#define VERSION_AS_NUMBER 1,0,0,0 #define VERSION_AS_NUMBER 1,0,0,0
#endif #endif
#if defined(FLUTTER_VERSION) #if defined(FLUTTER_VERSION)
#define VERSION_AS_STRING FLUTTER_VERSION #define VERSION_AS_STRING FLUTTER_VERSION
#else #else
#define VERSION_AS_STRING "1.0.0" #define VERSION_AS_STRING "1.0.0"
#endif #endif
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION_AS_NUMBER FILEVERSION VERSION_AS_NUMBER
PRODUCTVERSION VERSION_AS_NUMBER PRODUCTVERSION VERSION_AS_NUMBER
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG FILEFLAGS VS_FF_DEBUG
#else #else
FILEFLAGS 0x0L FILEFLAGS 0x0L
#endif #endif
FILEOS VOS__WINDOWS32 FILEOS VOS__WINDOWS32
FILETYPE VFT_APP FILETYPE VFT_APP
FILESUBTYPE 0x0L FILESUBTYPE 0x0L
BEGIN BEGIN
BLOCK "StringFileInfo" BLOCK "StringFileInfo"
BEGIN BEGIN
BLOCK "040904e4" BLOCK "040904e4"
BEGIN BEGIN
VALUE "CompanyName", "com.dan63.tetrastats" "\0" VALUE "CompanyName", "com.dan63.tetrastats" "\0"
VALUE "FileDescription", "tetra_stats" "\0" VALUE "FileDescription", "tetra_stats" "\0"
VALUE "FileVersion", VERSION_AS_STRING "\0" VALUE "FileVersion", VERSION_AS_STRING "\0"
VALUE "InternalName", "tetra_stats" "\0" VALUE "InternalName", "tetra_stats" "\0"
VALUE "LegalCopyright", "Copyright (C) 2023 com.dan63.tetrastats. All rights reserved." "\0" VALUE "LegalCopyright", "Copyright (C) 2023 com.dan63.tetrastats. All rights reserved." "\0"
VALUE "OriginalFilename", "tetra_stats.exe" "\0" VALUE "OriginalFilename", "tetra_stats.exe" "\0"
VALUE "ProductName", "tetra_stats" "\0" VALUE "ProductName", "tetra_stats" "\0"
VALUE "ProductVersion", VERSION_AS_STRING "\0" VALUE "ProductVersion", VERSION_AS_STRING "\0"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"
BEGIN BEGIN
VALUE "Translation", 0x409, 1252 VALUE "Translation", 0x409, 1252
END END
END END
#endif // English (United States) resources #endif // English (United States) resources
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED #ifndef APSTUDIO_INVOKED
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// //
// Generated from the TEXTINCLUDE 3 resource. // Generated from the TEXTINCLUDE 3 resource.
// //
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED #endif // not APSTUDIO_INVOKED

View File

@ -1,66 +1,66 @@
#include "flutter_window.h" #include "flutter_window.h"
#include <optional> #include <optional>
#include "flutter/generated_plugin_registrant.h" #include "flutter/generated_plugin_registrant.h"
FlutterWindow::FlutterWindow(const flutter::DartProject& project) FlutterWindow::FlutterWindow(const flutter::DartProject& project)
: project_(project) {} : project_(project) {}
FlutterWindow::~FlutterWindow() {} FlutterWindow::~FlutterWindow() {}
bool FlutterWindow::OnCreate() { bool FlutterWindow::OnCreate() {
if (!Win32Window::OnCreate()) { if (!Win32Window::OnCreate()) {
return false; return false;
} }
RECT frame = GetClientArea(); RECT frame = GetClientArea();
// The size here must match the window dimensions to avoid unnecessary surface // The size here must match the window dimensions to avoid unnecessary surface
// creation / destruction in the startup path. // creation / destruction in the startup path.
flutter_controller_ = std::make_unique<flutter::FlutterViewController>( flutter_controller_ = std::make_unique<flutter::FlutterViewController>(
frame.right - frame.left, frame.bottom - frame.top, project_); frame.right - frame.left, frame.bottom - frame.top, project_);
// Ensure that basic setup of the controller was successful. // Ensure that basic setup of the controller was successful.
if (!flutter_controller_->engine() || !flutter_controller_->view()) { if (!flutter_controller_->engine() || !flutter_controller_->view()) {
return false; return false;
} }
RegisterPlugins(flutter_controller_->engine()); RegisterPlugins(flutter_controller_->engine());
SetChildContent(flutter_controller_->view()->GetNativeWindow()); SetChildContent(flutter_controller_->view()->GetNativeWindow());
flutter_controller_->engine()->SetNextFrameCallback([&]() { flutter_controller_->engine()->SetNextFrameCallback([&]() {
this->Show(); this->Show();
}); });
return true; return true;
} }
void FlutterWindow::OnDestroy() { void FlutterWindow::OnDestroy() {
if (flutter_controller_) { if (flutter_controller_) {
flutter_controller_ = nullptr; flutter_controller_ = nullptr;
} }
Win32Window::OnDestroy(); Win32Window::OnDestroy();
} }
LRESULT LRESULT
FlutterWindow::MessageHandler(HWND hwnd, UINT const message, FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
WPARAM const wparam, WPARAM const wparam,
LPARAM const lparam) noexcept { LPARAM const lparam) noexcept {
// Give Flutter, including plugins, an opportunity to handle window messages. // Give Flutter, including plugins, an opportunity to handle window messages.
if (flutter_controller_) { if (flutter_controller_) {
std::optional<LRESULT> result = std::optional<LRESULT> result =
flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
lparam); lparam);
if (result) { if (result) {
return *result; return *result;
} }
} }
switch (message) { switch (message) {
case WM_FONTCHANGE: case WM_FONTCHANGE:
flutter_controller_->engine()->ReloadSystemFonts(); flutter_controller_->engine()->ReloadSystemFonts();
break; break;
} }
return Win32Window::MessageHandler(hwnd, message, wparam, lparam); return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
} }

View File

@ -1,33 +1,33 @@
#ifndef RUNNER_FLUTTER_WINDOW_H_ #ifndef RUNNER_FLUTTER_WINDOW_H_
#define RUNNER_FLUTTER_WINDOW_H_ #define RUNNER_FLUTTER_WINDOW_H_
#include <flutter/dart_project.h> #include <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h> #include <flutter/flutter_view_controller.h>
#include <memory> #include <memory>
#include "win32_window.h" #include "win32_window.h"
// A window that does nothing but host a Flutter view. // A window that does nothing but host a Flutter view.
class FlutterWindow : public Win32Window { class FlutterWindow : public Win32Window {
public: public:
// Creates a new FlutterWindow hosting a Flutter view running |project|. // Creates a new FlutterWindow hosting a Flutter view running |project|.
explicit FlutterWindow(const flutter::DartProject& project); explicit FlutterWindow(const flutter::DartProject& project);
virtual ~FlutterWindow(); virtual ~FlutterWindow();
protected: protected:
// Win32Window: // Win32Window:
bool OnCreate() override; bool OnCreate() override;
void OnDestroy() override; void OnDestroy() override;
LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
LPARAM const lparam) noexcept override; LPARAM const lparam) noexcept override;
private: private:
// The project to run. // The project to run.
flutter::DartProject project_; flutter::DartProject project_;
// The Flutter instance hosted by this window. // The Flutter instance hosted by this window.
std::unique_ptr<flutter::FlutterViewController> flutter_controller_; std::unique_ptr<flutter::FlutterViewController> flutter_controller_;
}; };
#endif // RUNNER_FLUTTER_WINDOW_H_ #endif // RUNNER_FLUTTER_WINDOW_H_

View File

@ -1,43 +1,43 @@
#include <flutter/dart_project.h> #include <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h> #include <flutter/flutter_view_controller.h>
#include <windows.h> #include <windows.h>
#include "flutter_window.h" #include "flutter_window.h"
#include "utils.h" #include "utils.h"
int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
_In_ wchar_t *command_line, _In_ int show_command) { _In_ wchar_t *command_line, _In_ int show_command) {
// Attach to console when present (e.g., 'flutter run') or create a // Attach to console when present (e.g., 'flutter run') or create a
// new console when running with a debugger. // new console when running with a debugger.
if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
CreateAndAttachConsole(); CreateAndAttachConsole();
} }
// Initialize COM, so that it is available for use in the library and/or // Initialize COM, so that it is available for use in the library and/or
// plugins. // plugins.
::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
flutter::DartProject project(L"data"); flutter::DartProject project(L"data");
std::vector<std::string> command_line_arguments = std::vector<std::string> command_line_arguments =
GetCommandLineArguments(); GetCommandLineArguments();
project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
FlutterWindow window(project); FlutterWindow window(project);
Win32Window::Point origin(10, 10); Win32Window::Point origin(10, 10);
Win32Window::Size size(1280, 720); Win32Window::Size size(1280, 720);
if (!window.Create(L"tetra_stats", origin, size)) { if (!window.Create(L"tetra_stats", origin, size)) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
window.SetQuitOnClose(true); window.SetQuitOnClose(true);
::MSG msg; ::MSG msg;
while (::GetMessage(&msg, nullptr, 0, 0)) { while (::GetMessage(&msg, nullptr, 0, 0)) {
::TranslateMessage(&msg); ::TranslateMessage(&msg);
::DispatchMessage(&msg); ::DispatchMessage(&msg);
} }
::CoUninitialize(); ::CoUninitialize();
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@ -1,16 +1,16 @@
//{{NO_DEPENDENCIES}} //{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file. // Microsoft Visual C++ generated include file.
// Used by Runner.rc // Used by Runner.rc
// //
#define IDI_APP_ICON 101 #define IDI_APP_ICON 101
// Next default values for new objects // Next default values for new objects
// //
#ifdef APSTUDIO_INVOKED #ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS #ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102 #define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101 #define _APS_NEXT_SYMED_VALUE 101
#endif #endif
#endif #endif

View File

@ -1,20 +1,20 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<application xmlns="urn:schemas-microsoft-com:asm.v3"> <application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings> <windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness> <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</windowsSettings> </windowsSettings>
</application> </application>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application> <application>
<!-- Windows 10 and Windows 11 --> <!-- Windows 10 and Windows 11 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- Windows 8.1 --> <!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows 8 --> <!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!-- Windows 7 --> <!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
</application> </application>
</compatibility> </compatibility>
</assembly> </assembly>

View File

@ -1,64 +1,64 @@
#include "utils.h" #include "utils.h"
#include <flutter_windows.h> #include <flutter_windows.h>
#include <io.h> #include <io.h>
#include <stdio.h> #include <stdio.h>
#include <windows.h> #include <windows.h>
#include <iostream> #include <iostream>
void CreateAndAttachConsole() { void CreateAndAttachConsole() {
if (::AllocConsole()) { if (::AllocConsole()) {
FILE *unused; FILE *unused;
if (freopen_s(&unused, "CONOUT$", "w", stdout)) { if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
_dup2(_fileno(stdout), 1); _dup2(_fileno(stdout), 1);
} }
if (freopen_s(&unused, "CONOUT$", "w", stderr)) { if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
_dup2(_fileno(stdout), 2); _dup2(_fileno(stdout), 2);
} }
std::ios::sync_with_stdio(); std::ios::sync_with_stdio();
FlutterDesktopResyncOutputStreams(); FlutterDesktopResyncOutputStreams();
} }
} }
std::vector<std::string> GetCommandLineArguments() { std::vector<std::string> GetCommandLineArguments() {
// Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.
int argc; int argc;
wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
if (argv == nullptr) { if (argv == nullptr) {
return std::vector<std::string>(); return std::vector<std::string>();
} }
std::vector<std::string> command_line_arguments; std::vector<std::string> command_line_arguments;
// Skip the first argument as it's the binary name. // Skip the first argument as it's the binary name.
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
command_line_arguments.push_back(Utf8FromUtf16(argv[i])); command_line_arguments.push_back(Utf8FromUtf16(argv[i]));
} }
::LocalFree(argv); ::LocalFree(argv);
return command_line_arguments; return command_line_arguments;
} }
std::string Utf8FromUtf16(const wchar_t* utf16_string) { std::string Utf8FromUtf16(const wchar_t* utf16_string) {
if (utf16_string == nullptr) { if (utf16_string == nullptr) {
return std::string(); return std::string();
} }
int target_length = ::WideCharToMultiByte( int target_length = ::WideCharToMultiByte(
CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
-1, nullptr, 0, nullptr, nullptr); -1, nullptr, 0, nullptr, nullptr);
std::string utf8_string; std::string utf8_string;
if (target_length == 0 || target_length > utf8_string.max_size()) { if (target_length == 0 || target_length > utf8_string.max_size()) {
return utf8_string; return utf8_string;
} }
utf8_string.resize(target_length); utf8_string.resize(target_length);
int converted_length = ::WideCharToMultiByte( int converted_length = ::WideCharToMultiByte(
CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
-1, utf8_string.data(), -1, utf8_string.data(),
target_length, nullptr, nullptr); target_length, nullptr, nullptr);
if (converted_length == 0) { if (converted_length == 0) {
return std::string(); return std::string();
} }
return utf8_string; return utf8_string;
} }

View File

@ -1,19 +1,19 @@
#ifndef RUNNER_UTILS_H_ #ifndef RUNNER_UTILS_H_
#define RUNNER_UTILS_H_ #define RUNNER_UTILS_H_
#include <string> #include <string>
#include <vector> #include <vector>
// Creates a console for the process, and redirects stdout and stderr to // Creates a console for the process, and redirects stdout and stderr to
// it for both the runner and the Flutter library. // it for both the runner and the Flutter library.
void CreateAndAttachConsole(); void CreateAndAttachConsole();
// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string // 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. // encoded in UTF-8. Returns an empty std::string on failure.
std::string Utf8FromUtf16(const wchar_t* utf16_string); std::string Utf8FromUtf16(const wchar_t* utf16_string);
// Gets the command line arguments passed in as a std::vector<std::string>, // Gets the command line arguments passed in as a std::vector<std::string>,
// encoded in UTF-8. Returns an empty std::vector<std::string> on failure. // encoded in UTF-8. Returns an empty std::vector<std::string> on failure.
std::vector<std::string> GetCommandLineArguments(); std::vector<std::string> GetCommandLineArguments();
#endif // RUNNER_UTILS_H_ #endif // RUNNER_UTILS_H_

View File

@ -1,288 +1,288 @@
#include "win32_window.h" #include "win32_window.h"
#include <dwmapi.h> #include <dwmapi.h>
#include <flutter_windows.h> #include <flutter_windows.h>
#include "resource.h" #include "resource.h"
namespace { namespace {
/// Window attribute that enables dark mode window decorations. /// Window attribute that enables dark mode window decorations.
/// ///
/// Redefined in case the developer's machine has a Windows SDK older than /// Redefined in case the developer's machine has a Windows SDK older than
/// version 10.0.22000.0. /// version 10.0.22000.0.
/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute /// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE #ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 #define DWMWA_USE_IMMERSIVE_DARK_MODE 20
#endif #endif
constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
/// Registry key for app theme preference. /// Registry key for app theme preference.
/// ///
/// A value of 0 indicates apps should use dark mode. A non-zero or missing /// A value of 0 indicates apps should use dark mode. A non-zero or missing
/// value indicates apps should use light mode. /// value indicates apps should use light mode.
constexpr const wchar_t kGetPreferredBrightnessRegKey[] = constexpr const wchar_t kGetPreferredBrightnessRegKey[] =
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme";
// The number of Win32Window objects that currently exist. // The number of Win32Window objects that currently exist.
static int g_active_window_count = 0; static int g_active_window_count = 0;
using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
// Scale helper to convert logical scaler values to physical using passed in // Scale helper to convert logical scaler values to physical using passed in
// scale factor // scale factor
int Scale(int source, double scale_factor) { int Scale(int source, double scale_factor) {
return static_cast<int>(source * scale_factor); return static_cast<int>(source * scale_factor);
} }
// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. // Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
// This API is only needed for PerMonitor V1 awareness mode. // This API is only needed for PerMonitor V1 awareness mode.
void EnableFullDpiSupportIfAvailable(HWND hwnd) { void EnableFullDpiSupportIfAvailable(HWND hwnd) {
HMODULE user32_module = LoadLibraryA("User32.dll"); HMODULE user32_module = LoadLibraryA("User32.dll");
if (!user32_module) { if (!user32_module) {
return; return;
} }
auto enable_non_client_dpi_scaling = auto enable_non_client_dpi_scaling =
reinterpret_cast<EnableNonClientDpiScaling*>( reinterpret_cast<EnableNonClientDpiScaling*>(
GetProcAddress(user32_module, "EnableNonClientDpiScaling")); GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
if (enable_non_client_dpi_scaling != nullptr) { if (enable_non_client_dpi_scaling != nullptr) {
enable_non_client_dpi_scaling(hwnd); enable_non_client_dpi_scaling(hwnd);
} }
FreeLibrary(user32_module); FreeLibrary(user32_module);
} }
} // namespace } // namespace
// Manages the Win32Window's window class registration. // Manages the Win32Window's window class registration.
class WindowClassRegistrar { class WindowClassRegistrar {
public: public:
~WindowClassRegistrar() = default; ~WindowClassRegistrar() = default;
// Returns the singleton registar instance. // Returns the singleton registar instance.
static WindowClassRegistrar* GetInstance() { static WindowClassRegistrar* GetInstance() {
if (!instance_) { if (!instance_) {
instance_ = new WindowClassRegistrar(); instance_ = new WindowClassRegistrar();
} }
return instance_; return instance_;
} }
// Returns the name of the window class, registering the class if it hasn't // Returns the name of the window class, registering the class if it hasn't
// previously been registered. // previously been registered.
const wchar_t* GetWindowClass(); const wchar_t* GetWindowClass();
// Unregisters the window class. Should only be called if there are no // Unregisters the window class. Should only be called if there are no
// instances of the window. // instances of the window.
void UnregisterWindowClass(); void UnregisterWindowClass();
private: private:
WindowClassRegistrar() = default; WindowClassRegistrar() = default;
static WindowClassRegistrar* instance_; static WindowClassRegistrar* instance_;
bool class_registered_ = false; bool class_registered_ = false;
}; };
WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
const wchar_t* WindowClassRegistrar::GetWindowClass() { const wchar_t* WindowClassRegistrar::GetWindowClass() {
if (!class_registered_) { if (!class_registered_) {
WNDCLASS window_class{}; WNDCLASS window_class{};
window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
window_class.lpszClassName = kWindowClassName; window_class.lpszClassName = kWindowClassName;
window_class.style = CS_HREDRAW | CS_VREDRAW; window_class.style = CS_HREDRAW | CS_VREDRAW;
window_class.cbClsExtra = 0; window_class.cbClsExtra = 0;
window_class.cbWndExtra = 0; window_class.cbWndExtra = 0;
window_class.hInstance = GetModuleHandle(nullptr); window_class.hInstance = GetModuleHandle(nullptr);
window_class.hIcon = window_class.hIcon =
LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
window_class.hbrBackground = 0; window_class.hbrBackground = 0;
window_class.lpszMenuName = nullptr; window_class.lpszMenuName = nullptr;
window_class.lpfnWndProc = Win32Window::WndProc; window_class.lpfnWndProc = Win32Window::WndProc;
RegisterClass(&window_class); RegisterClass(&window_class);
class_registered_ = true; class_registered_ = true;
} }
return kWindowClassName; return kWindowClassName;
} }
void WindowClassRegistrar::UnregisterWindowClass() { void WindowClassRegistrar::UnregisterWindowClass() {
UnregisterClass(kWindowClassName, nullptr); UnregisterClass(kWindowClassName, nullptr);
class_registered_ = false; class_registered_ = false;
} }
Win32Window::Win32Window() { Win32Window::Win32Window() {
++g_active_window_count; ++g_active_window_count;
} }
Win32Window::~Win32Window() { Win32Window::~Win32Window() {
--g_active_window_count; --g_active_window_count;
Destroy(); Destroy();
} }
bool Win32Window::Create(const std::wstring& title, bool Win32Window::Create(const std::wstring& title,
const Point& origin, const Point& origin,
const Size& size) { const Size& size) {
Destroy(); Destroy();
const wchar_t* window_class = const wchar_t* window_class =
WindowClassRegistrar::GetInstance()->GetWindowClass(); WindowClassRegistrar::GetInstance()->GetWindowClass();
const POINT target_point = {static_cast<LONG>(origin.x), const POINT target_point = {static_cast<LONG>(origin.x),
static_cast<LONG>(origin.y)}; static_cast<LONG>(origin.y)};
HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
double scale_factor = dpi / 96.0; double scale_factor = dpi / 96.0;
HWND window = CreateWindow( HWND window = CreateWindow(
window_class, title.c_str(), WS_OVERLAPPEDWINDOW, window_class, title.c_str(), WS_OVERLAPPEDWINDOW,
Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
Scale(size.width, scale_factor), Scale(size.height, scale_factor), Scale(size.width, scale_factor), Scale(size.height, scale_factor),
nullptr, nullptr, GetModuleHandle(nullptr), this); nullptr, nullptr, GetModuleHandle(nullptr), this);
if (!window) { if (!window) {
return false; return false;
} }
UpdateTheme(window); UpdateTheme(window);
return OnCreate(); return OnCreate();
} }
bool Win32Window::Show() { bool Win32Window::Show() {
return ShowWindow(window_handle_, SW_SHOWNORMAL); return ShowWindow(window_handle_, SW_SHOWNORMAL);
} }
// static // static
LRESULT CALLBACK Win32Window::WndProc(HWND const window, LRESULT CALLBACK Win32Window::WndProc(HWND const window,
UINT const message, UINT const message,
WPARAM const wparam, WPARAM const wparam,
LPARAM const lparam) noexcept { LPARAM const lparam) noexcept {
if (message == WM_NCCREATE) { if (message == WM_NCCREATE) {
auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam); auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam);
SetWindowLongPtr(window, GWLP_USERDATA, SetWindowLongPtr(window, GWLP_USERDATA,
reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams)); reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));
auto that = static_cast<Win32Window*>(window_struct->lpCreateParams); auto that = static_cast<Win32Window*>(window_struct->lpCreateParams);
EnableFullDpiSupportIfAvailable(window); EnableFullDpiSupportIfAvailable(window);
that->window_handle_ = window; that->window_handle_ = window;
} else if (Win32Window* that = GetThisFromHandle(window)) { } else if (Win32Window* that = GetThisFromHandle(window)) {
return that->MessageHandler(window, message, wparam, lparam); return that->MessageHandler(window, message, wparam, lparam);
} }
return DefWindowProc(window, message, wparam, lparam); return DefWindowProc(window, message, wparam, lparam);
} }
LRESULT LRESULT
Win32Window::MessageHandler(HWND hwnd, Win32Window::MessageHandler(HWND hwnd,
UINT const message, UINT const message,
WPARAM const wparam, WPARAM const wparam,
LPARAM const lparam) noexcept { LPARAM const lparam) noexcept {
switch (message) { switch (message) {
case WM_DESTROY: case WM_DESTROY:
window_handle_ = nullptr; window_handle_ = nullptr;
Destroy(); Destroy();
if (quit_on_close_) { if (quit_on_close_) {
PostQuitMessage(0); PostQuitMessage(0);
} }
return 0; return 0;
case WM_DPICHANGED: { case WM_DPICHANGED: {
auto newRectSize = reinterpret_cast<RECT*>(lparam); auto newRectSize = reinterpret_cast<RECT*>(lparam);
LONG newWidth = newRectSize->right - newRectSize->left; LONG newWidth = newRectSize->right - newRectSize->left;
LONG newHeight = newRectSize->bottom - newRectSize->top; LONG newHeight = newRectSize->bottom - newRectSize->top;
SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
newHeight, SWP_NOZORDER | SWP_NOACTIVATE); newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
return 0; return 0;
} }
case WM_SIZE: { case WM_SIZE: {
RECT rect = GetClientArea(); RECT rect = GetClientArea();
if (child_content_ != nullptr) { if (child_content_ != nullptr) {
// Size and position the child window. // Size and position the child window.
MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
rect.bottom - rect.top, TRUE); rect.bottom - rect.top, TRUE);
} }
return 0; return 0;
} }
case WM_ACTIVATE: case WM_ACTIVATE:
if (child_content_ != nullptr) { if (child_content_ != nullptr) {
SetFocus(child_content_); SetFocus(child_content_);
} }
return 0; return 0;
case WM_DWMCOLORIZATIONCOLORCHANGED: case WM_DWMCOLORIZATIONCOLORCHANGED:
UpdateTheme(hwnd); UpdateTheme(hwnd);
return 0; return 0;
} }
return DefWindowProc(window_handle_, message, wparam, lparam); return DefWindowProc(window_handle_, message, wparam, lparam);
} }
void Win32Window::Destroy() { void Win32Window::Destroy() {
OnDestroy(); OnDestroy();
if (window_handle_) { if (window_handle_) {
DestroyWindow(window_handle_); DestroyWindow(window_handle_);
window_handle_ = nullptr; window_handle_ = nullptr;
} }
if (g_active_window_count == 0) { if (g_active_window_count == 0) {
WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
} }
} }
Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
return reinterpret_cast<Win32Window*>( return reinterpret_cast<Win32Window*>(
GetWindowLongPtr(window, GWLP_USERDATA)); GetWindowLongPtr(window, GWLP_USERDATA));
} }
void Win32Window::SetChildContent(HWND content) { void Win32Window::SetChildContent(HWND content) {
child_content_ = content; child_content_ = content;
SetParent(content, window_handle_); SetParent(content, window_handle_);
RECT frame = GetClientArea(); RECT frame = GetClientArea();
MoveWindow(content, frame.left, frame.top, frame.right - frame.left, MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
frame.bottom - frame.top, true); frame.bottom - frame.top, true);
SetFocus(child_content_); SetFocus(child_content_);
} }
RECT Win32Window::GetClientArea() { RECT Win32Window::GetClientArea() {
RECT frame; RECT frame;
GetClientRect(window_handle_, &frame); GetClientRect(window_handle_, &frame);
return frame; return frame;
} }
HWND Win32Window::GetHandle() { HWND Win32Window::GetHandle() {
return window_handle_; return window_handle_;
} }
void Win32Window::SetQuitOnClose(bool quit_on_close) { void Win32Window::SetQuitOnClose(bool quit_on_close) {
quit_on_close_ = quit_on_close; quit_on_close_ = quit_on_close;
} }
bool Win32Window::OnCreate() { bool Win32Window::OnCreate() {
// No-op; provided for subclasses. // No-op; provided for subclasses.
return true; return true;
} }
void Win32Window::OnDestroy() { void Win32Window::OnDestroy() {
// No-op; provided for subclasses. // No-op; provided for subclasses.
} }
void Win32Window::UpdateTheme(HWND const window) { void Win32Window::UpdateTheme(HWND const window) {
DWORD light_mode; DWORD light_mode;
DWORD light_mode_size = sizeof(light_mode); DWORD light_mode_size = sizeof(light_mode);
LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey,
kGetPreferredBrightnessRegValue, kGetPreferredBrightnessRegValue,
RRF_RT_REG_DWORD, nullptr, &light_mode, RRF_RT_REG_DWORD, nullptr, &light_mode,
&light_mode_size); &light_mode_size);
if (result == ERROR_SUCCESS) { if (result == ERROR_SUCCESS) {
BOOL enable_dark_mode = light_mode == 0; BOOL enable_dark_mode = light_mode == 0;
DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE,
&enable_dark_mode, sizeof(enable_dark_mode)); &enable_dark_mode, sizeof(enable_dark_mode));
} }
} }

Some files were not shown because too many files have changed in this diff Show More