├── icons.sketch
├── ios
├── Runner
│ ├── Runner-Bridging-Header.h
│ ├── Assets.xcassets
│ │ ├── LaunchImage.imageset
│ │ │ ├── LaunchImage.png
│ │ │ ├── LaunchImage@2x.png
│ │ │ ├── LaunchImage@3x.png
│ │ │ ├── README.md
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── AppDelegate.swift
│ ├── Base.lproj
│ │ ├── Main.storyboard
│ │ └── LaunchScreen.storyboard
│ └── Info.plist
├── Flutter
│ ├── Debug.xcconfig
│ ├── Release.xcconfig
│ └── AppFrameworkInfo.plist
├── Runner.xcodeproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── WorkspaceSettings.xcsettings
│ │ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── WorkspaceSettings.xcsettings
│ │ └── IDEWorkspaceChecks.plist
├── .gitignore
├── GoogleService-Info.plist
├── Podfile
└── Podfile.lock
├── web
├── favicon.png
├── icons
│ ├── Icon-192.png
│ └── Icon-512.png
├── manifest.json
└── index.html
├── launcher-icon.sketch
├── assets
├── fonts
│ ├── icons.ttf
│ ├── AquaIcons.ttf
│ ├── Poppins-Black.ttf
│ ├── Poppins-Bold.ttf
│ ├── Poppins-Light.ttf
│ ├── Poppins-Thin.ttf
│ ├── WorkSans-Bold.ttf
│ ├── WorkSans-Thin.ttf
│ ├── Poppins-Italic.ttf
│ ├── Poppins-Medium.ttf
│ ├── Poppins-Regular.ttf
│ ├── Poppins-SemiBold.ttf
│ ├── WorkSans-Black.ttf
│ ├── WorkSans-Italic.ttf
│ ├── WorkSans-Light.ttf
│ ├── WorkSans-Medium.ttf
│ ├── WorkSans-Regular.ttf
│ ├── Poppins-BoldItalic.ttf
│ ├── Poppins-ExtraBold.ttf
│ ├── Poppins-ExtraLight.ttf
│ ├── Poppins-ThinItalic.ttf
│ ├── SourceCodePro-Bold.ttf
│ ├── WorkSans-ExtraBold.ttf
│ ├── WorkSans-SemiBold.ttf
│ ├── Poppins-BlackItalic.ttf
│ ├── Poppins-LightItalic.ttf
│ ├── Poppins-MediumItalic.ttf
│ ├── SourceCodePro-Black.ttf
│ ├── SourceCodePro-Italic.ttf
│ ├── SourceCodePro-Light.ttf
│ ├── SourceCodePro-Medium.ttf
│ ├── SourceCodePro-Regular.ttf
│ ├── WorkSans-BlackItalic.ttf
│ ├── WorkSans-BoldItalic.ttf
│ ├── WorkSans-ExtraLight.ttf
│ ├── WorkSans-LightItalic.ttf
│ ├── WorkSans-MediumItalic.ttf
│ ├── WorkSans-ThinItalic.ttf
│ ├── Poppins-ExtraBoldItalic.ttf
│ ├── Poppins-SemiBoldItalic.ttf
│ ├── SourceCodePro-SemiBold.ttf
│ ├── WorkSans-SemiBoldItalic.ttf
│ ├── Poppins-ExtraLightItalic.ttf
│ ├── SourceCodePro-BlackItalic.ttf
│ ├── SourceCodePro-BoldItalic.ttf
│ ├── SourceCodePro-ExtraLight.ttf
│ ├── SourceCodePro-LightItalic.ttf
│ ├── SourceCodePro-MediumItalic.ttf
│ ├── WorkSans-ExtraBoldItalic.ttf
│ ├── WorkSans-ExtraLightItalic.ttf
│ ├── SourceCodePro-SemiBoldItalic.ttf
│ └── SourceCodePro-ExtraLightItalic.ttf
└── launcher-icon
│ ├── icon@1024px.png
│ ├── adaptive-icon-background@432px.png
│ └── adaptive-icon-foreground@432px.png
├── android
├── gradle.properties
├── app
│ ├── src
│ │ ├── main
│ │ │ ├── kotlin
│ │ │ │ └── app
│ │ │ │ │ └── axross
│ │ │ │ │ └── aqua
│ │ │ │ │ └── MainActivity.kt
│ │ │ ├── res
│ │ │ │ ├── drawable
│ │ │ │ │ └── launch_background.xml
│ │ │ │ ├── drawable-v21
│ │ │ │ │ └── launch_background.xml
│ │ │ │ ├── values
│ │ │ │ │ └── styles.xml
│ │ │ │ └── values-night
│ │ │ │ │ └── styles.xml
│ │ │ └── AndroidManifest.xml
│ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ └── profile
│ │ │ └── AndroidManifest.xml
│ ├── google-services.json
│ └── build.gradle
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── .gitignore
├── settings.gradle
└── build.gradle
├── lib
├── src
│ ├── models
│ │ ├── anonymous_user.dart
│ │ ├── hand_range_preset.dart
│ │ └── calculation.dart
│ ├── services
│ │ ├── auth_manager_service.dart
│ │ ├── analytics_service.dart
│ │ ├── error_reporter_service.dart
│ │ └── isolate_evaluation_service.dart
│ ├── common_widgets
│ │ ├── fill.dart
│ │ ├── analytics.dart
│ │ ├── authentication.dart
│ │ ├── error_reporter.dart
│ │ ├── aqua_environment.dart
│ │ ├── current_simulation_session.dart
│ │ ├── aqua_popup_transition.dart
│ │ ├── aqua_icons.dart
│ │ ├── aqua_preferences.dart
│ │ ├── aqua_appear_animation.dart
│ │ ├── aqua_slider.dart
│ │ ├── aqua_tab_view.dart
│ │ ├── playing_card.dart
│ │ ├── readonly_rank_pair_grid.dart
│ │ ├── aqua_theme.dart
│ │ ├── aqua_scaffold.dart
│ │ ├── aqua_button.dart
│ │ ├── card_picker.dart
│ │ ├── aqua_tab_bar.dart
│ │ ├── digits_text.dart
│ │ └── rank_pair_select_grid.dart
│ ├── constants
│ │ ├── hand.dart
│ │ ├── card.dart
│ │ ├── theme.dart
│ │ └── hand_range.dart
│ ├── view_models
│ │ ├── card_pair_draft.dart
│ │ ├── community_cards_draft.dart
│ │ ├── hand_range_draft_list.dart
│ │ ├── hand_range_draft.dart
│ │ └── simulation_session.dart
│ └── utilities
│ │ └── system_ui_overlay_style.dart
├── app
│ ├── services
│ │ ├── noop_error_reporter_service.dart
│ │ ├── firebase_auth_manager_service.dart
│ │ ├── sentry_error_reporter_service.dart
│ │ ├── flutter_isolate_evaluation_service.dart
│ │ └── amplitude_analytics_service.dart
│ ├── main_route.dart
│ ├── main_route
│ │ └── preferences_tab_page.dart
│ ├── app.dart
│ └── preset_select_route.dart
└── main.dart
├── .metadata
├── .github
└── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── bug_report.md
├── .gitignore
├── README.md
├── pubspec.yaml
└── pubspec.lock
/icons.sketch:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/icons.sketch
--------------------------------------------------------------------------------
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/web/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/web/favicon.png
--------------------------------------------------------------------------------
/launcher-icon.sketch:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/launcher-icon.sketch
--------------------------------------------------------------------------------
/assets/fonts/icons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/icons.ttf
--------------------------------------------------------------------------------
/web/icons/Icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/web/icons/Icon-192.png
--------------------------------------------------------------------------------
/web/icons/Icon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/web/icons/Icon-512.png
--------------------------------------------------------------------------------
/assets/fonts/AquaIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/AquaIcons.ttf
--------------------------------------------------------------------------------
/assets/fonts/Poppins-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/Poppins-Black.ttf
--------------------------------------------------------------------------------
/assets/fonts/Poppins-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/Poppins-Bold.ttf
--------------------------------------------------------------------------------
/assets/fonts/Poppins-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/Poppins-Light.ttf
--------------------------------------------------------------------------------
/assets/fonts/Poppins-Thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/Poppins-Thin.ttf
--------------------------------------------------------------------------------
/assets/fonts/WorkSans-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/WorkSans-Bold.ttf
--------------------------------------------------------------------------------
/assets/fonts/WorkSans-Thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/WorkSans-Thin.ttf
--------------------------------------------------------------------------------
/assets/fonts/Poppins-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/Poppins-Italic.ttf
--------------------------------------------------------------------------------
/assets/fonts/Poppins-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/Poppins-Medium.ttf
--------------------------------------------------------------------------------
/assets/fonts/Poppins-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/Poppins-Regular.ttf
--------------------------------------------------------------------------------
/assets/fonts/Poppins-SemiBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/Poppins-SemiBold.ttf
--------------------------------------------------------------------------------
/assets/fonts/WorkSans-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/WorkSans-Black.ttf
--------------------------------------------------------------------------------
/assets/fonts/WorkSans-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/WorkSans-Italic.ttf
--------------------------------------------------------------------------------
/assets/fonts/WorkSans-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/WorkSans-Light.ttf
--------------------------------------------------------------------------------
/assets/fonts/WorkSans-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/WorkSans-Medium.ttf
--------------------------------------------------------------------------------
/assets/fonts/WorkSans-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/WorkSans-Regular.ttf
--------------------------------------------------------------------------------
/assets/fonts/Poppins-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/Poppins-BoldItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/Poppins-ExtraBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/Poppins-ExtraBold.ttf
--------------------------------------------------------------------------------
/assets/fonts/Poppins-ExtraLight.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/Poppins-ExtraLight.ttf
--------------------------------------------------------------------------------
/assets/fonts/Poppins-ThinItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/Poppins-ThinItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/SourceCodePro-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/SourceCodePro-Bold.ttf
--------------------------------------------------------------------------------
/assets/fonts/WorkSans-ExtraBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/WorkSans-ExtraBold.ttf
--------------------------------------------------------------------------------
/assets/fonts/WorkSans-SemiBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/WorkSans-SemiBold.ttf
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/assets/fonts/Poppins-BlackItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/Poppins-BlackItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/Poppins-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/Poppins-LightItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/Poppins-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/Poppins-MediumItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/SourceCodePro-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/SourceCodePro-Black.ttf
--------------------------------------------------------------------------------
/assets/fonts/SourceCodePro-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/SourceCodePro-Italic.ttf
--------------------------------------------------------------------------------
/assets/fonts/SourceCodePro-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/SourceCodePro-Light.ttf
--------------------------------------------------------------------------------
/assets/fonts/SourceCodePro-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/SourceCodePro-Medium.ttf
--------------------------------------------------------------------------------
/assets/fonts/SourceCodePro-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/SourceCodePro-Regular.ttf
--------------------------------------------------------------------------------
/assets/fonts/WorkSans-BlackItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/WorkSans-BlackItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/WorkSans-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/WorkSans-BoldItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/WorkSans-ExtraLight.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/WorkSans-ExtraLight.ttf
--------------------------------------------------------------------------------
/assets/fonts/WorkSans-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/WorkSans-LightItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/WorkSans-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/WorkSans-MediumItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/WorkSans-ThinItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/WorkSans-ThinItalic.ttf
--------------------------------------------------------------------------------
/assets/launcher-icon/icon@1024px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/launcher-icon/icon@1024px.png
--------------------------------------------------------------------------------
/assets/fonts/Poppins-ExtraBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/Poppins-ExtraBoldItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/Poppins-SemiBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/Poppins-SemiBoldItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/SourceCodePro-SemiBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/SourceCodePro-SemiBold.ttf
--------------------------------------------------------------------------------
/assets/fonts/WorkSans-SemiBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/WorkSans-SemiBoldItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/Poppins-ExtraLightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/Poppins-ExtraLightItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/SourceCodePro-BlackItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/SourceCodePro-BlackItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/SourceCodePro-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/SourceCodePro-BoldItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/SourceCodePro-ExtraLight.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/SourceCodePro-ExtraLight.ttf
--------------------------------------------------------------------------------
/assets/fonts/SourceCodePro-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/SourceCodePro-LightItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/SourceCodePro-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/SourceCodePro-MediumItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/WorkSans-ExtraBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/WorkSans-ExtraBoldItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/WorkSans-ExtraLightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/WorkSans-ExtraLightItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/SourceCodePro-SemiBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/SourceCodePro-SemiBoldItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/SourceCodePro-ExtraLightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/fonts/SourceCodePro-ExtraLightItalic.ttf
--------------------------------------------------------------------------------
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/assets/launcher-icon/adaptive-icon-background@432px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/launcher-icon/adaptive-icon-background@432px.png
--------------------------------------------------------------------------------
/assets/launcher-icon/adaptive-icon-foreground@432px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/assets/launcher-icon/adaptive-icon-foreground@432px.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/axross/aqua/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/lib/src/models/anonymous_user.dart:
--------------------------------------------------------------------------------
1 | import "package:meta/meta.dart";
2 |
3 | @immutable
4 | class AnonymousUser {
5 | AnonymousUser({required this.id});
6 |
7 | final String id;
8 | }
9 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/app/axross/aqua/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package app.axross.aqua
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/lib/src/services/auth_manager_service.dart:
--------------------------------------------------------------------------------
1 | import "package:aqua/src/models/anonymous_user.dart";
2 | import "package:flutter/foundation.dart";
3 |
4 | abstract class AuthManagerService extends ChangeNotifier {
5 | AnonymousUser? get user;
6 |
7 | Future initialize();
8 | }
9 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
7 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/lib/src/models/hand_range_preset.dart:
--------------------------------------------------------------------------------
1 | import "package:meta/meta.dart";
2 | import "package:poker/poker.dart";
3 |
4 | @immutable
5 | class HandRangePreset {
6 | const HandRangePreset({
7 | required this.name,
8 | required this.handRange,
9 | });
10 |
11 | final String name;
12 |
13 | final HandRange handRange;
14 | }
15 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: f4abaa0735eba4dfd8f33f73363911d63931fe03
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/lib/src/services/analytics_service.dart:
--------------------------------------------------------------------------------
1 | import "package:aqua/src/models/anonymous_user.dart";
2 |
3 | abstract class AnalyticsService {
4 | void setUser(AnonymousUser? user);
5 |
6 | void logScreenChange({
7 | required String screenName,
8 | Map parameters = const {},
9 | });
10 |
11 | void logEvent({
12 | required String name,
13 | Map parameters = const {},
14 | });
15 | }
16 |
--------------------------------------------------------------------------------
/lib/src/services/error_reporter_service.dart:
--------------------------------------------------------------------------------
1 | import "package:aqua/src/models/anonymous_user.dart";
2 | import "package:flutter/foundation.dart";
3 |
4 | abstract class ErrorReporterService {
5 | void setUser(AnonymousUser? user);
6 |
7 | Future captureException({
8 | required dynamic exception,
9 | required StackTrace stackTrace,
10 | });
11 |
12 | Future captureFlutterException(FlutterErrorDetails details);
13 | }
14 |
--------------------------------------------------------------------------------
/lib/src/common_widgets/fill.dart:
--------------------------------------------------------------------------------
1 | import "package:flutter/widgets.dart";
2 |
3 | @immutable
4 | class Fill extends StatelessWidget {
5 | const Fill({@required this.child, Key? key}) : super(key: key);
6 |
7 | final child;
8 |
9 | @override
10 | Widget build(BuildContext context) {
11 | return ConstrainedBox(
12 | constraints: BoxConstraints.loose(MediaQuery.of(context).size),
13 | child: child,
14 | );
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/lib/src/services/isolate_evaluation_service.dart:
--------------------------------------------------------------------------------
1 | import "package:aqua/src/models/calculation.dart";
2 | import "package:poker/poker.dart";
3 |
4 | abstract class IsolateEvaluationService {
5 | Stream get onProgress;
6 |
7 | Future initialize();
8 |
9 | Future dispose();
10 |
11 | void requestEvaluation({
12 | required CardSet communityCards,
13 | required List players,
14 | required int times,
15 | });
16 | }
17 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/lib/src/constants/hand.dart:
--------------------------------------------------------------------------------
1 | import "package:poker/poker.dart";
2 |
3 | const handTypeStrings = {
4 | MadeHandType.straightFlush: "Str. Flush",
5 | MadeHandType.quads: "Four of a Kind",
6 | MadeHandType.fullHouse: "Fullhouse",
7 | MadeHandType.flush: "Flush",
8 | MadeHandType.straight: "Straight",
9 | MadeHandType.trips: "Three of a Kind",
10 | MadeHandType.twoPairs: "Two Pairs",
11 | MadeHandType.pair: "Pair",
12 | MadeHandType.highcard: "High Card",
13 | };
14 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
4 | def properties = new Properties()
5 |
6 | assert localPropertiesFile.exists()
7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
8 |
9 | def flutterSdkPath = properties.getProperty("flutter.sdk")
10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
12 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/lib/src/models/calculation.dart:
--------------------------------------------------------------------------------
1 | import "package:meta/meta.dart";
2 | import "package:poker/poker.dart";
3 |
4 | @immutable
5 | class Calculation {
6 | Calculation({
7 | required this.communityCards,
8 | required this.players,
9 | required this.result,
10 | required this.startedAt,
11 | required this.endedAt,
12 | });
13 |
14 | final CardSet communityCards;
15 |
16 | final List players;
17 |
18 | final EvaluationResult result;
19 |
20 | final DateTime startedAt;
21 |
22 | final DateTime endedAt;
23 | }
24 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: feature-request
6 | assignees: ''
7 |
8 | ---
9 |
10 |
14 |
15 | ## ✏️ Is your feature request related to a problem? Or new one? Please describe.
16 |
17 | Please write here...
18 |
19 | ## 📸 Is there any screenshots or URL to show me?
20 |
21 | Please paste screenshots or URL here...
22 |
--------------------------------------------------------------------------------
/lib/app/services/noop_error_reporter_service.dart:
--------------------------------------------------------------------------------
1 | import "package:aqua/src/models/anonymous_user.dart";
2 | import "package:aqua/src/services/error_reporter_service.dart";
3 | import "package:flutter/foundation.dart";
4 |
5 | class NoopErrorReporterService implements ErrorReporterService {
6 | NoopErrorReporterService();
7 |
8 | void setUser(AnonymousUser? user) {}
9 |
10 | Future captureException({
11 | required dynamic exception,
12 | required StackTrace stackTrace,
13 | }) async {}
14 |
15 | Future captureFlutterException(FlutterErrorDetails details) async {}
16 | }
17 |
--------------------------------------------------------------------------------
/lib/src/common_widgets/analytics.dart:
--------------------------------------------------------------------------------
1 | import "package:aqua/src/services/analytics_service.dart";
2 | import "package:flutter/widgets.dart";
3 |
4 | class Analytics extends InheritedWidget {
5 | Analytics({
6 | required this.analytics,
7 | required Widget child,
8 | Key? key,
9 | }) : super(key: key, child: child);
10 |
11 | final AnalyticsService analytics;
12 |
13 | @override
14 | bool updateShouldNotify(Analytics old) => analytics != old.analytics;
15 |
16 | static AnalyticsService of(BuildContext context) =>
17 | context.dependOnInheritedWidgetOfExactType()!.analytics;
18 | }
19 |
--------------------------------------------------------------------------------
/lib/src/common_widgets/authentication.dart:
--------------------------------------------------------------------------------
1 | import "package:aqua/src/services/auth_manager_service.dart";
2 | import "package:flutter/widgets.dart";
3 |
4 | class Authentication extends InheritedWidget {
5 | Authentication({
6 | Key? key,
7 | required this.manager,
8 | required Widget child,
9 | }) : super(key: key, child: child);
10 |
11 | final AuthManagerService manager;
12 |
13 | @override
14 | bool updateShouldNotify(Authentication old) => manager != old.manager;
15 |
16 | static AuthManagerService of(BuildContext context) =>
17 | context.dependOnInheritedWidgetOfExactType()!.manager;
18 | }
19 |
--------------------------------------------------------------------------------
/lib/src/common_widgets/error_reporter.dart:
--------------------------------------------------------------------------------
1 | import "package:aqua/src/services/error_reporter_service.dart";
2 | import "package:flutter/widgets.dart";
3 |
4 | class ErrorReporter extends InheritedWidget {
5 | ErrorReporter({
6 | required this.service,
7 | required Widget child,
8 | Key? key,
9 | }) : super(key: key, child: child);
10 |
11 | final ErrorReporterService service;
12 |
13 | @override
14 | bool updateShouldNotify(ErrorReporter old) => service != old.service;
15 |
16 | static ErrorReporterService of(BuildContext context) =>
17 | context.dependOnInheritedWidgetOfExactType()!.service;
18 | }
19 |
--------------------------------------------------------------------------------
/lib/src/constants/card.dart:
--------------------------------------------------------------------------------
1 | import "package:poker/poker.dart";
2 |
3 | const ranksInStrongnessOrder = [
4 | Rank.ace,
5 | Rank.king,
6 | Rank.queen,
7 | Rank.jack,
8 | Rank.ten,
9 | Rank.nine,
10 | Rank.eight,
11 | Rank.seven,
12 | Rank.six,
13 | Rank.five,
14 | Rank.four,
15 | Rank.trey,
16 | Rank.deuce,
17 | ];
18 |
19 | const rankChars = {
20 | Rank.ace: "A",
21 | Rank.king: "K",
22 | Rank.queen: "Q",
23 | Rank.jack: "J",
24 | Rank.ten: "T",
25 | Rank.nine: "9",
26 | Rank.eight: "8",
27 | Rank.seven: "7",
28 | Rank.six: "6",
29 | Rank.five: "5",
30 | Rank.four: "4",
31 | Rank.trey: "3",
32 | Rank.deuce: "2",
33 | };
34 |
--------------------------------------------------------------------------------
/web/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "aqua",
3 | "short_name": "aqua",
4 | "start_url": ".",
5 | "display": "standalone",
6 | "background_color": "#0175C2",
7 | "theme_color": "#0175C2",
8 | "description": "A new Flutter project.",
9 | "orientation": "portrait-primary",
10 | "prefer_related_applications": false,
11 | "icons": [
12 | {
13 | "src": "icons/Icon-192.png",
14 | "sizes": "192x192",
15 | "type": "image/png"
16 | },
17 | {
18 | "src": "icons/Icon-512.png",
19 | "sizes": "512x512",
20 | "type": "image/png"
21 | }
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | *.mode1v3
2 | *.mode2v3
3 | *.moved-aside
4 | *.pbxuser
5 | *.perspectivev3
6 | **/*sync/
7 | .sconsign.dblite
8 | .tags*
9 | **/.vagrant/
10 | **/DerivedData/
11 | Icon?
12 | **/Pods/
13 | **/.symlinks/
14 | profile
15 | xcuserdata
16 | **/.generated/
17 | Flutter/App.framework
18 | Flutter/Flutter.framework
19 | Flutter/Flutter.podspec
20 | Flutter/Generated.xcconfig
21 | Flutter/ephemeral/
22 | Flutter/app.flx
23 | Flutter/app.zip
24 | Flutter/flutter_assets/
25 | Flutter/flutter_export_environment.sh
26 | ServiceDefinitions.json
27 | Runner/GeneratedPluginRegistrant.*
28 |
29 | # Exceptions to above rules.
30 | !default.mode1v3
31 | !default.mode2v3
32 | !default.pbxuser
33 | !default.perspectivev3
34 |
--------------------------------------------------------------------------------
/lib/src/common_widgets/aqua_environment.dart:
--------------------------------------------------------------------------------
1 | import "package:flutter/foundation.dart";
2 | import "package:flutter/widgets.dart";
3 |
4 | class AquaEnvironment extends InheritedWidget {
5 | AquaEnvironment({
6 | Key? key,
7 | required Widget child,
8 | }) : data = AquaEnvironmentData(),
9 | super(key: key, child: child);
10 |
11 | final AquaEnvironmentData data;
12 |
13 | @override
14 | bool updateShouldNotify(AquaEnvironment old) => data != old.data;
15 |
16 | static AquaEnvironmentData of(BuildContext context) =>
17 | context.dependOnInheritedWidgetOfExactType()!.data;
18 | }
19 |
20 | @immutable
21 | class AquaEnvironmentData {
22 | final isDebugBuild = kDebugMode;
23 | }
24 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 |
14 |
15 | ## ✏️ Could you concisely describe about the bug?
16 |
17 | Please write here...
18 |
19 | ## 🤔 What is it expected?
20 |
21 | Please write here...
22 |
23 | ## 🏃♀️ Could you introduce how to reproduce the bug?
24 |
25 | 1. Tap "..." button
26 | 2. Scroll down to "..."
27 | 3. What you made should be a coffee, but it was tea actually
28 |
29 | ## 📸 Do you have screenshots?
30 |
31 | If you have, please paste here...
32 |
--------------------------------------------------------------------------------
/lib/src/common_widgets/current_simulation_session.dart:
--------------------------------------------------------------------------------
1 | import "package:aqua/src/view_models/simulation_session.dart";
2 | import "package:flutter/widgets.dart";
3 |
4 | class CurrentSimulationSession extends InheritedWidget {
5 | CurrentSimulationSession({
6 | required this.simulationSession,
7 | required Widget child,
8 | Key? key,
9 | }) : super(key: key, child: child);
10 |
11 | final CalculationSession simulationSession;
12 |
13 | @override
14 | bool updateShouldNotify(CurrentSimulationSession old) =>
15 | simulationSession != old.simulationSession;
16 |
17 | static CalculationSession of(BuildContext context) => context
18 | .dependOnInheritedWidgetOfExactType()!
19 | .simulationSession;
20 | }
21 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.3.50'
3 | repositories {
4 | google()
5 | jcenter()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:4.1.0'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | classpath 'com.google.gms:google-services:4.3.8'
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | google()
18 | jcenter()
19 | }
20 | }
21 |
22 | rootProject.buildDir = '../build'
23 | subprojects {
24 | project.buildDir = "${rootProject.buildDir}/${project.name}"
25 | project.evaluationDependsOn(':app')
26 | }
27 |
28 | task clean(type: Delete) {
29 | delete rootProject.buildDir
30 | }
31 |
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 8.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/lib/src/common_widgets/aqua_popup_transition.dart:
--------------------------------------------------------------------------------
1 | import "package:flutter/widgets.dart";
2 |
3 | class AquaPopupTransition extends AnimatedWidget {
4 | AquaPopupTransition({
5 | Key? key,
6 | required this.animation,
7 | required this.child,
8 | }) : super(key: key, listenable: animation);
9 |
10 | final Animation animation;
11 |
12 | final Widget child;
13 |
14 | @override
15 | Widget build(BuildContext context) {
16 | return Opacity(
17 | opacity: Tween(begin: 0.0, end: 1.0).animate(animation).value,
18 | child: Transform.scale(
19 | scale: Tween(begin: .98, end: 1.0).animate(animation).value,
20 | child: Transform.translate(
21 | offset: Tween(
22 | begin: Offset(0.0, -8.0),
23 | end: Offset(0.0, 0.0),
24 | ).animate(animation).value,
25 | child: child,
26 | ),
27 | ),
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/lib/app/services/firebase_auth_manager_service.dart:
--------------------------------------------------------------------------------
1 | import "package:aqua/src/models/anonymous_user.dart";
2 | import "package:aqua/src/services/auth_manager_service.dart";
3 | import "package:firebase_auth/firebase_auth.dart";
4 | import "package:flutter/foundation.dart";
5 |
6 | class FirebaseAuthManagerService extends ChangeNotifier
7 | implements AuthManagerService {
8 | FirebaseAuthManagerService();
9 |
10 | AnonymousUser? _user;
11 |
12 | AnonymousUser? get user => _user;
13 |
14 | initialize() async {
15 | final FirebaseAuth firebaseAuth = FirebaseAuth.instance;
16 |
17 | firebaseAuth.userChanges().listen((firebaseUser) {
18 | _user = firebaseUser != null ? AnonymousUser(id: firebaseUser.uid) : null;
19 |
20 | notifyListeners();
21 | });
22 |
23 | try {
24 | await firebaseAuth.signInAnonymously();
25 | } on Exception catch (error) {
26 | print(error);
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | **/ios/Flutter/.last_build_id
26 | .dart_tool/
27 | .flutter-plugins
28 | .flutter-plugins-dependencies
29 | .packages
30 | .pub-cache/
31 | .pub/
32 | /build/
33 |
34 | # Web related
35 | lib/generated_plugin_registrant.dart
36 |
37 | # Symbolication related
38 | app.*.symbols
39 |
40 | # Obfuscation related
41 | app.*.map.json
42 |
43 | # Android Studio will place build artifacts here
44 | /android/app/debug
45 | /android/app/profile
46 | /android/app/release
47 |
48 | #
49 | android/app/src/main/res/drawable-*/ic_launcher*.png
50 | android/app/src/main/res/mipmap-*/ic_launcher.png
51 | android/app/src/main/res/mipmap-*/ic_launcher.xml
52 | ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-*.png
53 |
--------------------------------------------------------------------------------
/lib/src/common_widgets/aqua_icons.dart:
--------------------------------------------------------------------------------
1 | import "package:flutter/widgets.dart";
2 |
3 | abstract class AquaIcons {
4 | AquaIcons._();
5 |
6 | static const IconData backspace = IconData(0xe800, fontFamily: _family);
7 | static const IconData cardPair = IconData(0xe801, fontFamily: _family);
8 | static const IconData check = IconData(0xe802, fontFamily: _family);
9 | static const IconData chevronLeft = IconData(0xe803, fontFamily: _family);
10 | static const IconData clock = IconData(0xe804, fontFamily: _family);
11 | static const IconData club = IconData(0xe805, fontFamily: _family);
12 | static const IconData diamond = IconData(0xe806, fontFamily: _family);
13 | static const IconData gear = IconData(0xe807, fontFamily: _family);
14 | static const IconData grid = IconData(0xe808, fontFamily: _family);
15 | static const IconData heart = IconData(0xe809, fontFamily: _family);
16 | static const IconData percent = IconData(0xe80a, fontFamily: _family);
17 | static const IconData plus = IconData(0xe80b, fontFamily: _family);
18 | static const IconData save = IconData(0xe80c, fontFamily: _family);
19 | static const IconData spade = IconData(0xe80d, fontFamily: _family);
20 | static const IconData trash = IconData(0xe80e, fontFamily: _family);
21 | }
22 |
23 | const _family = "AquaIcons";
24 |
--------------------------------------------------------------------------------
/lib/src/view_models/card_pair_draft.dart:
--------------------------------------------------------------------------------
1 | import "package:flutter/foundation.dart";
2 | import "package:poker/poker.dart";
3 |
4 | class CardPairDraft extends ChangeNotifier {
5 | CardPairDraft(this._a, this._b) : assert(_a != _b);
6 |
7 | CardPairDraft.empty();
8 |
9 | Card? _a;
10 |
11 | Card? _b;
12 |
13 | bool get isComplete => _a != null && _b != null;
14 |
15 | @override
16 | int get hashCode {
17 | int result = 17;
18 |
19 | result = 37 * result + _a.hashCode;
20 | result = 37 * result + _b.hashCode;
21 |
22 | return result;
23 | }
24 |
25 | CardPair toCardPair() {
26 | assert(isComplete);
27 |
28 | return CardPair(_a!, _b!);
29 | }
30 |
31 | /// Returns a string representation such like `"AsKh"`.
32 | @override
33 | String toString() => "$_a$_b";
34 |
35 | Card? operator [](int index) {
36 | assert(index == 0 || index == 1, "index should 0 or 1.");
37 |
38 | return index == 0 ? _a : _b;
39 | }
40 |
41 | operator []=(int index, Card? card) {
42 | assert(index == 0 || index == 1, "index should 0 or 1.");
43 |
44 | if (index == 0) {
45 | _a = card;
46 | } else {
47 | _b = card;
48 | }
49 |
50 | notifyListeners();
51 | }
52 |
53 | @override
54 | operator ==(Object other) =>
55 | other is CardPairDraft && other._a == _a && other._b == _b;
56 | }
57 |
--------------------------------------------------------------------------------
/ios/GoogleService-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CLIENT_ID
6 | 1070741331805-rmt0hnk3ieurnp3hnnlq0krr98ongupj.apps.googleusercontent.com
7 | REVERSED_CLIENT_ID
8 | com.googleusercontent.apps.1070741331805-rmt0hnk3ieurnp3hnnlq0krr98ongupj
9 | ANDROID_CLIENT_ID
10 | 1070741331805-dlv2er04ppa5obsu7rc84o1hqlkv3rfi.apps.googleusercontent.com
11 | API_KEY
12 | AIzaSyAKZY4Vq1c7dnz6eGeUlUG1xZqKnrFaE1s
13 | GCM_SENDER_ID
14 | 1070741331805
15 | PLIST_VERSION
16 | 1
17 | BUNDLE_ID
18 | app.axross.aqua
19 | PROJECT_ID
20 | ax-aqua
21 | STORAGE_BUCKET
22 | ax-aqua.appspot.com
23 | IS_ADS_ENABLED
24 |
25 | IS_ANALYTICS_ENABLED
26 |
27 | IS_APPINVITE_ENABLED
28 |
29 | IS_GCM_ENABLED
30 |
31 | IS_SIGNIN_ENABLED
32 |
33 | GOOGLE_APP_ID
34 | 1:1070741331805:ios:44fd52f657f127f9c052b4
35 | DATABASE_URL
36 | https://ax-aqua.firebaseio.com
37 |
38 |
--------------------------------------------------------------------------------
/lib/src/common_widgets/aqua_preferences.dart:
--------------------------------------------------------------------------------
1 | import "package:flutter/widgets.dart";
2 | import "package:shared_preferences/shared_preferences.dart";
3 |
4 | class AquaPreferences extends InheritedWidget {
5 | AquaPreferences({
6 | required this.data,
7 | required Widget child,
8 | Key? key,
9 | }) : super(key: key, child: child);
10 |
11 | final AquaPreferenceData data;
12 |
13 | @override
14 | bool updateShouldNotify(AquaPreferences old) => data != old.data;
15 |
16 | static AquaPreferenceData of(BuildContext context) =>
17 | context.dependOnInheritedWidgetOfExactType()!.data;
18 | }
19 |
20 | class AquaPreferenceData extends ChangeNotifier {
21 | AquaPreferenceData();
22 |
23 | late SharedPreferences preferences;
24 |
25 | bool _isLoaded = false;
26 |
27 | get isLoaded => _isLoaded;
28 |
29 | late bool _prefersWinRate;
30 |
31 | bool get prefersWinRate => _prefersWinRate;
32 |
33 | Future setPreferWinRate(bool value) async {
34 | _prefersWinRate = value;
35 |
36 | notifyListeners();
37 |
38 | await preferences.setBool("prefersWinRate", value);
39 | }
40 |
41 | Future initialize() async {
42 | preferences = await SharedPreferences.getInstance();
43 |
44 | if (!preferences.containsKey("prefersWinRate")) {
45 | await preferences.setBool("prefersWinRate", false);
46 | }
47 |
48 | _prefersWinRate = preferences.getBool("prefersWinRate")!;
49 | _isLoaded = true;
50 |
51 | notifyListeners();
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | # platform :ios, '9.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | platform :ios, '10.0'
14 |
15 | def flutter_root
16 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
17 | unless File.exist?(generated_xcode_build_settings_path)
18 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
19 | end
20 |
21 | File.foreach(generated_xcode_build_settings_path) do |line|
22 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
23 | return matches[1].strip if matches
24 | end
25 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
26 | end
27 |
28 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
29 |
30 | flutter_ios_podfile_setup
31 |
32 | target 'Runner' do
33 | use_frameworks!
34 | use_modular_headers!
35 |
36 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
37 | end
38 |
39 | post_install do |installer|
40 | installer.pods_project.targets.each do |target|
41 | flutter_additional_ios_build_settings(target)
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/lib/src/utilities/system_ui_overlay_style.dart:
--------------------------------------------------------------------------------
1 | import "package:flutter/painting.dart";
2 | import "package:flutter/services.dart";
3 |
4 | void setSystemUIOverlayStyle({
5 | required Color topColor,
6 | Color? bottomColor,
7 | }) {
8 | final topBrightness = _estimateBrightness(topColor);
9 | final bottomBrightness = _estimateBrightness(topColor);
10 |
11 | SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
12 | statusBarBrightness: topBrightness,
13 | statusBarColor: topColor,
14 | statusBarIconBrightness: _opposite(topBrightness),
15 | systemNavigationBarColor: bottomColor ?? topColor,
16 | systemNavigationBarIconBrightness: _opposite(bottomBrightness),
17 | ));
18 | }
19 |
20 | Brightness _opposite(Brightness brightness) =>
21 | brightness == Brightness.light ? Brightness.dark : Brightness.light;
22 |
23 | Brightness _estimateBrightness(Color color) {
24 | final double relativeLuminance = color.computeLuminance();
25 |
26 | // See
27 | // The spec says to use kThreshold=0.0525, but Material Design appears to bias
28 | // more towards using light text than WCAG20 recommends. Material Design spec
29 | // doesn't say what value to use, but 0.15 seemed close to what the Material
30 | // Design spec shows for its color palette on
31 | // .
32 | const double kThreshold = 0.15;
33 |
34 | return (relativeLuminance + 0.05) * (relativeLuminance + 0.05) > kThreshold
35 | ? Brightness.light
36 | : Brightness.dark;
37 | }
38 |
--------------------------------------------------------------------------------
/lib/app/services/sentry_error_reporter_service.dart:
--------------------------------------------------------------------------------
1 | import "package:aqua/src/models/anonymous_user.dart";
2 | import "package:aqua/src/services/error_reporter_service.dart";
3 | import "package:flutter/foundation.dart";
4 | import "package:package_info/package_info.dart";
5 | import "package:sentry/sentry.dart";
6 |
7 | class SentryErrorReporterService implements ErrorReporterService {
8 | SentryErrorReporterService({required String sentryDsn})
9 | : _sentryClient = SentryClient(SentryOptions(dsn: sentryDsn)) {
10 | PackageInfo.fromPlatform().then((packageInfo) {
11 | Sentry.configureScope((scope) {
12 | scope.setTag("environment", "production");
13 | scope.setTag("version", packageInfo.version);
14 | });
15 | });
16 | }
17 |
18 | final SentryClient _sentryClient;
19 |
20 | void setUser(AnonymousUser? user) {
21 | Sentry.configureScope((scope) {
22 | scope.user = user != null ? SentryUser(id: user.id) : null;
23 | });
24 | }
25 |
26 | Future captureException({
27 | required dynamic exception,
28 | required StackTrace stackTrace,
29 | }) async {
30 | await _sentryClient.captureException(exception, stackTrace: stackTrace);
31 | }
32 |
33 | Future captureFlutterException(FlutterErrorDetails details) async {
34 | Sentry.configureScope((scope) {
35 | scope.setExtra("details", details.toString());
36 | });
37 |
38 | await _sentryClient.captureException(
39 | details.exception,
40 | stackTrace: details.stack,
41 | );
42 |
43 | Sentry.configureScope((scope) {
44 | scope.setExtra("details", null);
45 | });
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/lib/src/view_models/community_cards_draft.dart:
--------------------------------------------------------------------------------
1 | import "package:flutter/foundation.dart";
2 | import "package:meta/meta.dart";
3 | import "package:poker/poker.dart";
4 |
5 | class CommunityCardsDraft extends ChangeNotifier {
6 | CommunityCardsDraft(Iterable cards)
7 | : assert(cards.length <= 5),
8 | assert(cards.every((c) => c == null || c is Card)),
9 | cards = [null, null, null, null, null]
10 | ..setRange(0, cards.length, cards);
11 |
12 | CommunityCardsDraft.empty() : cards = [null, null, null, null, null];
13 |
14 | @visibleForTesting
15 | final List cards;
16 |
17 | int get hashCode {
18 | int result = 17;
19 |
20 | result = 37 * result + cards[0].hashCode;
21 | result = 37 * result + cards[1].hashCode;
22 | result = 37 * result + cards[2].hashCode;
23 | result = 37 * result + cards[3].hashCode;
24 | result = 37 * result + cards[4].hashCode;
25 |
26 | return result;
27 | }
28 |
29 | // Set toSet() => cards.where((c) => c != null).toSet();
30 | Set toSet() => cards.whereType().toSet();
31 |
32 | operator [](index) {
33 | assert(index != null);
34 | assert(index >= 0 && index < 5);
35 |
36 | return cards[index];
37 | }
38 |
39 | operator []=(index, card) {
40 | assert(index != null);
41 | assert(index >= 0 && index < 5);
42 | assert(card != null);
43 |
44 | cards[index] = card;
45 | }
46 |
47 | operator ==(Object other) =>
48 | other is CommunityCardsDraft &&
49 | other[0] == this[0] &&
50 | other[1] == this[1] &&
51 | other[2] == this[2] &&
52 | other[3] == this[3] &&
53 | other[4] == this[4];
54 | }
55 |
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | aqua
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UIViewControllerBasedStatusBarAppearance
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/lib/src/common_widgets/aqua_appear_animation.dart:
--------------------------------------------------------------------------------
1 | import "package:aqua/src/common_widgets/aqua_popup_transition.dart";
2 | import "package:flutter/widgets.dart";
3 |
4 | @immutable
5 | class AquaAppearAnimation extends StatefulWidget {
6 | AquaAppearAnimation({Key? key, required this.child, this.isVisible = false})
7 | : super(key: key);
8 |
9 | final bool isVisible;
10 |
11 | final Widget child;
12 |
13 | @override
14 | _AquaAppearAnimationState createState() => _AquaAppearAnimationState();
15 | }
16 |
17 | class _AquaAppearAnimationState extends State
18 | with SingleTickerProviderStateMixin {
19 | late AnimationController _animationController;
20 |
21 | late Animation _curvedAnimation;
22 |
23 | @override
24 | void initState() {
25 | super.initState();
26 |
27 | _animationController = AnimationController(
28 | duration: Duration(milliseconds: 200),
29 | vsync: this,
30 | value: widget.isVisible ? 1.0 : 0.0,
31 | );
32 | _curvedAnimation = CurvedAnimation(
33 | parent: _animationController,
34 | curve: Curves.easeInOutCubic,
35 | );
36 | }
37 |
38 | @override
39 | void didUpdateWidget(oldWidget) {
40 | super.didUpdateWidget(oldWidget);
41 |
42 | if (widget.isVisible != oldWidget.isVisible) {
43 | if (widget.isVisible) {
44 | _animationController.forward();
45 | } else {
46 | _animationController.reverse();
47 | }
48 | }
49 | }
50 |
51 | @override
52 | Widget build(BuildContext context) {
53 | return AquaPopupTransition(
54 | animation: _curvedAnimation,
55 | child: AnimatedBuilder(
56 | animation: _animationController,
57 | builder: (context, child) => _animationController.isDismissed
58 | ? SizedBox(height: 0)
59 | : widget.child,
60 | ),
61 | );
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/android/app/google-services.json:
--------------------------------------------------------------------------------
1 | {
2 | "project_info": {
3 | "project_number": "1070741331805",
4 | "firebase_url": "https://ax-aqua.firebaseio.com",
5 | "project_id": "ax-aqua",
6 | "storage_bucket": "ax-aqua.appspot.com"
7 | },
8 | "client": [
9 | {
10 | "client_info": {
11 | "mobilesdk_app_id": "1:1070741331805:android:472a2516c8a8aa71c052b4",
12 | "android_client_info": {
13 | "package_name": "app.axross.aqua"
14 | }
15 | },
16 | "oauth_client": [
17 | {
18 | "client_id": "1070741331805-dlv2er04ppa5obsu7rc84o1hqlkv3rfi.apps.googleusercontent.com",
19 | "client_type": 1,
20 | "android_info": {
21 | "package_name": "app.axross.aqua",
22 | "certificate_hash": "e019e9559cc05ae53f2f4ff97551b43e9f69314e"
23 | }
24 | },
25 | {
26 | "client_id": "1070741331805-25q27uuoqf0k9kv8gqai7cfrs689j9rf.apps.googleusercontent.com",
27 | "client_type": 3
28 | }
29 | ],
30 | "api_key": [
31 | {
32 | "current_key": "AIzaSyAuphIwAWuvlxnXfKRiW0JdartJYQPTp-8"
33 | }
34 | ],
35 | "services": {
36 | "appinvite_service": {
37 | "other_platform_oauth_client": [
38 | {
39 | "client_id": "1070741331805-25q27uuoqf0k9kv8gqai7cfrs689j9rf.apps.googleusercontent.com",
40 | "client_type": 3
41 | },
42 | {
43 | "client_id": "1070741331805-rmt0hnk3ieurnp3hnnlq0krr98ongupj.apps.googleusercontent.com",
44 | "client_type": 2,
45 | "ios_info": {
46 | "bundle_id": "app.axross.aqua",
47 | "app_store_id": "1485519383"
48 | }
49 | }
50 | ]
51 | }
52 | }
53 | }
54 | ],
55 | "configuration_version": "1"
56 | }
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply plugin: 'kotlin-android'
26 | apply plugin: 'com.google.gms.google-services'
27 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
28 |
29 | android {
30 | compileSdkVersion 30
31 |
32 | sourceSets {
33 | main.java.srcDirs += 'src/main/kotlin'
34 | }
35 |
36 | defaultConfig {
37 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
38 | applicationId "app.axross.aqua"
39 | minSdkVersion 21
40 | targetSdkVersion 30
41 | versionCode flutterVersionCode.toInteger()
42 | versionName flutterVersionName
43 | }
44 |
45 | buildTypes {
46 | release {
47 | // TODO: Add your own signing config for the release build.
48 | // Signing with the debug keys for now, so `flutter run --release` works.
49 | signingConfig signingConfigs.debug
50 | }
51 | }
52 | }
53 |
54 | flutter {
55 | source '../..'
56 | }
57 |
58 | dependencies {
59 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
60 | }
61 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Aqua
2 |
3 | [](https://codemagic.io/apps/5f13196520f165f7137dc471/5f288e6c62e59203f98e84e8/latest_build)
4 |
5 | ♠️ Beautiful Texas Hold'em poker odds calculator made of Flutter.
6 |
7 | - 🎛 Effectively uses [ChangeNotifier](https://api.flutter.dev/flutter/foundation/ChangeNotifier-class.html)/[ValueNotifier](https://api.flutter.dev/flutter/foundation/ValueNotifier-class.html) with [AnimatedBuilder](https://api.flutter.dev/flutter/widgets/AnimatedBuilder-class.html)/[ValueListenableBuilder](https://api.flutter.dev/flutter/widgets/ValueListenableBuilder-class.html). No [provider](https://pub.dev/packages/provider) or BLoC pattern used
8 | - 🧩 [Isolate](https://api.dart.dev/stable/2.9.0/dart-isolate/dart-isolate-library.html) and [Stream](https://api.dart.dev/stable/2.9.0/dart-async/Stream-class.html) are used for multithreading calculation and communication between main thread and isolates
9 | - 💅 Built of an original [theme](lib/src/constants/theme.dart) class and corresponding [widgets](lib/src/common_widgets). Dark mode is supported
10 |
11 | 
12 |
13 | ## Download
14 |
15 | [
](https://play.google.com/store/apps/details?id=app.axross.aqua&hl=en) [
](https://apps.apple.com/us/app/odds-calculator-for-poker/id1485519383)
16 |
17 | If you want an access of the beta program, feel free to contact at [yo@kohei.dev](mailto:yo@kohei.dev)!
18 |
19 | ## Bug Report / Feature Request / Contribution
20 |
21 | Feel free to open an issue or pull request! Everything you do would be grateful!
22 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
13 |
17 |
21 |
26 |
30 |
31 |
32 |
33 |
34 |
35 |
37 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/lib/src/common_widgets/aqua_slider.dart:
--------------------------------------------------------------------------------
1 | import "dart:io";
2 | import "package:aqua/src/common_widgets/aqua_theme.dart";
3 | import "package:flutter/material.dart";
4 | import "package:flutter/services.dart";
5 |
6 | class AquaSlider extends StatefulWidget {
7 | AquaSlider({
8 | Key? key,
9 | required this.divisions,
10 | required this.value,
11 | required this.label,
12 | this.onChanged,
13 | this.onChangeStart,
14 | this.onChangeEnd,
15 | });
16 |
17 | final int divisions;
18 |
19 | final double value;
20 |
21 | final String label;
22 |
23 | final void Function(double value)? onChanged;
24 |
25 | final void Function(double value)? onChangeStart;
26 |
27 | final void Function(double value)? onChangeEnd;
28 |
29 | @override
30 | State createState() => _AquaSliderState();
31 | }
32 |
33 | class _AquaSliderState extends State {
34 | @override
35 | Widget build(BuildContext context) {
36 | final theme = AquaTheme.of(context);
37 | final style = theme.sliderStyle;
38 |
39 | return SliderTheme(
40 | data: SliderThemeData(
41 | thumbColor: style.thumbColor,
42 | activeTrackColor: style.activeTrackColor,
43 | inactiveTrackColor: style.inactiveTrackColor,
44 | valueIndicatorColor: style.valueIndicatorColor,
45 | valueIndicatorTextStyle: style.valueIndicatorTextStyle,
46 | ),
47 | child: Material(
48 | color: Color(0x00000000),
49 | child: Slider(
50 | divisions: widget.divisions,
51 | value: widget.value,
52 | label: widget.label,
53 | onChanged: (value) {
54 | if (Platform.isIOS) {
55 | HapticFeedback.selectionClick();
56 | }
57 |
58 | if (widget.onChanged != null) {
59 | widget.onChanged!(value);
60 | }
61 | },
62 | onChangeStart: (value) {
63 | HapticFeedback.lightImpact();
64 |
65 | if (widget.onChangeStart != null) {
66 | widget.onChangeStart!(value);
67 | }
68 | },
69 | onChangeEnd: (value) {
70 | if (widget.onChangeEnd != null) {
71 | widget.onChangeEnd!(value);
72 | }
73 | },
74 | ),
75 | ),
76 | );
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/lib/src/common_widgets/aqua_tab_view.dart:
--------------------------------------------------------------------------------
1 | import "package:flutter/widgets.dart";
2 |
3 | class AquaTabView extends StatefulWidget {
4 | AquaTabView({
5 | Key? key,
6 | required this.views,
7 | this.activeViewIndex = 0,
8 | }) : assert(views.length >= 1),
9 | assert(activeViewIndex < views.length),
10 | super(key: key);
11 |
12 | final List views;
13 |
14 | final int activeViewIndex;
15 |
16 | @override
17 | State createState() => _AquaTabViewState();
18 | }
19 |
20 | class _AquaTabViewState extends State {
21 | late int _previousActiveViewIndex;
22 |
23 | @override
24 | void initState() {
25 | super.initState();
26 |
27 | _previousActiveViewIndex = widget.activeViewIndex;
28 | }
29 |
30 | @override
31 | void didUpdateWidget(AquaTabView oldWidget) {
32 | super.didUpdateWidget(oldWidget);
33 |
34 | if (oldWidget.activeViewIndex != widget.activeViewIndex) {
35 | setState(() {
36 | _previousActiveViewIndex = oldWidget.activeViewIndex;
37 | });
38 | }
39 | }
40 |
41 | Widget build(BuildContext context) => AnimatedSwitcher(
42 | duration: const Duration(milliseconds: 200),
43 | transitionBuilder: (child, animation) => FadeTransition(
44 | opacity: Tween(begin: 0.5, end: 1).animate(
45 | CurvedAnimation(
46 | parent: animation,
47 | curve: Curves.easeOutCubic,
48 | ),
49 | ),
50 | child: SlideTransition(
51 | position: _previousActiveViewIndex > widget.activeViewIndex
52 | ? Tween(
53 | begin: Offset(-0.05, 0),
54 | end: Offset(0, 0),
55 | ).animate(CurvedAnimation(
56 | parent: animation,
57 | curve: Curves.easeOutCubic,
58 | ))
59 | : Tween(
60 | begin: Offset(0.05, 0),
61 | end: Offset(0, 0),
62 | ).animate(CurvedAnimation(
63 | parent: animation,
64 | curve: Curves.easeOutCubic,
65 | )),
66 | child: child,
67 | ),
68 | ),
69 | child: widget.views[widget.activeViewIndex],
70 | );
71 | }
72 |
--------------------------------------------------------------------------------
/lib/src/view_models/hand_range_draft_list.dart:
--------------------------------------------------------------------------------
1 | import "dart:collection";
2 | import "package:aqua/src/view_models/hand_range_draft.dart";
3 | import "package:flutter/foundation.dart";
4 | import "package:poker/poker.dart";
5 |
6 | class HandRangeDraftList extends ChangeNotifier with ListMixin {
7 | HandRangeDraftList.empty() : _handRangeDrafts = [];
8 |
9 | HandRangeDraftList.of(Iterable handRangeDrafts)
10 | : _handRangeDrafts = handRangeDrafts.toList();
11 |
12 | final List _handRangeDrafts;
13 |
14 | final Map _listeners = {};
15 |
16 | int get length => _handRangeDrafts.length;
17 |
18 | bool get hasIncomplete =>
19 | _handRangeDrafts.any((handRange) => handRange.isEmpty);
20 |
21 | CardSet get usedCards {
22 | final usedCards = CardSet.empty;
23 |
24 | for (final handRangeDraft in _handRangeDrafts) {
25 | if (handRangeDraft.type != HandRangeDraftInputType.cardPair) {
26 | continue;
27 | }
28 |
29 | if (handRangeDraft.firstCardPair[0] != null) {
30 | usedCards.union(CardSet.single(handRangeDraft.firstCardPair[0]!));
31 | }
32 |
33 | if (handRangeDraft.firstCardPair[1] != null) {
34 | usedCards.union(CardSet.single(handRangeDraft.firstCardPair[1]!));
35 | }
36 | }
37 |
38 | return usedCards;
39 | }
40 |
41 | set length(value) {
42 | if (value <= length) {
43 | for (final handRangeDraft in _handRangeDrafts.getRange(value, length)) {
44 | handRangeDraft.removeListener(_listeners[handRangeDraft]!);
45 | }
46 |
47 | _handRangeDrafts.length = value;
48 |
49 | return;
50 | }
51 |
52 | for (int i = length; i < value; ++i) {
53 | final handRange = HandRangeDraft.emptyCardPair();
54 |
55 | _handRangeDrafts.add(handRange);
56 | _listeners[handRange] = () {
57 | notifyListeners();
58 | };
59 | }
60 | }
61 |
62 | operator [](index) => _handRangeDrafts[index];
63 |
64 | operator []=(index, handRangeDraft) {
65 | assert(index < length);
66 |
67 | final previous = _handRangeDrafts[index];
68 |
69 | previous.removeListener(_listeners[previous]!);
70 |
71 | _handRangeDrafts[index] = handRangeDraft;
72 |
73 | _listeners[handRangeDraft] = () {
74 | notifyListeners();
75 | };
76 |
77 | handRangeDraft.addListener(_listeners[handRangeDraft]!);
78 |
79 | notifyListeners();
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import "dart:async";
2 | import "package:amplitude_flutter/amplitude.dart";
3 | import "package:aqua/app/app.dart";
4 | import "package:aqua/app/services/amplitude_analytics_service.dart";
5 | import "package:aqua/app/services/firebase_auth_manager_service.dart";
6 | import "package:aqua/app/services/noop_error_reporter_service.dart";
7 | import "package:aqua/app/services/sentry_error_reporter_service.dart";
8 | import 'package:aqua/src/common_widgets/aqua_preferences.dart';
9 | import "package:firebase_analytics/firebase_analytics.dart";
10 | import 'package:firebase_core/firebase_core.dart';
11 | import "package:flutter/foundation.dart";
12 | import "package:flutter/widgets.dart";
13 |
14 | void main() async {
15 | WidgetsFlutterBinding.ensureInitialized();
16 |
17 | final authManagerService = FirebaseAuthManagerService();
18 | final amplitudeInstance = Amplitude.getInstance();
19 | final analyticsService = AmplitudeAnalyticsService(
20 | amplitudeAnalytics: amplitudeInstance,
21 | firebaseAnalytics: FirebaseAnalytics(),
22 | );
23 | final applicationPreferenceData = AquaPreferenceData();
24 |
25 | if (!kDebugMode) {
26 | final errorReporter = SentryErrorReporterService(
27 | sentryDsn:
28 | "https://7f698d26a29e495881c4adf639830a1a@o30395.ingest.sentry.io/5375048",
29 | );
30 |
31 | FlutterError.onError = (details) {
32 | errorReporter.captureFlutterException(details);
33 | };
34 |
35 | runZonedGuarded(() async {
36 | runApp(AquaApp(
37 | analyticsService: analyticsService,
38 | authManagerService: authManagerService,
39 | errorReporter: errorReporter,
40 | applicationPreferenceData: applicationPreferenceData,
41 | prepare: () async {
42 | await Firebase.initializeApp();
43 | await authManagerService.initialize();
44 | await amplitudeInstance.init("94ba98446847f79253029f7f8e6d9cf3");
45 | await amplitudeInstance
46 | .setUserProperties({"Environment": "production"});
47 | await amplitudeInstance.trackingSessionEvents(true);
48 | await applicationPreferenceData.initialize();
49 | },
50 | ));
51 | }, (exception, stackTrace) async {
52 | errorReporter.captureException(
53 | exception: exception,
54 | stackTrace: stackTrace,
55 | );
56 | });
57 | } else {
58 | runApp(AquaApp(
59 | analyticsService: analyticsService,
60 | authManagerService: authManagerService,
61 | errorReporter: NoopErrorReporterService(),
62 | applicationPreferenceData: applicationPreferenceData,
63 | ));
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/lib/app/services/flutter_isolate_evaluation_service.dart:
--------------------------------------------------------------------------------
1 | import "dart:async";
2 | import "dart:isolate";
3 | import "package:aqua/src/models/calculation.dart";
4 | import "package:aqua/src/services/isolate_evaluation_service.dart";
5 | import "package:poker/poker.dart";
6 |
7 | class FlutterIsolateEvaluationService implements IsolateEvaluationService {
8 | Isolate? _isolate;
9 |
10 | SendPort? _toIsolate;
11 |
12 | final _streamController = StreamController.broadcast();
13 |
14 | Stream get onProgress => _streamController.stream;
15 |
16 | Future initialize() async {
17 | final completer = Completer();
18 | final receivePort = ReceivePort();
19 |
20 | receivePort.listen((data) async {
21 | if (_toIsolate == null) {
22 | _toIsolate = data;
23 | completer.complete();
24 |
25 | return;
26 | }
27 |
28 | if (_streamController.isClosed) return;
29 |
30 | if (data is Exception) {
31 | _streamController.addError(data);
32 | } else {
33 | _streamController.add(data);
34 | }
35 | });
36 |
37 | _isolate = await Isolate.spawn(_isolateFunction, receivePort.sendPort);
38 |
39 | await completer.future;
40 | }
41 |
42 | Future dispose() async {
43 | _isolate?.kill(priority: Isolate.immediate);
44 |
45 | _streamController.close();
46 | }
47 |
48 | void requestEvaluation({
49 | required CardSet communityCards,
50 | required List players,
51 | required int times,
52 | }) {
53 | _toIsolate?.send([communityCards, players, times]);
54 | }
55 | }
56 |
57 | void _isolateFunction(SendPort toMain) {
58 | final receivePort = ReceivePort();
59 |
60 | receivePort.listen((data) async {
61 | final communityCards = data[0] as CardSet;
62 | final players = data[1] as List;
63 | final times = data[2] as int;
64 | final startedAt = DateTime.now();
65 |
66 | if (times % 2000 != 0) {
67 | toMain.send(ArgumentError.value(times, "times must be multiple of 2000"));
68 | }
69 |
70 | final evaluator = MontecarloEvaluator(
71 | communityCards: communityCards,
72 | players: players,
73 | );
74 | EvaluationResult accumulatedResult =
75 | EvaluationResult.empty(playerLength: players.length);
76 |
77 | for (int i = 1; i <= times / 2000; ++i) {
78 | for (final result in evaluator.take(2000)) {
79 | accumulatedResult += result;
80 | }
81 |
82 | toMain.send(Calculation(
83 | communityCards: communityCards,
84 | players: players,
85 | result: accumulatedResult,
86 | startedAt: startedAt,
87 | endedAt: DateTime.now(),
88 | ));
89 | }
90 | });
91 |
92 | toMain.send(receivePort.sendPort);
93 | }
94 |
--------------------------------------------------------------------------------
/lib/app/services/amplitude_analytics_service.dart:
--------------------------------------------------------------------------------
1 | import "package:amplitude_flutter/amplitude.dart";
2 | import "package:aqua/src/services/analytics_service.dart";
3 | import "package:aqua/src/models/anonymous_user.dart";
4 | import "package:firebase_analytics/firebase_analytics.dart";
5 | import "package:flutter/widgets.dart";
6 |
7 | @immutable
8 | class AmplitudeAnalyticsService implements AnalyticsService {
9 | const AmplitudeAnalyticsService({
10 | required Amplitude amplitudeAnalytics,
11 | required FirebaseAnalytics firebaseAnalytics,
12 | }) : _amplitudeAnalytics = amplitudeAnalytics,
13 | _firebaseAnalytics = firebaseAnalytics;
14 |
15 | final Amplitude _amplitudeAnalytics;
16 |
17 | final FirebaseAnalytics _firebaseAnalytics;
18 |
19 | void setUser(AnonymousUser? user) {
20 | _amplitudeAnalytics.setUserId(user?.id);
21 | _firebaseAnalytics.setUserId(user?.id);
22 | }
23 |
24 | void logScreenChange({
25 | required String screenName,
26 | Map parameters = const {},
27 | }) {
28 | final encodedParameters = _encodeParameters(parameters);
29 |
30 | _amplitudeAnalytics.logEvent(
31 | "Transition to $screenName",
32 | eventProperties: encodedParameters,
33 | );
34 | _firebaseAnalytics.logEvent(
35 | name: _toSnakeCase("Transition to $screenName"),
36 | parameters: _snakeCaseParameters(encodedParameters),
37 | );
38 | _firebaseAnalytics.setCurrentScreen(
39 | screenName: screenName,
40 | );
41 | }
42 |
43 | void logEvent({
44 | required String name,
45 | Map parameters = const {},
46 | }) {
47 | final encodedParameters = _encodeParameters(parameters);
48 |
49 | _amplitudeAnalytics.logEvent(
50 | name,
51 | eventProperties: encodedParameters,
52 | );
53 | _firebaseAnalytics.logEvent(
54 | name: _toSnakeCase(name),
55 | parameters: _snakeCaseParameters(encodedParameters),
56 | );
57 | }
58 |
59 | Map _encodeParameters(Map parameters) {
60 | return parameters.map((key, value) {
61 | dynamic processedValue;
62 |
63 | if (value is num || value is String || value is bool) {
64 | processedValue = value;
65 | }
66 |
67 | return MapEntry(key, processedValue);
68 | });
69 | }
70 |
71 | Map _snakeCaseParameters(Map parameters) {
72 | return parameters.map((key, value) => MapEntry(_toSnakeCase(key), value));
73 | }
74 | }
75 |
76 | String _toSnakeCase(String value) {
77 | return value
78 | .replaceAll(RegExp(r" +"), "_")
79 | .replaceAllMapped(
80 | RegExp(r"[A-Z]"),
81 | (match) => String.fromCharCode(match[0]!.codeUnitAt(0) + 32),
82 | )
83 | .replaceAll(RegExp(r"[^A-Za-z0-9_]+"), "");
84 | }
85 |
--------------------------------------------------------------------------------
/lib/src/common_widgets/playing_card.dart:
--------------------------------------------------------------------------------
1 | import "package:aqua/src/common_widgets/aqua_icons.dart";
2 | import "package:aqua/src/common_widgets/aqua_theme.dart";
3 | import "package:aqua/src/common_widgets/fill.dart";
4 | import "package:aqua/src/constants/card.dart";
5 | import "package:flutter/widgets.dart";
6 | import "package:poker/poker.dart";
7 |
8 | class PlayingCard extends StatelessWidget {
9 | PlayingCard({Key? key, required this.card}) : super(key: key);
10 |
11 | final Card card;
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | final style = AquaTheme.of(context).playingCardStyle;
16 |
17 | return Fill(
18 | child: AspectRatio(
19 | aspectRatio: 2.25 / 3.5,
20 | child: LayoutBuilder(
21 | builder: (_, constraints) => DecoratedBox(
22 | decoration: BoxDecoration(
23 | color: style.backgroundColor,
24 | borderRadius: BorderRadius.circular(constraints.maxWidth * 0.1),
25 | ),
26 | child: Stack(
27 | children: [
28 | Align(
29 | alignment: Alignment.topCenter,
30 | child: Text(
31 | rankChars[card.rank]!,
32 | textAlign: TextAlign.center,
33 | style: style.textStyle.copyWith(
34 | color: style.suitColors[card.suit],
35 | fontSize: constraints.maxHeight * 0.475,
36 | ),
37 | ),
38 | ),
39 | Align(
40 | alignment: Alignment.bottomCenter,
41 | child: Padding(
42 | padding: EdgeInsets.only(
43 | bottom: constraints.maxHeight * 0.05,
44 | ),
45 | child: FittedBox(
46 | fit: BoxFit.contain,
47 | child: Icon(
48 | _suitIcons[card.suit],
49 | color: style.suitColors[card.suit],
50 | size: constraints.maxHeight * 0.475,
51 | ),
52 | ),
53 | ),
54 | ),
55 | ],
56 | ),
57 | ),
58 | ),
59 | ),
60 | );
61 | }
62 | }
63 |
64 | const _suitIcons = {
65 | Suit.spade: AquaIcons.spade,
66 | Suit.heart: AquaIcons.heart,
67 | Suit.diamond: AquaIcons.diamond,
68 | Suit.club: AquaIcons.club,
69 | };
70 |
71 | class PlayingCardBack extends StatelessWidget {
72 | @override
73 | Widget build(BuildContext context) {
74 | return Fill(
75 | child: AspectRatio(
76 | aspectRatio: 2.25 / 3.5,
77 | child: LayoutBuilder(
78 | builder: (_, constraints) => Container(
79 | decoration: BoxDecoration(
80 | color: AquaTheme.of(context).playingCardStyle.backgroundColor,
81 | borderRadius: BorderRadius.circular(constraints.maxWidth * 0.1),
82 | ),
83 | ),
84 | ),
85 | ),
86 | );
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/lib/src/common_widgets/readonly_rank_pair_grid.dart:
--------------------------------------------------------------------------------
1 | import "package:aqua/src/common_widgets/aqua_theme.dart";
2 | import "package:aqua/src/constants/card.dart";
3 | import "package:flutter/widgets.dart";
4 | import "package:poker/poker.dart";
5 |
6 | class ReadonlyRankPairGrid extends StatelessWidget {
7 | ReadonlyRankPairGrid({required this.rankPairs, Key? key}) : super(key: key);
8 |
9 | final Set rankPairs;
10 |
11 | @override
12 | Widget build(BuildContext context) {
13 | final style = AquaTheme.of(context).rankPairGridStyle;
14 |
15 | return LayoutBuilder(
16 | builder: (context, constraints) => DecoratedBox(
17 | decoration: BoxDecoration(
18 | color: style.backgroundColor,
19 | borderRadius: BorderRadius.circular(constraints.maxWidth * 0.05),
20 | ),
21 | child: Table(
22 | children: List.generate(ranksInStrongnessOrder.length, (row) {
23 | return TableRow(
24 | children: List.generate(ranksInStrongnessOrder.length, (column) {
25 | final high = row < column ? row : column;
26 | final kicker = high == row ? column : row;
27 | final rankPairsPart = row < column
28 | ? RankPair.suited(
29 | high: ranksInStrongnessOrder[high],
30 | kicker: ranksInStrongnessOrder[kicker],
31 | )
32 | : RankPair.ofsuit(
33 | high: ranksInStrongnessOrder[high],
34 | kicker: ranksInStrongnessOrder[kicker],
35 | );
36 | BorderRadius? borderRadius;
37 |
38 | if (row == 0 && column == 0) {
39 | borderRadius = BorderRadius.only(
40 | topLeft: Radius.circular(constraints.maxWidth * 0.05));
41 | }
42 |
43 | if (row == 0 && column == ranksInStrongnessOrder.length - 1) {
44 | borderRadius = BorderRadius.only(
45 | topRight: Radius.circular(constraints.maxWidth * 0.05));
46 | }
47 |
48 | if (row == ranksInStrongnessOrder.length - 1 && column == 0) {
49 | borderRadius = BorderRadius.only(
50 | bottomLeft: Radius.circular(constraints.maxWidth * 0.05));
51 | }
52 |
53 | if (row == ranksInStrongnessOrder.length - 1 &&
54 | column == ranksInStrongnessOrder.length - 1) {
55 | borderRadius = BorderRadius.only(
56 | bottomRight:
57 | Radius.circular(constraints.maxWidth * 0.05));
58 | }
59 |
60 | return TableCell(
61 | child: Container(
62 | height: constraints.maxWidth / 13,
63 | decoration: BoxDecoration(
64 | color: rankPairs.contains(rankPairsPart)
65 | ? style.selectedForegroundColor
66 | : null,
67 | borderRadius: borderRadius,
68 | ),
69 | ),
70 | );
71 | }),
72 | );
73 | }),
74 | ),
75 | ),
76 | );
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/lib/src/common_widgets/aqua_theme.dart:
--------------------------------------------------------------------------------
1 | import "package:aqua/src/common_widgets/aqua_button.dart";
2 | import "package:aqua/src/common_widgets/aqua_scaffold.dart";
3 | import "package:aqua/src/common_widgets/digits_text.dart";
4 | import "package:flutter/widgets.dart";
5 | import "package:poker/poker.dart";
6 |
7 | class AquaTheme extends InheritedWidget {
8 | AquaTheme({
9 | required this.data,
10 | required Widget child,
11 | Key? key,
12 | }) : super(
13 | key: key,
14 | child: DefaultAquaButtonStyle(
15 | style: data.buttonStyleSet.normal,
16 | child: DefaultTextStyle(
17 | style: data.textStyleSet.body,
18 | child: child,
19 | ),
20 | ),
21 | );
22 |
23 | final AquaThemeData data;
24 |
25 | @override
26 | bool updateShouldNotify(AquaTheme old) => data != old.data;
27 |
28 | static AquaThemeData of(BuildContext context) =>
29 | context.dependOnInheritedWidgetOfExactType()!.data;
30 | }
31 |
32 | @immutable
33 | class AquaThemeData {
34 | const AquaThemeData({
35 | required this.textStyleSet,
36 | required this.buttonStyleSet,
37 | required this.scaffoldStyle,
38 | required this.playingCardStyle,
39 | required this.rankPairGridStyle,
40 | required this.digitTextStyle,
41 | required this.sliderStyle,
42 | required this.cursorColor,
43 | required this.elevationBoxShadows,
44 | });
45 |
46 | final AquaTextStyleSet textStyleSet;
47 |
48 | final AquaButtonStyleSet buttonStyleSet;
49 |
50 | final AquaScaffoldStyle scaffoldStyle;
51 |
52 | final AquaPlayingCardStyle playingCardStyle;
53 |
54 | final AquaRankPairGridStyle rankPairGridStyle;
55 |
56 | final AquaDigitTextStyle digitTextStyle;
57 |
58 | final AquaSliderStyle sliderStyle;
59 |
60 | final Color cursorColor;
61 |
62 | final List elevationBoxShadows;
63 | }
64 |
65 | @immutable
66 | class AquaTextStyleSet {
67 | const AquaTextStyleSet({
68 | required this.headline,
69 | required this.body,
70 | required this.caption,
71 | required this.errorCaption,
72 | });
73 |
74 | final TextStyle headline;
75 |
76 | final TextStyle body;
77 |
78 | final TextStyle caption;
79 |
80 | final TextStyle errorCaption;
81 | }
82 |
83 | class AquaPlayingCardStyle {
84 | const AquaPlayingCardStyle({
85 | required this.textStyle,
86 | required this.backgroundColor,
87 | required this.suitColors,
88 | });
89 |
90 | final TextStyle textStyle;
91 |
92 | final Color backgroundColor;
93 |
94 | final Map suitColors;
95 | }
96 |
97 | class AquaRankPairGridStyle {
98 | AquaRankPairGridStyle({
99 | required this.backgroundColor,
100 | required this.textStyle,
101 | required this.selectedBackgroundColor,
102 | required this.selectedForegroundColor,
103 | });
104 |
105 | final Color backgroundColor;
106 |
107 | final TextStyle textStyle;
108 |
109 | final Color selectedBackgroundColor;
110 |
111 | final Color selectedForegroundColor;
112 | }
113 |
114 | class AquaSliderStyle {
115 | AquaSliderStyle({
116 | required this.thumbColor,
117 | required this.activeTrackColor,
118 | required this.inactiveTrackColor,
119 | required this.valueIndicatorColor,
120 | required this.valueIndicatorTextStyle,
121 | });
122 |
123 | final Color thumbColor;
124 |
125 | final Color activeTrackColor;
126 |
127 | final Color inactiveTrackColor;
128 |
129 | final Color valueIndicatorColor;
130 |
131 | final TextStyle valueIndicatorTextStyle;
132 | }
133 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/lib/app/main_route.dart:
--------------------------------------------------------------------------------
1 | import "package:aqua/app/main_route/preferences_tab_page.dart";
2 | import "package:aqua/app/main_route/simulation_tab_page.dart";
3 | import "package:aqua/src/common_widgets/analytics.dart";
4 | import "package:aqua/src/common_widgets/aqua_icons.dart";
5 | import "package:aqua/src/common_widgets/aqua_tab_bar.dart";
6 | import "package:aqua/src/common_widgets/aqua_tab_view.dart";
7 | import "package:aqua/src/view_models/simulation_session.dart";
8 | import "package:flutter/cupertino.dart";
9 |
10 | class MainRoute extends CupertinoPageRoute {
11 | MainRoute({
12 | RouteSettings? settings,
13 | }) : super(
14 | title: "Main",
15 | builder: (context) => _MainPage(),
16 | settings: settings,
17 | );
18 | }
19 |
20 | class _MainPage extends StatefulWidget {
21 | @override
22 | State<_MainPage> createState() => _MainPageState();
23 | }
24 |
25 | class _MainPageState extends State<_MainPage> {
26 | late int _activeTabViewIndex;
27 |
28 | /// A ValueNotifier that holds a CalculationSession inside.
29 | /// Replace the held CalculationSession to start a new session
30 | late ValueNotifier _calculationSession;
31 |
32 | @override
33 | void initState() {
34 | super.initState();
35 |
36 | _activeTabViewIndex = 0;
37 |
38 | late CalculationSession calculationSession;
39 | calculationSession = CalculationSession.initial(
40 | onStartCalculation: (_, __) {
41 | Analytics.of(context).logEvent(
42 | name: "Start Simulation",
43 | parameters: {
44 | "Number of Community Cards":
45 | calculationSession.communityCards.toSet().length,
46 | "Number of Hand Ranges": calculationSession.players.length,
47 | },
48 | );
49 | },
50 | onFinishCalculation: (snapshot) {
51 | Analytics.of(context).logEvent(
52 | name: "Finish Simulation",
53 | parameters: {
54 | "Number of Community Cards":
55 | calculationSession.communityCards.toSet().length,
56 | "Number of Hand Ranges": calculationSession.players.length,
57 | },
58 | );
59 | },
60 | );
61 |
62 | _calculationSession = ValueNotifier(calculationSession);
63 | }
64 |
65 | @override
66 | Widget build(BuildContext context) {
67 | return ValueListenableBuilder(
68 | valueListenable: _calculationSession,
69 | builder: (context, calculationSession, _) => Container(
70 | color: Color(0xffffffff),
71 | child: Column(
72 | children: [
73 | Expanded(
74 | child: AquaTabView(
75 | views: [
76 | SimulationTabPage(
77 | key: ValueKey(0),
78 | calculationSession: calculationSession,
79 | ),
80 | PreferencesTabPage(key: ValueKey(1)),
81 | ],
82 | activeViewIndex: _activeTabViewIndex,
83 | ),
84 | ),
85 | AquaTabBar(
86 | activeIndex: _activeTabViewIndex,
87 | onChanged: (index) {
88 | setState(() {
89 | _activeTabViewIndex = index;
90 | });
91 | },
92 | items: [
93 | AquaTabBarItem(label: "Calculation", icon: AquaIcons.percent),
94 | AquaTabBarItem(label: "Preferences", icon: AquaIcons.gear),
95 | ],
96 | ),
97 | ],
98 | ),
99 | ),
100 | );
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/lib/src/common_widgets/aqua_scaffold.dart:
--------------------------------------------------------------------------------
1 | import "package:aqua/src/common_widgets/analytics.dart";
2 | import "package:aqua/src/common_widgets/aqua_button.dart";
3 | import "package:aqua/src/common_widgets/aqua_icons.dart";
4 | import "package:aqua/src/common_widgets/aqua_theme.dart";
5 | import "package:flutter/widgets.dart";
6 |
7 | @immutable
8 | class AquaScadffold extends StatelessWidget {
9 | AquaScadffold({
10 | Key? key,
11 | this.scrollController,
12 | this.title = "Title",
13 | this.actions = const [],
14 | this.slivers = const [],
15 | }) : super(key: key);
16 |
17 | final ScrollController? scrollController;
18 |
19 | final String title;
20 |
21 | final List actions;
22 |
23 | final List slivers;
24 |
25 | @override
26 | Widget build(BuildContext context) {
27 | final theme = AquaTheme.of(context);
28 | final style = theme.scaffoldStyle;
29 | final defaultActionButtonStyle =
30 | theme.buttonStyleSet[style.defaultActionButtonvariant];
31 |
32 | return Container(
33 | color: style.backgroundColor,
34 | child: CustomScrollView(
35 | controller: scrollController,
36 | slivers: [
37 | SliverSafeArea(
38 | sliver: SliverPadding(
39 | padding: const EdgeInsets.fromLTRB(16, 32, 16, 16),
40 | sliver: SliverToBoxAdapter(
41 | child: Row(
42 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
43 | children: [
44 | Row(
45 | children: [
46 | if (ModalRoute.of(context)?.canPop ?? false) ...[
47 | AquaButton(
48 | variant: AquaButtonVariant.secondary,
49 | icon: AquaIcons.chevronLeft,
50 | onTap: () {
51 | Analytics.of(context).logEvent(
52 | name: "Tap a Scaffold Back Button",
53 | );
54 |
55 | Navigator.of(context).pop();
56 | },
57 | ),
58 | SizedBox(width: 16),
59 | ],
60 | Text(
61 | title,
62 | textAlign: TextAlign.start,
63 | style: style.titleTextStyle,
64 | ),
65 | ],
66 | ),
67 | DefaultAquaButtonStyle(
68 | style: defaultActionButtonStyle,
69 | child: Row(
70 | crossAxisAlignment: CrossAxisAlignment.center,
71 | children: actions,
72 | ),
73 | ),
74 | ],
75 | ),
76 | ),
77 | ),
78 | ),
79 | ...slivers,
80 | SliverSafeArea(
81 | bottom: true,
82 | sliver: SliverPadding(
83 | padding: EdgeInsets.only(bottom: 64.0),
84 | ),
85 | ),
86 | ],
87 | ),
88 | );
89 | }
90 | }
91 |
92 | @immutable
93 | class AquaScaffoldStyle {
94 | const AquaScaffoldStyle({
95 | required this.titleTextStyle,
96 | this.defaultActionButtonvariant = AquaButtonVariant.secondary,
97 | required this.backgroundColor,
98 | });
99 |
100 | final TextStyle titleTextStyle;
101 |
102 | final AquaButtonVariant defaultActionButtonvariant;
103 |
104 | final Color backgroundColor;
105 | }
106 |
--------------------------------------------------------------------------------
/lib/src/view_models/hand_range_draft.dart:
--------------------------------------------------------------------------------
1 | import "package:aqua/src/view_models/card_pair_draft.dart";
2 | import "package:flutter/foundation.dart";
3 | import "package:poker/poker.dart";
4 |
5 | extension Open on HandRange {
6 | Set get onlyCardPairs => components.whereType().toSet();
7 |
8 | bool get hasCardPair => onlyCardPairs.length >= 1;
9 |
10 | Set get onlyRankPairs => components.whereType().toSet();
11 |
12 | bool get hasRankPair => onlyRankPairs.length >= 1;
13 | }
14 |
15 | class HandRangeDraft extends ChangeNotifier {
16 | HandRangeDraft({
17 | required HandRangeDraftInputType type,
18 | required List cardPairs,
19 | required Set rankPairs,
20 | }) : _type = type,
21 | _cardPairs = cardPairs,
22 | _rankPairs = rankPairs;
23 |
24 | HandRangeDraft.emptyCardPair()
25 | : _type = HandRangeDraftInputType.cardPair,
26 | _cardPairs = [CardPairDraft.empty()],
27 | _rankPairs = {};
28 |
29 | HandRangeDraft.emptyRankPairs()
30 | : _type = HandRangeDraftInputType.rankPairs,
31 | _cardPairs = [CardPairDraft.empty()],
32 | _rankPairs = {};
33 |
34 | HandRangeDraft.fromHandRange(HandRange handRange)
35 | : _type = handRange.hasCardPair
36 | ? handRange.hasRankPair
37 | ? HandRangeDraftInputType.mixed
38 | : HandRangeDraftInputType.cardPair
39 | : HandRangeDraftInputType.rankPairs,
40 | _cardPairs = handRange.onlyCardPairs.length >= 1
41 | ? handRange.onlyCardPairs
42 | .map((cp) => CardPairDraft(cp.first, cp.last))
43 | .toList()
44 | : [CardPairDraft.empty()],
45 | _rankPairs = handRange.onlyRankPairs;
46 |
47 | HandRangeDraftInputType _type;
48 |
49 | List _cardPairs;
50 |
51 | Set _rankPairs;
52 |
53 | HandRangeDraftInputType get type => _type;
54 |
55 | set type(HandRangeDraftInputType type) {
56 | if (type == _type) {
57 | return;
58 | }
59 |
60 | _type = type;
61 |
62 | if (type == HandRangeDraftInputType.cardPair) {
63 | _rankPairs = {};
64 | }
65 |
66 | if (type == HandRangeDraftInputType.rankPairs) {
67 | _cardPairs = [CardPairDraft.empty()];
68 | }
69 |
70 | notifyListeners();
71 | }
72 |
73 | CardPairDraft get firstCardPair => _cardPairs[0];
74 |
75 | set firstCardPair(CardPairDraft cardPair) {
76 | if (cardPair == _cardPairs[0]) {
77 | return;
78 | }
79 |
80 | _cardPairs[0] = cardPair;
81 |
82 | notifyListeners();
83 | }
84 |
85 | Set get rankPairs => _rankPairs;
86 |
87 | set rankPairs(Set rankPairs) {
88 | if (rankPairs == _rankPairs) {
89 | return;
90 | }
91 |
92 | _rankPairs = rankPairs;
93 |
94 | notifyListeners();
95 | }
96 |
97 | bool get isEmpty => toHandRange().isEmpty;
98 |
99 | bool get isNotEmpty => toHandRange().isNotEmpty;
100 |
101 | HandRange toHandRange() {
102 | switch (_type) {
103 | case HandRangeDraftInputType.cardPair:
104 | return HandRange(
105 | firstCardPair.isComplete ? {firstCardPair.toCardPair()} : {});
106 | case HandRangeDraftInputType.rankPairs:
107 | return HandRange(_rankPairs);
108 | case HandRangeDraftInputType.mixed:
109 | return HandRange({
110 | ..._cardPairs
111 | .where((cp) => cp.isComplete)
112 | .map((cp) => cp.toCardPair()),
113 | ..._rankPairs
114 | });
115 | default:
116 | throw UnimplementedError();
117 | }
118 | }
119 |
120 | @override
121 | String toString() {
122 | return "{ type: $_type, cardPairs: _cardPairs, rankPairs: _rankPairs }";
123 | }
124 | }
125 |
126 | enum HandRangeDraftInputType {
127 | cardPair,
128 | rankPairs,
129 | mixed,
130 | }
131 |
--------------------------------------------------------------------------------
/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | aqua
27 |
28 |
29 |
30 |
33 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/lib/src/view_models/simulation_session.dart:
--------------------------------------------------------------------------------
1 | import "package:aqua/app/services/flutter_isolate_evaluation_service.dart";
2 | import "package:aqua/src/models/calculation.dart";
3 | import "package:aqua/src/services/isolate_evaluation_service.dart";
4 | import "package:aqua/src/view_models/hand_range_draft_list.dart";
5 | import "package:flutter/foundation.dart";
6 | import "package:poker/poker.dart";
7 |
8 | class CalculationSession extends ChangeNotifier {
9 | CalculationSession.initial({
10 | this.onStartCalculation,
11 | this.onFinishCalculation,
12 | }) : _communityCards = CardSet.empty,
13 | _players = HandRangeDraftList.empty(),
14 | _result = EvaluationResult.empty(playerLength: 0) {
15 | _players.addListener(() {
16 | _clearResults();
17 | _enqueueCalculation();
18 | notifyListeners();
19 | });
20 | }
21 |
22 | final void Function(
23 | CardSet communityCards,
24 | List players,
25 | )? onStartCalculation;
26 |
27 | final void Function(Calculation? calculation)? onFinishCalculation;
28 |
29 | HandRangeDraftList _players;
30 |
31 | CardSet _communityCards;
32 |
33 | CardSet get communityCards => _communityCards;
34 |
35 | set communityCards(CardSet communityCards) {
36 | if (communityCards == _communityCards) {
37 | return;
38 | }
39 |
40 | _communityCards = communityCards;
41 |
42 | _clearResults();
43 | _enqueueCalculation();
44 | notifyListeners();
45 | }
46 |
47 | HandRangeDraftList get players => _players;
48 |
49 | EvaluationResult _result;
50 |
51 | EvaluationResult get result => _result;
52 |
53 | bool _hasPossibleMatchup = true;
54 |
55 | get hasPossibleMatchup => _hasPossibleMatchup;
56 |
57 | IsolateEvaluationService? _isolateEvaluationService;
58 |
59 | void _clearResults() {
60 | _result = EvaluationResult.empty(playerLength: players.length);
61 | }
62 |
63 | void _enqueueCalculation() async {
64 | if (_players.length <= 1) return;
65 | if (_players.hasIncomplete) return;
66 |
67 | final players = _players.map((hr) => hr.toHandRange()).toList();
68 |
69 | if (_isolateEvaluationService != null) {
70 | _isolateEvaluationService!.dispose();
71 | _isolateEvaluationService = null;
72 |
73 | if (onFinishCalculation != null) {
74 | onFinishCalculation!(null);
75 | }
76 | }
77 |
78 | _result = EvaluationResult.empty(playerLength: _players.length);
79 |
80 | notifyListeners();
81 |
82 | final isolateEvaluationService = FlutterIsolateEvaluationService();
83 |
84 | _isolateEvaluationService = isolateEvaluationService;
85 |
86 | await isolateEvaluationService.initialize();
87 |
88 | if (onStartCalculation != null) {
89 | onStartCalculation!(communityCards, players);
90 | }
91 |
92 | final timesToSimulate = 200000;
93 |
94 | isolateEvaluationService
95 | ..onProgress.listen(
96 | (calculation) {
97 | _hasPossibleMatchup = true;
98 | _result = calculation.result;
99 |
100 | notifyListeners();
101 |
102 | if (calculation.result.tries == timesToSimulate) {
103 | isolateEvaluationService.dispose();
104 | _isolateEvaluationService = null;
105 |
106 | if (onFinishCalculation != null) {
107 | onFinishCalculation!(calculation);
108 | }
109 | }
110 | },
111 | onError: (error) {
112 | isolateEvaluationService.dispose();
113 | _isolateEvaluationService = null;
114 |
115 | // if (error is NoPossibleMatchupException) {
116 | // debugPrint("calculation canceled: ${error.runtimeType}");
117 |
118 | _hasPossibleMatchup = false;
119 |
120 | notifyListeners();
121 |
122 | // return;
123 | // }
124 |
125 | throw error;
126 | },
127 | )
128 | ..requestEvaluation(
129 | players: players,
130 | communityCards: communityCards,
131 | times: timesToSimulate,
132 | );
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/lib/src/common_widgets/aqua_button.dart:
--------------------------------------------------------------------------------
1 | import "package:aqua/src/common_widgets/aqua_theme.dart";
2 | import "package:flutter/widgets.dart";
3 |
4 | class AquaButton extends StatelessWidget {
5 | AquaButton({
6 | this.variant,
7 | this.label,
8 | this.icon,
9 | this.style,
10 | this.onTap,
11 | Key? key,
12 | }) : assert(style != null || variant != null),
13 | super(key: key);
14 |
15 | final AquaButtonVariant? variant;
16 |
17 | final String? label;
18 |
19 | final IconData? icon;
20 |
21 | final AquaButtonStyle? style;
22 |
23 | final void Function()? onTap;
24 |
25 | AquaButtonStyle _resolveStyle(BuildContext context) {
26 | if (style != null) return style!;
27 | if (variant != null) return AquaTheme.of(context).buttonStyleSet[variant!];
28 |
29 | final defaultStyle = DefaultAquaButtonStyle.of(context);
30 |
31 | assert(defaultStyle != null);
32 |
33 | return defaultStyle!;
34 | }
35 |
36 | @override
37 | Widget build(BuildContext context) {
38 | final style = _resolveStyle(context);
39 |
40 | return GestureDetector(
41 | onTap: onTap,
42 | child: DecoratedBox(
43 | decoration: ShapeDecoration(
44 | color: style.backgroundColor,
45 | shape: RoundedRectangleBorder(
46 | borderRadius: BorderRadius.circular(8.0),
47 | ),
48 | ),
49 | child: ConstrainedBox(
50 | constraints: BoxConstraints(
51 | minWidth: aquaButtonHeight,
52 | maxWidth: double.infinity,
53 | minHeight: aquaButtonHeight,
54 | maxHeight: aquaButtonHeight,
55 | ),
56 | child: Padding(
57 | padding: label != null
58 | ? const EdgeInsets.symmetric(horizontal: 12.0)
59 | : EdgeInsets.zero,
60 | child: Row(
61 | mainAxisAlignment: MainAxisAlignment.center,
62 | crossAxisAlignment: CrossAxisAlignment.center,
63 | children: [
64 | if (icon != null)
65 | Icon(
66 | icon,
67 | size: style.labelTextStyle.fontSize! * 20 / 14,
68 | color: style.labelTextStyle.color,
69 | ),
70 | if (label != null && icon != null) SizedBox(width: 4.0),
71 | if (label != null) Text(label!, style: style.labelTextStyle),
72 | ],
73 | ),
74 | ),
75 | ),
76 | ),
77 | );
78 | }
79 | }
80 |
81 | class DefaultAquaButtonStyle extends InheritedWidget {
82 | DefaultAquaButtonStyle({
83 | required this.style,
84 | required Widget child,
85 | Key? key,
86 | }) : super(key: key, child: child);
87 |
88 | final AquaButtonStyle style;
89 |
90 | @override
91 | bool updateShouldNotify(DefaultAquaButtonStyle old) => style != old.style;
92 |
93 | static AquaButtonStyle? of(BuildContext context) {
94 | final a =
95 | context.dependOnInheritedWidgetOfExactType();
96 |
97 | return a != null ? a.style : null;
98 | }
99 | }
100 |
101 | enum AquaButtonVariant {
102 | normal,
103 | primary,
104 | secondary,
105 | danger,
106 | }
107 |
108 | @immutable
109 | class AquaButtonStyleSet {
110 | const AquaButtonStyleSet({
111 | required this.normal,
112 | required this.primary,
113 | required this.secondary,
114 | required this.danger,
115 | });
116 |
117 | final AquaButtonStyle normal;
118 |
119 | final AquaButtonStyle primary;
120 |
121 | final AquaButtonStyle secondary;
122 |
123 | final AquaButtonStyle danger;
124 |
125 | AquaButtonStyle operator [](AquaButtonVariant variant) {
126 | switch (variant) {
127 | case AquaButtonVariant.normal:
128 | return normal;
129 | case AquaButtonVariant.primary:
130 | return primary;
131 | case AquaButtonVariant.secondary:
132 | return secondary;
133 | case AquaButtonVariant.danger:
134 | return danger;
135 | }
136 | }
137 | }
138 |
139 | class AquaButtonStyle {
140 | const AquaButtonStyle({
141 | required this.labelTextStyle,
142 | required this.backgroundColor,
143 | });
144 |
145 | final TextStyle labelTextStyle;
146 |
147 | final Color backgroundColor;
148 | }
149 |
150 | const aquaButtonHeight = 36.0;
151 |
--------------------------------------------------------------------------------
/lib/app/main_route/preferences_tab_page.dart:
--------------------------------------------------------------------------------
1 | import "package:aqua/src/common_widgets/analytics.dart";
2 | import "package:aqua/src/common_widgets/aqua_preferences.dart";
3 | import "package:aqua/src/common_widgets/aqua_scaffold.dart";
4 | import "package:aqua/src/common_widgets/aqua_theme.dart";
5 | import "package:aqua/src/utilities/system_ui_overlay_style.dart";
6 | import "package:flutter/cupertino.dart";
7 |
8 | class PreferencesTabPage extends StatefulWidget {
9 | PreferencesTabPage({Key? key}) : super(key: key);
10 |
11 | @override
12 | State createState() => _PreferencesTabPageState();
13 | }
14 |
15 | class _PreferencesTabPageState extends State {
16 | @override
17 | void initState() {
18 | super.initState();
19 |
20 | WidgetsBinding.instance?.addPostFrameCallback((_) {
21 | Analytics.of(context).logScreenChange(
22 | screenName: "Preferences Screen",
23 | );
24 | });
25 | }
26 |
27 | @override
28 | void didChangeDependencies() {
29 | super.didChangeDependencies();
30 |
31 | final theme = AquaTheme.of(context);
32 |
33 | setSystemUIOverlayStyle(
34 | topColor: theme.scaffoldStyle.backgroundColor,
35 | bottomColor: theme.scaffoldStyle.backgroundColor,
36 | );
37 | }
38 |
39 | @override
40 | Widget build(BuildContext context) {
41 | final theme = AquaTheme.of(context);
42 | final preferences = AquaPreferences.of(context);
43 |
44 | return AquaScadffold(
45 | title: "Preferences",
46 | slivers: [
47 | SliverToBoxAdapter(
48 | child: Padding(
49 | padding: const EdgeInsets.symmetric(horizontal: 16.0),
50 | child: Column(
51 | crossAxisAlignment: CrossAxisAlignment.start,
52 | children: [
53 | Text(
54 | "Calculation",
55 | style: theme.textStyleSet.headline,
56 | ),
57 | SizedBox(height: 16.0),
58 | AnimatedBuilder(
59 | animation: preferences,
60 | builder: (context, _) => Padding(
61 | padding: const EdgeInsets.symmetric(vertical: 8.0),
62 | child: Row(
63 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
64 | crossAxisAlignment: CrossAxisAlignment.start,
65 | children: [
66 | Expanded(
67 | child: Column(
68 | crossAxisAlignment: CrossAxisAlignment.start,
69 | children: [
70 | Text(
71 | "Display in Equity",
72 | style: theme.textStyleSet.body,
73 | ),
74 | SizedBox(height: 8.0),
75 | Text(
76 | preferences.prefersWinRate
77 | ? "Shows both win and tie rate.\n\"Win\" is you're the only player who got the pot. \"Tie\" is when you share the pot with others."
78 | : "Shows how many probability of shares for the pot you have.",
79 | style: theme.textStyleSet.caption,
80 | ),
81 | ],
82 | ),
83 | ),
84 | CupertinoSwitch(
85 | value: !preferences.prefersWinRate,
86 | onChanged: (value) {
87 | preferences.setPreferWinRate(!value);
88 | },
89 | // TODO:
90 | // create a style class for those colors
91 | // probably including whole this preferences widgets
92 | activeColor:
93 | theme.buttonStyleSet.primary.backgroundColor,
94 | trackColor: theme.playingCardStyle.backgroundColor,
95 | ),
96 | ],
97 | ),
98 | ),
99 | ),
100 | ],
101 | ),
102 | ),
103 | ),
104 | ],
105 | );
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/lib/app/app.dart:
--------------------------------------------------------------------------------
1 | import "package:aqua/app/main_route.dart";
2 | import "package:aqua/src/common_widgets/analytics.dart";
3 | import "package:aqua/src/common_widgets/aqua_environment.dart";
4 | import "package:aqua/src/common_widgets/aqua_preferences.dart";
5 | import "package:aqua/src/common_widgets/aqua_theme.dart";
6 | import "package:aqua/src/common_widgets/authentication.dart";
7 | import "package:aqua/src/common_widgets/error_reporter.dart";
8 | import "package:aqua/src/constants/theme.dart";
9 | import "package:aqua/src/services/analytics_service.dart";
10 | import "package:aqua/src/services/auth_manager_service.dart";
11 | import "package:aqua/src/services/error_reporter_service.dart";
12 | import "package:flutter/material.dart";
13 |
14 | class AquaApp extends StatefulWidget {
15 | AquaApp({
16 | Key? key,
17 | this.prepare,
18 | required this.analyticsService,
19 | required this.authManagerService,
20 | required this.errorReporter,
21 | required this.applicationPreferenceData,
22 | }) : super(key: key);
23 |
24 | final AnalyticsService analyticsService;
25 |
26 | final AuthManagerService authManagerService;
27 |
28 | final ErrorReporterService errorReporter;
29 |
30 | final AquaPreferenceData applicationPreferenceData;
31 |
32 | final Future Function()? prepare;
33 |
34 | @override
35 | State createState() => _AquaAppState();
36 | }
37 |
38 | class _AquaAppState extends State {
39 | /// A singleton AquaPreferenceData object that is used in entire aqua app.
40 |
41 | bool _isReady = false;
42 |
43 | @override
44 | void initState() {
45 | super.initState();
46 |
47 | widget.authManagerService.addListener(() {
48 | final user = widget.authManagerService.user;
49 |
50 | widget.analyticsService.setUser(user);
51 | widget.errorReporter.setUser(user);
52 | });
53 |
54 | final user = widget.authManagerService.user;
55 |
56 | widget.analyticsService.setUser(user);
57 | widget.errorReporter.setUser(user);
58 |
59 | if (widget.prepare == null) {
60 | _isReady = true;
61 | }
62 | }
63 |
64 | @override
65 | void didChangeDependencies() {
66 | super.didChangeDependencies();
67 |
68 | if (widget.prepare != null) {
69 | widget.prepare!().then((_) {
70 | setState(() {
71 | _isReady = true;
72 | });
73 | });
74 | }
75 | }
76 |
77 | @override
78 | Widget build(BuildContext context) {
79 | return ErrorReporter(
80 | service: widget.errorReporter,
81 | child: AquaEnvironment(
82 | child: Authentication(
83 | manager: widget.authManagerService,
84 | child: Analytics(
85 | analytics: widget.analyticsService,
86 | child: AquaPreferences(
87 | data: widget.applicationPreferenceData,
88 | child: _isReady
89 | ? WidgetsApp(
90 | title: "Odds Calculator",
91 | color: Color(0xff19232e),
92 | builder: (context, child) => AquaTheme(
93 | data: MediaQuery.of(context).platformBrightness ==
94 | Brightness.dark
95 | ? darkTheme
96 | : lightTheme,
97 | child: child!,
98 | ),
99 | onGenerateRoute: (settings) => MainRoute(),
100 | )
101 | : WidgetsApp(
102 | title: "Odds Calculator",
103 | color: Color(0xff19232e),
104 | builder: (context, child) => AquaTheme(
105 | data: MediaQuery.of(context).platformBrightness ==
106 | Brightness.dark
107 | ? darkTheme
108 | : lightTheme,
109 | child: Container(
110 | color: Color(0xffffffff),
111 | child: Center(
112 | child: CircularProgressIndicator(
113 | valueColor:
114 | AlwaysStoppedAnimation(Color(0xff54a0ff)),
115 | ),
116 | ),
117 | ),
118 | ),
119 | ),
120 | ),
121 | ),
122 | ),
123 | ),
124 | );
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/lib/app/preset_select_route.dart:
--------------------------------------------------------------------------------
1 | import "package:aqua/src/common_widgets/analytics.dart";
2 | import "package:aqua/src/common_widgets/aqua_scaffold.dart";
3 | import "package:aqua/src/common_widgets/aqua_theme.dart";
4 | import "package:aqua/src/common_widgets/readonly_rank_pair_grid.dart";
5 | import "package:aqua/src/constants/hand_range_preset.dart";
6 | import "package:aqua/src/utilities/system_ui_overlay_style.dart";
7 | import "package:aqua/src/view_models/hand_range_draft.dart";
8 | import "package:flutter/cupertino.dart";
9 |
10 | class PresetSelectRoute extends CupertinoPageRoute {
11 | PresetSelectRoute({
12 | RouteSettings? settings,
13 | }) : super(
14 | title: "PresetSelect",
15 | builder: (context) => _PresetSelectPage(),
16 | settings: settings,
17 | );
18 | }
19 |
20 | class _PresetSelectPage extends StatefulWidget {
21 | _PresetSelectPage({Key? key}) : super(key: key);
22 |
23 | @override
24 | State<_PresetSelectPage> createState() => _PresetSelectPageState();
25 | }
26 |
27 | class _PresetSelectPageState extends State<_PresetSelectPage> {
28 | @override
29 | void initState() {
30 | super.initState();
31 |
32 | WidgetsBinding.instance?.addPostFrameCallback((_) {
33 | Analytics.of(context).logScreenChange(
34 | screenName: "Preset Select Screen",
35 | );
36 | });
37 | }
38 |
39 | @override
40 | void didChangeDependencies() {
41 | super.didChangeDependencies();
42 |
43 | final theme = AquaTheme.of(context);
44 |
45 | setSystemUIOverlayStyle(
46 | topColor: theme.scaffoldStyle.backgroundColor,
47 | bottomColor: theme.scaffoldStyle.backgroundColor,
48 | );
49 | }
50 |
51 | @override
52 | Widget build(BuildContext context) {
53 | final theme = AquaTheme.of(context);
54 |
55 | return AquaScadffold(
56 | title: "Presets",
57 | slivers: [
58 | SliverList(
59 | delegate: SliverChildBuilderDelegate(
60 | (context, index) {
61 | if (index % 2 == 0) {
62 | final preset = bundledPresets[index ~/ 2];
63 | final handRangeDraft =
64 | HandRangeDraft.fromHandRange(preset.handRange);
65 |
66 | return GestureDetector(
67 | behavior: HitTestBehavior.opaque,
68 | onTap: () {
69 | Analytics.of(context).logEvent(
70 | name: "Tap a Preset Item",
71 | parameters: {
72 | "Preset Name": preset.name,
73 | "Hand Range Type": handRangeDraft.type.toString(),
74 | "Bundled": true,
75 | },
76 | );
77 |
78 | Navigator.of(context).pop(preset);
79 | },
80 | child: Padding(
81 | padding: const EdgeInsets.symmetric(horizontal: 16.0),
82 | child: Row(
83 | crossAxisAlignment: CrossAxisAlignment.start,
84 | children: [
85 | SizedBox(
86 | width: 64,
87 | height: 64,
88 | child: ReadonlyRankPairGrid(
89 | rankPairs: preset.handRange.onlyRankPairs,
90 | ),
91 | ),
92 | SizedBox(width: 16.0),
93 | Column(
94 | crossAxisAlignment: CrossAxisAlignment.start,
95 | children: [
96 | SizedBox(height: 4),
97 | Text(
98 | preset.name,
99 | style: theme.textStyleSet.body,
100 | ),
101 | SizedBox(height: 4),
102 | Text(
103 | "${(preset.handRange.length / 1326 * 100).floor()}% combs",
104 | style: theme.textStyleSet.caption,
105 | ),
106 | ],
107 | ),
108 | ],
109 | ),
110 | ),
111 | );
112 | }
113 |
114 | return SizedBox(height: 16.0);
115 | },
116 | childCount: bundledPresets.length * 2 - 1,
117 | ),
118 | ),
119 | ],
120 | );
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/lib/src/common_widgets/card_picker.dart:
--------------------------------------------------------------------------------
1 | import "package:aqua/src/common_widgets/playing_card.dart";
2 | import "package:flutter/services.dart";
3 | import "package:flutter/widgets.dart";
4 | import "package:poker/poker.dart";
5 |
6 | class CardPicker extends StatelessWidget {
7 | CardPicker({
8 | CardSet? unavailableCards,
9 | void Function(Card)? onCardTap,
10 | Key? key,
11 | }) : unavailableCards = unavailableCards ?? CardSet.empty,
12 | onCardTap = onCardTap ?? ((_) {}),
13 | super(key: key);
14 |
15 | final CardSet unavailableCards;
16 |
17 | final void Function(Card) onCardTap;
18 |
19 | @override
20 | Widget build(BuildContext context) {
21 | return Column(
22 | mainAxisSize: MainAxisSize.min,
23 | children: List.generate(
24 | _cards.length * 2 - 1,
25 | (i) => i % 2 == 0
26 | ? Row(
27 | children: List.generate(
28 | _cards[i ~/ 2].length * 2 - 1,
29 | (j) => j % 2 == 0
30 | ? Expanded(
31 | child:
32 | unavailableCards.contains(_cards[i ~/ 2][j ~/ 2])
33 | ? Opacity(
34 | opacity: 0.25,
35 | child: PlayingCard(
36 | card: _cards[i ~/ 2][j ~/ 2],
37 | ),
38 | )
39 | : GestureDetector(
40 | onTap: () {
41 | HapticFeedback.selectionClick();
42 |
43 | onCardTap(_cards[i ~/ 2][j ~/ 2]);
44 | },
45 | child: PlayingCard(
46 | card: _cards[i ~/ 2][j ~/ 2],
47 | ),
48 | ),
49 | )
50 | : SizedBox(width: 2),
51 | ),
52 | )
53 | : SizedBox(height: 2),
54 | ),
55 | );
56 | }
57 | }
58 |
59 | final _cards = [
60 | [
61 | Card(rank: Rank.ace, suit: Suit.spade),
62 | Card(rank: Rank.king, suit: Suit.spade),
63 | Card(rank: Rank.queen, suit: Suit.spade),
64 | Card(rank: Rank.jack, suit: Suit.spade),
65 | Card(rank: Rank.ten, suit: Suit.spade),
66 | Card(rank: Rank.nine, suit: Suit.spade),
67 | Card(rank: Rank.eight, suit: Suit.spade),
68 | Card(rank: Rank.seven, suit: Suit.spade),
69 | Card(rank: Rank.six, suit: Suit.spade),
70 | Card(rank: Rank.five, suit: Suit.spade),
71 | Card(rank: Rank.four, suit: Suit.spade),
72 | Card(rank: Rank.trey, suit: Suit.spade),
73 | Card(rank: Rank.deuce, suit: Suit.spade),
74 | ],
75 | [
76 | Card(rank: Rank.ace, suit: Suit.heart),
77 | Card(rank: Rank.king, suit: Suit.heart),
78 | Card(rank: Rank.queen, suit: Suit.heart),
79 | Card(rank: Rank.jack, suit: Suit.heart),
80 | Card(rank: Rank.ten, suit: Suit.heart),
81 | Card(rank: Rank.nine, suit: Suit.heart),
82 | Card(rank: Rank.eight, suit: Suit.heart),
83 | Card(rank: Rank.seven, suit: Suit.heart),
84 | Card(rank: Rank.six, suit: Suit.heart),
85 | Card(rank: Rank.five, suit: Suit.heart),
86 | Card(rank: Rank.four, suit: Suit.heart),
87 | Card(rank: Rank.trey, suit: Suit.heart),
88 | Card(rank: Rank.deuce, suit: Suit.heart),
89 | ],
90 | [
91 | Card(rank: Rank.ace, suit: Suit.diamond),
92 | Card(rank: Rank.king, suit: Suit.diamond),
93 | Card(rank: Rank.queen, suit: Suit.diamond),
94 | Card(rank: Rank.jack, suit: Suit.diamond),
95 | Card(rank: Rank.ten, suit: Suit.diamond),
96 | Card(rank: Rank.nine, suit: Suit.diamond),
97 | Card(rank: Rank.eight, suit: Suit.diamond),
98 | Card(rank: Rank.seven, suit: Suit.diamond),
99 | Card(rank: Rank.six, suit: Suit.diamond),
100 | Card(rank: Rank.five, suit: Suit.diamond),
101 | Card(rank: Rank.four, suit: Suit.diamond),
102 | Card(rank: Rank.trey, suit: Suit.diamond),
103 | Card(rank: Rank.deuce, suit: Suit.diamond),
104 | ],
105 | [
106 | Card(rank: Rank.ace, suit: Suit.club),
107 | Card(rank: Rank.king, suit: Suit.club),
108 | Card(rank: Rank.queen, suit: Suit.club),
109 | Card(rank: Rank.jack, suit: Suit.club),
110 | Card(rank: Rank.ten, suit: Suit.club),
111 | Card(rank: Rank.nine, suit: Suit.club),
112 | Card(rank: Rank.eight, suit: Suit.club),
113 | Card(rank: Rank.seven, suit: Suit.club),
114 | Card(rank: Rank.six, suit: Suit.club),
115 | Card(rank: Rank.five, suit: Suit.club),
116 | Card(rank: Rank.four, suit: Suit.club),
117 | Card(rank: Rank.trey, suit: Suit.club),
118 | Card(rank: Rank.deuce, suit: Suit.club),
119 | ],
120 | ];
121 |
--------------------------------------------------------------------------------
/lib/src/common_widgets/aqua_tab_bar.dart:
--------------------------------------------------------------------------------
1 | import "package:aqua/src/common_widgets/aqua_theme.dart";
2 | import "package:flutter/widgets.dart";
3 |
4 | class AquaTabBar extends StatefulWidget {
5 | AquaTabBar({
6 | Key? key,
7 | required this.items,
8 | this.initialSelectedIndex = 0,
9 | this.activeIndex = 0,
10 | this.onChanged,
11 | }) : super(key: key);
12 |
13 | final List items;
14 |
15 | final int initialSelectedIndex;
16 |
17 | final int activeIndex;
18 |
19 | final void Function(int index)? onChanged;
20 |
21 | @override
22 | _AquaTabBarState createState() => _AquaTabBarState();
23 | }
24 |
25 | class _AquaTabBarState extends State {
26 | late int _selectedIndex;
27 |
28 | @override
29 | void initState() {
30 | super.initState();
31 |
32 | _selectedIndex = widget.activeIndex;
33 | }
34 |
35 | @override
36 | void didUpdateWidget(AquaTabBar oldWidget) {
37 | super.didUpdateWidget(oldWidget);
38 |
39 | if (widget.activeIndex != oldWidget.activeIndex) {
40 | _selectedIndex = widget.activeIndex;
41 | }
42 | }
43 |
44 | @override
45 | Widget build(BuildContext context) {
46 | return DecoratedBox(
47 | decoration: BoxDecoration(
48 | color: AquaTheme.of(context).scaffoldStyle.backgroundColor,
49 | boxShadow: AquaTheme.of(context)
50 | .elevationBoxShadows
51 | .map((boxShadow) => BoxShadow(
52 | color: boxShadow.color,
53 | offset: boxShadow.offset * -1,
54 | blurRadius: boxShadow.blurRadius,
55 | spreadRadius: boxShadow.spreadRadius,
56 | ))
57 | .toList(),
58 | ),
59 | child: SafeArea(
60 | top: false,
61 | bottom: true,
62 | child: LayoutBuilder(builder: (context, constraints) {
63 | return Stack(
64 | children: [
65 | Row(
66 | mainAxisSize: MainAxisSize.max,
67 | children: List.generate(widget.items.length, (index) {
68 | return Expanded(
69 | child: GestureDetector(
70 | behavior: HitTestBehavior.opaque,
71 | onTap: () {
72 | setState(() {
73 | _selectedIndex = index;
74 | });
75 |
76 | if (widget.onChanged != null) {
77 | widget.onChanged!(index);
78 | }
79 | },
80 | child: Padding(
81 | padding: EdgeInsets.only(top: 8, bottom: 4),
82 | child: Column(
83 | children: [
84 | TweenAnimationBuilder(
85 | tween: ColorTween(
86 | begin: AquaTheme.of(context)
87 | .textStyleSet
88 | .caption
89 | .color,
90 | end: _selectedIndex == index
91 | ? AquaTheme.of(context).cursorColor
92 | : AquaTheme.of(context)
93 | .textStyleSet
94 | .caption
95 | .color,
96 | ),
97 | duration: Duration(milliseconds: 200),
98 | builder: (context, color, _) => Icon(
99 | widget.items[index].icon,
100 | size: 24,
101 | color: color,
102 | ),
103 | ),
104 | TweenAnimationBuilder(
105 | tween: TextStyleTween(
106 | begin: AquaTheme.of(context)
107 | .textStyleSet
108 | .caption
109 | .copyWith(fontSize: 10),
110 | end: AquaTheme.of(context)
111 | .textStyleSet
112 | .caption
113 | .copyWith(
114 | color: _selectedIndex == index
115 | ? AquaTheme.of(context).cursorColor
116 | : AquaTheme.of(context)
117 | .textStyleSet
118 | .caption
119 | .color,
120 | fontSize: 10,
121 | ),
122 | ),
123 | duration: Duration(milliseconds: 200),
124 | builder: (context, style, _) => Text(
125 | widget.items[index].label,
126 | style: style,
127 | ),
128 | ),
129 | ],
130 | ),
131 | ),
132 | ),
133 | );
134 | }),
135 | ),
136 | ],
137 | );
138 | }),
139 | ),
140 | );
141 | }
142 | }
143 |
144 | @immutable
145 | class AquaTabBarItem {
146 | const AquaTabBarItem({
147 | required this.label,
148 | required this.icon,
149 | });
150 |
151 | final String label;
152 |
153 | final IconData icon;
154 | }
155 |
--------------------------------------------------------------------------------
/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Amplitude (7.2.0)
3 | - amplitude_flutter (0.0.1):
4 | - Amplitude (= 7.2.0)
5 | - Flutter
6 | - Firebase/Analytics (8.6.0):
7 | - Firebase/Core
8 | - Firebase/Auth (8.6.0):
9 | - Firebase/CoreOnly
10 | - FirebaseAuth (~> 8.6.0)
11 | - Firebase/Core (8.6.0):
12 | - Firebase/CoreOnly
13 | - FirebaseAnalytics (~> 8.6.0)
14 | - Firebase/CoreOnly (8.6.0):
15 | - FirebaseCore (= 8.6.0)
16 | - firebase_analytics (8.3.1):
17 | - Firebase/Analytics (= 8.6.0)
18 | - firebase_core
19 | - Flutter
20 | - firebase_auth (3.1.0):
21 | - Firebase/Auth (= 8.6.0)
22 | - firebase_core
23 | - Flutter
24 | - firebase_core (1.6.0):
25 | - Firebase/CoreOnly (= 8.6.0)
26 | - Flutter
27 | - FirebaseAnalytics (8.6.0):
28 | - FirebaseAnalytics/AdIdSupport (= 8.6.0)
29 | - FirebaseCore (~> 8.0)
30 | - FirebaseInstallations (~> 8.0)
31 | - GoogleUtilities/AppDelegateSwizzler (~> 7.4)
32 | - GoogleUtilities/MethodSwizzler (~> 7.4)
33 | - GoogleUtilities/Network (~> 7.4)
34 | - "GoogleUtilities/NSData+zlib (~> 7.4)"
35 | - nanopb (~> 2.30908.0)
36 | - FirebaseAnalytics/AdIdSupport (8.6.0):
37 | - FirebaseCore (~> 8.0)
38 | - FirebaseInstallations (~> 8.0)
39 | - GoogleAppMeasurement (= 8.6.0)
40 | - GoogleUtilities/AppDelegateSwizzler (~> 7.4)
41 | - GoogleUtilities/MethodSwizzler (~> 7.4)
42 | - GoogleUtilities/Network (~> 7.4)
43 | - "GoogleUtilities/NSData+zlib (~> 7.4)"
44 | - nanopb (~> 2.30908.0)
45 | - FirebaseAuth (8.6.0):
46 | - FirebaseCore (~> 8.0)
47 | - GoogleUtilities/AppDelegateSwizzler (~> 7.4)
48 | - GoogleUtilities/Environment (~> 7.4)
49 | - GTMSessionFetcher/Core (~> 1.5)
50 | - FirebaseCore (8.6.0):
51 | - FirebaseCoreDiagnostics (~> 8.0)
52 | - GoogleUtilities/Environment (~> 7.4)
53 | - GoogleUtilities/Logger (~> 7.4)
54 | - FirebaseCoreDiagnostics (8.6.0):
55 | - GoogleDataTransport (~> 9.0)
56 | - GoogleUtilities/Environment (~> 7.4)
57 | - GoogleUtilities/Logger (~> 7.4)
58 | - nanopb (~> 2.30908.0)
59 | - FirebaseInstallations (8.6.0):
60 | - FirebaseCore (~> 8.0)
61 | - GoogleUtilities/Environment (~> 7.4)
62 | - GoogleUtilities/UserDefaults (~> 7.4)
63 | - PromisesObjC (< 3.0, >= 1.2)
64 | - Flutter (1.0.0)
65 | - GoogleAppMeasurement (8.6.0):
66 | - GoogleAppMeasurement/AdIdSupport (= 8.6.0)
67 | - GoogleUtilities/AppDelegateSwizzler (~> 7.4)
68 | - GoogleUtilities/MethodSwizzler (~> 7.4)
69 | - GoogleUtilities/Network (~> 7.4)
70 | - "GoogleUtilities/NSData+zlib (~> 7.4)"
71 | - nanopb (~> 2.30908.0)
72 | - GoogleAppMeasurement/AdIdSupport (8.6.0):
73 | - GoogleUtilities/AppDelegateSwizzler (~> 7.4)
74 | - GoogleUtilities/MethodSwizzler (~> 7.4)
75 | - GoogleUtilities/Network (~> 7.4)
76 | - "GoogleUtilities/NSData+zlib (~> 7.4)"
77 | - nanopb (~> 2.30908.0)
78 | - GoogleDataTransport (9.1.0):
79 | - GoogleUtilities/Environment (~> 7.2)
80 | - nanopb (~> 2.30908.0)
81 | - PromisesObjC (< 3.0, >= 1.2)
82 | - GoogleUtilities/AppDelegateSwizzler (7.5.2):
83 | - GoogleUtilities/Environment
84 | - GoogleUtilities/Logger
85 | - GoogleUtilities/Network
86 | - GoogleUtilities/Environment (7.5.2):
87 | - PromisesObjC (< 3.0, >= 1.2)
88 | - GoogleUtilities/Logger (7.5.2):
89 | - GoogleUtilities/Environment
90 | - GoogleUtilities/MethodSwizzler (7.5.2):
91 | - GoogleUtilities/Logger
92 | - GoogleUtilities/Network (7.5.2):
93 | - GoogleUtilities/Logger
94 | - "GoogleUtilities/NSData+zlib"
95 | - GoogleUtilities/Reachability
96 | - "GoogleUtilities/NSData+zlib (7.5.2)"
97 | - GoogleUtilities/Reachability (7.5.2):
98 | - GoogleUtilities/Logger
99 | - GoogleUtilities/UserDefaults (7.5.2):
100 | - GoogleUtilities/Logger
101 | - GTMSessionFetcher/Core (1.7.0)
102 | - nanopb (2.30908.0):
103 | - nanopb/decode (= 2.30908.0)
104 | - nanopb/encode (= 2.30908.0)
105 | - nanopb/decode (2.30908.0)
106 | - nanopb/encode (2.30908.0)
107 | - package_info (0.0.1):
108 | - Flutter
109 | - PromisesObjC (2.0.0)
110 | - shared_preferences (0.0.1):
111 | - Flutter
112 |
113 | DEPENDENCIES:
114 | - amplitude_flutter (from `.symlinks/plugins/amplitude_flutter/ios`)
115 | - firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`)
116 | - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`)
117 | - firebase_core (from `.symlinks/plugins/firebase_core/ios`)
118 | - Flutter (from `Flutter`)
119 | - package_info (from `.symlinks/plugins/package_info/ios`)
120 | - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`)
121 |
122 | SPEC REPOS:
123 | trunk:
124 | - Amplitude
125 | - Firebase
126 | - FirebaseAnalytics
127 | - FirebaseAuth
128 | - FirebaseCore
129 | - FirebaseCoreDiagnostics
130 | - FirebaseInstallations
131 | - GoogleAppMeasurement
132 | - GoogleDataTransport
133 | - GoogleUtilities
134 | - GTMSessionFetcher
135 | - nanopb
136 | - PromisesObjC
137 |
138 | EXTERNAL SOURCES:
139 | amplitude_flutter:
140 | :path: ".symlinks/plugins/amplitude_flutter/ios"
141 | firebase_analytics:
142 | :path: ".symlinks/plugins/firebase_analytics/ios"
143 | firebase_auth:
144 | :path: ".symlinks/plugins/firebase_auth/ios"
145 | firebase_core:
146 | :path: ".symlinks/plugins/firebase_core/ios"
147 | Flutter:
148 | :path: Flutter
149 | package_info:
150 | :path: ".symlinks/plugins/package_info/ios"
151 | shared_preferences:
152 | :path: ".symlinks/plugins/shared_preferences/ios"
153 |
154 | SPEC CHECKSUMS:
155 | Amplitude: c948c6f99c7f798c196523b2a5584367401d910f
156 | amplitude_flutter: 5c934cf8331619b62bbd5a69f3cfea9090b3ca94
157 | Firebase: 21ac9f28b09a8bdfc005f34c984fca84e7e8786d
158 | firebase_analytics: de68415415782a5ad1d922d5c51789afbb2bb82d
159 | firebase_auth: 2dfa8e886191c24ddcf4da34463d47b72e6d19dc
160 | firebase_core: c21ac09a8d23afd3594b56ed786bad12e5266bba
161 | FirebaseAnalytics: 8f32ae54ad42754f503354782575c4ddfc1425c3
162 | FirebaseAuth: 223adeeb2262b417532e89bf06a960e3a0a1e9e4
163 | FirebaseCore: 620b677f70f5470a8e59cb77f3ddc666f6f09785
164 | FirebaseCoreDiagnostics: 3721920bde3a9a6d5aa093c1d25e9d3e47f694af
165 | FirebaseInstallations: 0ede6ffcd215b8f93c19d9b06c1c54e2d4107e98
166 | Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
167 | GoogleAppMeasurement: 2c0c6e2a7ab3fe730ade6379f732bdefb46f50b0
168 | GoogleDataTransport: 85fd18ff3019bb85d3f2c551d04c481dedf71fc9
169 | GoogleUtilities: 8de2a97a17e15b6b98e38e8770e2d129a57c0040
170 | GTMSessionFetcher: 43748f93435c2aa068b1cbe39655aaf600652e91
171 | nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96
172 | package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62
173 | PromisesObjC: 68159ce6952d93e17b2dfe273b8c40907db5ba58
174 | shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d
175 |
176 | PODFILE CHECKSUM: d73294e4f74b04b437987fd6964a7b06dcc2d509
177 |
178 | COCOAPODS: 1.10.2
179 |
--------------------------------------------------------------------------------
/lib/src/common_widgets/digits_text.dart:
--------------------------------------------------------------------------------
1 | import "package:aqua/src/common_widgets/aqua_theme.dart";
2 | import "package:flutter/widgets.dart";
3 |
4 | class DigitsText extends StatelessWidget {
5 | DigitsText(
6 | this.value, {
7 | this.style,
8 | this.textStyle,
9 | this.useLargeWholeNumberPart = true,
10 | this.suffix = "%",
11 | this.fractionDigits = 2,
12 | this.showAlmostEqualPrefix = false,
13 | });
14 |
15 | final double value;
16 |
17 | final AquaDigitTextStyle? style;
18 |
19 | final TextStyle? textStyle;
20 |
21 | final bool useLargeWholeNumberPart;
22 |
23 | final String suffix;
24 |
25 | final int fractionDigits;
26 |
27 | final bool showAlmostEqualPrefix;
28 |
29 | @override
30 | Widget build(BuildContext context) {
31 | final theme = AquaTheme.of(context);
32 | final textStyle = this.textStyle ?? DefaultTextStyle.of(context).style;
33 | final style = this.style ?? theme.digitTextStyle;
34 |
35 | return RichText(
36 | text: TextSpan(
37 | children: [
38 | if (fractionDigits == 0 &&
39 | showAlmostEqualPrefix &&
40 | value % 1 != 0) ...[
41 | TextSpan(
42 | text: "≈ ",
43 | style: textStyle
44 | .copyWith(
45 | color: this.textStyle?.color ?? style.color,
46 | fontFamily: this.textStyle?.fontFamily ?? style.fontFamily,
47 | fontWeight: this.textStyle?.fontWeight ?? style.fontWeight,
48 | )
49 | .apply(fontSizeFactor: style.fontSizeFactor),
50 | ),
51 | ],
52 | TextSpan(
53 | text: "${(value * 100).toInt()}",
54 | style: textStyle
55 | .copyWith(
56 | color: this.textStyle?.color ?? style.color,
57 | fontFamily: this.textStyle?.fontFamily ?? style.fontFamily,
58 | fontWeight: this.textStyle?.fontWeight ?? style.fontWeight,
59 | )
60 | .apply(
61 | fontSizeFactor: useLargeWholeNumberPart
62 | ? style.largeFontSizeFactor
63 | : style.fontSizeFactor,
64 | ),
65 | ),
66 | if (fractionDigits > 0) ...[
67 | TextSpan(
68 | text: ".",
69 | style: textStyle
70 | .copyWith(
71 | color: this.textStyle?.color ?? style.color,
72 | fontFamily: this.textStyle?.fontFamily ?? style.fontFamily,
73 | fontWeight: this.textStyle?.fontWeight ?? style.fontWeight,
74 | )
75 | .apply(fontSizeFactor: style.fontSizeFactor),
76 | ),
77 | TextSpan(
78 | text:
79 | "${extractDecimalPartOf(value, fractionDigits: fractionDigits)}"
80 | .padLeft(fractionDigits, "0"),
81 | style: textStyle
82 | .copyWith(
83 | color: this.textStyle?.color ?? style.color,
84 | fontFamily: this.textStyle?.fontFamily ?? style.fontFamily,
85 | fontWeight: this.textStyle?.fontWeight ?? style.fontWeight,
86 | )
87 | .apply(fontSizeFactor: style.fontSizeFactor),
88 | ),
89 | ],
90 | TextSpan(
91 | text: suffix,
92 | style: textStyle
93 | .copyWith(
94 | color: this.textStyle?.color ?? style.color,
95 | fontFamily: this.textStyle?.fontFamily ?? style.fontFamily,
96 | )
97 | .apply(fontSizeFactor: style.fontSizeFactor),
98 | ),
99 | ],
100 | ),
101 | );
102 | }
103 | }
104 |
105 | int extractDecimalPartOf(double value, {required int fractionDigits}) {
106 | var onlyDecimalPart = value * 100 % 1;
107 | int asWholeNumber = 0;
108 |
109 | for (int d = 0; d < fractionDigits; ++d) {
110 | onlyDecimalPart = onlyDecimalPart * 10;
111 | asWholeNumber = asWholeNumber * 10 + onlyDecimalPart.toInt();
112 |
113 | onlyDecimalPart = onlyDecimalPart % 1;
114 | }
115 |
116 | return asWholeNumber;
117 | }
118 |
119 | class DigitsPlaceholderText extends StatelessWidget {
120 | DigitsPlaceholderText({
121 | this.style,
122 | this.textStyle,
123 | this.useLargeWholeNumberPart = true,
124 | this.suffix = "%",
125 | this.fractionDigits = 2,
126 | });
127 |
128 | final AquaDigitTextStyle? style;
129 |
130 | final TextStyle? textStyle;
131 |
132 | final bool useLargeWholeNumberPart;
133 |
134 | final String suffix;
135 |
136 | final int fractionDigits;
137 |
138 | @override
139 | Widget build(BuildContext context) {
140 | final theme = AquaTheme.of(context);
141 | final textStyle = this.textStyle ?? DefaultTextStyle.of(context).style;
142 | final style = this.style ?? theme.digitTextStyle;
143 |
144 | return RichText(
145 | text: TextSpan(
146 | children: [
147 | TextSpan(
148 | text: "??",
149 | style: textStyle
150 | .copyWith(
151 | color: this.textStyle?.color ?? style.placeholderColor,
152 | fontFamily: this.textStyle?.fontFamily ?? style.fontFamily,
153 | fontWeight: this.textStyle?.fontWeight ?? style.fontWeight,
154 | )
155 | .apply(
156 | fontSizeFactor: useLargeWholeNumberPart
157 | ? style.largeFontSizeFactor
158 | : style.fontSizeFactor,
159 | ),
160 | ),
161 | if (fractionDigits > 0) ...[
162 | TextSpan(
163 | text: ".",
164 | style: textStyle
165 | .copyWith(
166 | color: this.textStyle?.color ?? style.placeholderColor,
167 | fontFamily: this.textStyle?.fontFamily ?? style.fontFamily,
168 | fontWeight: this.textStyle?.fontWeight ?? style.fontWeight,
169 | )
170 | .apply(fontSizeFactor: style.fontSizeFactor),
171 | ),
172 | TextSpan(
173 | text: "".padLeft(fractionDigits, "?"),
174 | style: textStyle
175 | .copyWith(
176 | color: this.textStyle?.color ?? style.placeholderColor,
177 | fontFamily: this.textStyle?.fontFamily ?? style.fontFamily,
178 | fontWeight: this.textStyle?.fontWeight ?? style.fontWeight,
179 | )
180 | .apply(fontSizeFactor: style.fontSizeFactor),
181 | ),
182 | ],
183 | TextSpan(
184 | text: suffix,
185 | style: textStyle
186 | .copyWith(
187 | color: this.textStyle?.color ?? style.placeholderColor,
188 | fontFamily: this.textStyle?.fontFamily ?? style.fontFamily,
189 | )
190 | .apply(fontSizeFactor: style.fontSizeFactor),
191 | ),
192 | ],
193 | ),
194 | );
195 | }
196 | }
197 |
198 | class AquaDigitTextStyle {
199 | AquaDigitTextStyle({
200 | required this.color,
201 | required this.placeholderColor,
202 | required this.fontFamily,
203 | required this.fontWeight,
204 | required this.fontSizeFactor,
205 | required this.largeFontSizeFactor,
206 | });
207 |
208 | final Color color;
209 |
210 | final Color placeholderColor;
211 |
212 | final String fontFamily;
213 |
214 | final FontWeight fontWeight;
215 |
216 | final double fontSizeFactor;
217 |
218 | final double largeFontSizeFactor;
219 | }
220 |
--------------------------------------------------------------------------------
/lib/src/constants/theme.dart:
--------------------------------------------------------------------------------
1 | import "package:aqua/src/common_widgets/aqua_button.dart";
2 | import "package:aqua/src/common_widgets/aqua_scaffold.dart";
3 | import "package:aqua/src/common_widgets/aqua_theme.dart";
4 | import "package:aqua/src/common_widgets/digits_text.dart";
5 | import "package:flutter/painting.dart";
6 | import "package:poker/poker.dart";
7 |
8 | const _white = Color(0xffffffff);
9 | const _lessWhite = Color(0xfff2f2f7);
10 | const _silverSand = Color(0xffC5C9D0);
11 | const _black = Color(0xff000000);
12 | const _lessBlack = Color(0xff1c1c1e);
13 | const _davysGrey = Color(0xff5A5E63);
14 | const _gunmetal = Color(0xff29333D);
15 | const _aliceBlue = Color(0xffE8ECF1);
16 | const _cadetGrey = Color(0xff98A0A8);
17 | const _lemonPeel = Color(0xffF5B83D);
18 | const _chineseYellow = Color(0xffFFB41C);
19 | const _pullmanBrown = Color(0xff503D14);
20 | const _bananaMania = Color(0xffFBE8BF);
21 | const _charcoal = Color(0xff304050);
22 | const _frenchBlue = Color(0xff368CE2);
23 | const _beauBlue = Color(0xffBDD9F5);
24 | const _prussianBlue = Color(0xff122E4A);
25 | const _candyPink = Color(0xffE96363);
26 | // ignore: unused_element
27 | const _spanishPink = Color(0xfff5bcbc);
28 | // ignore: unused_element
29 | const _darkSienna = Color(0xff4C2020);
30 | const _jungleGreen = Color(0xff19B38C);
31 | // ignore: unused_element
32 | const _lightCyan = Color(0xffC6ECE2);
33 | // ignore: unused_element
34 | const _phthaloGreen = Color(0xff062D23);
35 |
36 | const baseTextStyle = TextStyle(
37 | fontFamily: "Poppins",
38 | fontSize: 16,
39 | fontWeight: FontWeight.w400,
40 | );
41 |
42 | final baseButtonTextStyle = baseTextStyle.copyWith(
43 | fontSize: 14,
44 | fontFamily: "Poppins",
45 | fontWeight: FontWeight.w500,
46 | );
47 |
48 | final lightTheme = AquaThemeData(
49 | scaffoldStyle: AquaScaffoldStyle(
50 | titleTextStyle: baseTextStyle.copyWith(
51 | color: _gunmetal,
52 | fontSize: 32,
53 | fontWeight: FontWeight.w600,
54 | ),
55 | backgroundColor: _white,
56 | ),
57 | buttonStyleSet: AquaButtonStyleSet(
58 | normal: AquaButtonStyle(
59 | labelTextStyle: baseButtonTextStyle.copyWith(color: _white),
60 | backgroundColor: _gunmetal,
61 | ),
62 | primary: AquaButtonStyle(
63 | labelTextStyle: baseButtonTextStyle.copyWith(color: _white),
64 | backgroundColor: _frenchBlue,
65 | ),
66 | secondary: AquaButtonStyle(
67 | labelTextStyle: baseButtonTextStyle.copyWith(color: _frenchBlue),
68 | backgroundColor: _beauBlue,
69 | ),
70 | danger: AquaButtonStyle(
71 | labelTextStyle: baseButtonTextStyle.copyWith(color: _white),
72 | backgroundColor: _candyPink,
73 | ),
74 | ),
75 | textStyleSet: AquaTextStyleSet(
76 | headline: baseTextStyle.copyWith(
77 | color: _gunmetal,
78 | fontWeight: FontWeight.w600,
79 | fontSize: 20,
80 | ),
81 | body: baseTextStyle.copyWith(color: _gunmetal),
82 | caption: baseTextStyle.copyWith(
83 | color: _cadetGrey,
84 | fontSize: 13.0,
85 | ),
86 | errorCaption: baseTextStyle.copyWith(
87 | color: _candyPink,
88 | fontSize: 13.0,
89 | ),
90 | ),
91 | digitTextStyle: AquaDigitTextStyle(
92 | color: _gunmetal,
93 | placeholderColor: _silverSand,
94 | fontFamily: "Poppins",
95 | fontWeight: FontWeight.w700,
96 | fontSizeFactor: 1.15,
97 | largeFontSizeFactor: 2.3,
98 | ),
99 | playingCardStyle: AquaPlayingCardStyle(
100 | textStyle: TextStyle(
101 | fontFamily: "Work Sans",
102 | fontWeight: FontWeight.w600,
103 | ),
104 | backgroundColor: _lessWhite,
105 | suitColors: {
106 | Suit.spade: _charcoal,
107 | Suit.heart: _candyPink,
108 | Suit.diamond: _frenchBlue,
109 | Suit.club: _jungleGreen,
110 | },
111 | ),
112 | rankPairGridStyle: AquaRankPairGridStyle(
113 | backgroundColor: _lessWhite,
114 | textStyle: baseTextStyle.copyWith(
115 | color: _silverSand,
116 | fontFamily: "Work Sans",
117 | ),
118 | selectedBackgroundColor: _bananaMania,
119 | selectedForegroundColor: _lemonPeel,
120 | ),
121 | sliderStyle: AquaSliderStyle(
122 | thumbColor: _gunmetal,
123 | activeTrackColor: _gunmetal,
124 | inactiveTrackColor: _silverSand,
125 | valueIndicatorColor: _gunmetal,
126 | valueIndicatorTextStyle: baseButtonTextStyle.copyWith(color: _white),
127 | ),
128 | cursorColor: _lemonPeel,
129 | elevationBoxShadows: [
130 | BoxShadow(
131 | color: Color(0x1f000000),
132 | offset: Offset(0, 0),
133 | blurRadius: 12,
134 | ),
135 | BoxShadow(
136 | color: Color(0x0f000000),
137 | offset: Offset(0, 12.0),
138 | blurRadius: 24,
139 | ),
140 | ],
141 | );
142 |
143 | final darkTheme = AquaThemeData(
144 | scaffoldStyle: AquaScaffoldStyle(
145 | titleTextStyle: baseTextStyle.copyWith(
146 | color: _white,
147 | fontSize: 32,
148 | fontWeight: FontWeight.w600,
149 | ),
150 | backgroundColor: _black,
151 | ),
152 | buttonStyleSet: AquaButtonStyleSet(
153 | normal: AquaButtonStyle(
154 | labelTextStyle: baseButtonTextStyle.copyWith(color: _black),
155 | backgroundColor: _aliceBlue,
156 | ),
157 | primary: AquaButtonStyle(
158 | labelTextStyle: baseButtonTextStyle.copyWith(color: _white),
159 | backgroundColor: _frenchBlue,
160 | ),
161 | secondary: AquaButtonStyle(
162 | labelTextStyle: baseButtonTextStyle.copyWith(color: _frenchBlue),
163 | backgroundColor: _prussianBlue,
164 | ),
165 | danger: AquaButtonStyle(
166 | labelTextStyle: baseButtonTextStyle.copyWith(color: _white),
167 | backgroundColor: _candyPink,
168 | ),
169 | ),
170 | textStyleSet: AquaTextStyleSet(
171 | headline: baseTextStyle.copyWith(
172 | color: _white,
173 | fontWeight: FontWeight.w600,
174 | fontSize: 20,
175 | ),
176 | body: baseTextStyle.copyWith(color: _white),
177 | caption: baseTextStyle.copyWith(
178 | color: _cadetGrey,
179 | fontSize: 13.0,
180 | ),
181 | errorCaption: baseTextStyle.copyWith(
182 | color: _candyPink,
183 | fontSize: 13.0,
184 | ),
185 | ),
186 | playingCardStyle: AquaPlayingCardStyle(
187 | textStyle: TextStyle(
188 | fontFamily: "Work Sans",
189 | fontWeight: FontWeight.w600,
190 | ),
191 | backgroundColor: _lessBlack,
192 | suitColors: {
193 | Suit.spade: _aliceBlue,
194 | Suit.heart: _candyPink,
195 | Suit.diamond: _frenchBlue,
196 | Suit.club: _jungleGreen,
197 | },
198 | ),
199 | rankPairGridStyle: AquaRankPairGridStyle(
200 | backgroundColor: _lessBlack,
201 | textStyle: baseTextStyle.copyWith(
202 | color: _davysGrey,
203 | fontFamily: "Work Sans",
204 | ),
205 | selectedBackgroundColor: _pullmanBrown,
206 | selectedForegroundColor: _chineseYellow,
207 | ),
208 | digitTextStyle: AquaDigitTextStyle(
209 | color: _white,
210 | placeholderColor: _davysGrey,
211 | fontFamily: "Poppins",
212 | fontWeight: FontWeight.w700,
213 | fontSizeFactor: 1.15,
214 | largeFontSizeFactor: 2.3,
215 | ),
216 | sliderStyle: AquaSliderStyle(
217 | thumbColor: _aliceBlue,
218 | activeTrackColor: _aliceBlue,
219 | inactiveTrackColor: _davysGrey,
220 | valueIndicatorColor: _aliceBlue,
221 | valueIndicatorTextStyle: baseButtonTextStyle.copyWith(color: _black),
222 | ),
223 | cursorColor: _chineseYellow,
224 | elevationBoxShadows: [
225 | BoxShadow(
226 | color: Color(0x1f000000),
227 | offset: Offset(0, 0),
228 | blurRadius: 12,
229 | ),
230 | BoxShadow(
231 | color: Color(0x0f000000),
232 | offset: Offset(0, 12.0),
233 | blurRadius: 24,
234 | ),
235 | ],
236 | );
237 |
--------------------------------------------------------------------------------
/lib/src/common_widgets/rank_pair_select_grid.dart:
--------------------------------------------------------------------------------
1 | import "package:aqua/src/common_widgets/aqua_theme.dart";
2 | import "package:aqua/src/common_widgets/fill.dart";
3 | import "package:aqua/src/constants/card.dart";
4 | import "package:flutter/material.dart";
5 | import "package:flutter/services.dart";
6 | import "package:flutter/widgets.dart";
7 | import "package:poker/poker.dart";
8 |
9 | class RankPairSelectGrid extends StatefulWidget {
10 | RankPairSelectGrid({
11 | Key? key,
12 | required this.onChanged,
13 | this.onChangeStart,
14 | this.onChangeEnd,
15 | this.value = const {},
16 | }) : super(key: key);
17 |
18 | final void Function(Set rankPairs) onChanged;
19 |
20 | final void Function(RankPair part, bool isToMark)? onChangeStart;
21 |
22 | final void Function(RankPair part, bool wasToMark)? onChangeEnd;
23 |
24 | final Set value;
25 |
26 | @override
27 | State createState() => _RankPairSelectGridState();
28 | }
29 |
30 | class _RankPairSelectGridState extends State {
31 | late Set selectedRange;
32 |
33 | bool isToMark = false;
34 |
35 | RankPair? lastChangedPart;
36 |
37 | @override
38 | void initState() {
39 | super.initState();
40 |
41 | selectedRange = {...widget.value};
42 | }
43 |
44 | @override
45 | void didUpdateWidget(RankPairSelectGrid oldWidget) {
46 | super.didUpdateWidget(oldWidget);
47 |
48 | if (oldWidget.value != widget.value) {
49 | setState(() {
50 | selectedRange = widget.value;
51 | });
52 | }
53 | }
54 |
55 | @override
56 | Widget build(BuildContext context) => Fill(
57 | child: AspectRatio(
58 | aspectRatio: 1,
59 | child: LayoutBuilder(
60 | builder: (context, constraints) => GestureDetector(
61 | onPanStart: (details) {
62 | final x = details.localPosition.dx *
63 | Rank.values.length ~/
64 | constraints.maxWidth;
65 | final y = details.localPosition.dy *
66 | Rank.values.length ~/
67 | constraints.maxHeight;
68 | final rankPairsPart = x > y
69 | ? RankPair.suited(
70 | high: ranksInStrongnessOrder[y],
71 | kicker: ranksInStrongnessOrder[x],
72 | )
73 | : RankPair.ofsuit(
74 | high: ranksInStrongnessOrder[x],
75 | kicker: ranksInStrongnessOrder[y],
76 | );
77 |
78 | isToMark = !selectedRange.contains(rankPairsPart);
79 |
80 | if (isToMark) {
81 | HapticFeedback.lightImpact();
82 |
83 | setState(() {
84 | selectedRange.add(rankPairsPart);
85 | lastChangedPart = rankPairsPart;
86 | });
87 |
88 | if (widget.onChangeStart != null) {
89 | widget.onChangeStart!(rankPairsPart, true);
90 | }
91 |
92 | widget.onChanged(selectedRange);
93 | } else {
94 | HapticFeedback.lightImpact();
95 |
96 | setState(() {
97 | selectedRange.remove(rankPairsPart);
98 | lastChangedPart = rankPairsPart;
99 | });
100 |
101 | if (widget.onChangeStart != null) {
102 | widget.onChangeStart!(rankPairsPart, false);
103 | }
104 |
105 | widget.onChanged(selectedRange);
106 | }
107 | },
108 | onPanUpdate: (details) {
109 | final x = details.localPosition.dx *
110 | Rank.values.length ~/
111 | constraints.maxWidth;
112 | final y = details.localPosition.dy *
113 | Rank.values.length ~/
114 | constraints.maxHeight;
115 |
116 | if (x < 0 || x >= Rank.values.length) return;
117 | if (y < 0 || y >= Rank.values.length) return;
118 |
119 | final rankPairsPart = x > y
120 | ? RankPair.suited(
121 | high: ranksInStrongnessOrder[y],
122 | kicker: ranksInStrongnessOrder[x],
123 | )
124 | : RankPair.ofsuit(
125 | high: ranksInStrongnessOrder[x],
126 | kicker: ranksInStrongnessOrder[y],
127 | );
128 |
129 | if (isToMark) {
130 | if (selectedRange.contains(rankPairsPart)) return;
131 |
132 | HapticFeedback.selectionClick();
133 |
134 | setState(() {
135 | selectedRange.add(rankPairsPart);
136 | lastChangedPart = rankPairsPart;
137 | });
138 |
139 | widget.onChanged(selectedRange);
140 | } else {
141 | if (!selectedRange.contains(rankPairsPart)) return;
142 |
143 | HapticFeedback.selectionClick();
144 |
145 | setState(() {
146 | selectedRange.remove(rankPairsPart);
147 | lastChangedPart = rankPairsPart;
148 | });
149 |
150 | widget.onChanged(selectedRange);
151 | }
152 | },
153 | onPanEnd: (details) {
154 | if (widget.onChangeEnd != null) {
155 | widget.onChangeEnd!(lastChangedPart!, isToMark);
156 | }
157 | },
158 | behavior: HitTestBehavior.opaque,
159 | child: Column(
160 | children: List.generate(Rank.values.length * 2 - 1, (i) {
161 | if (i % 2 == 1) return SizedBox(height: 2);
162 |
163 | final y = i ~/ 2;
164 |
165 | return Expanded(
166 | child: Row(
167 | children: List.generate(Rank.values.length * 2 - 1, (j) {
168 | if (j % 2 == 1) return SizedBox(width: 2);
169 |
170 | final x = j ~/ 2;
171 | final rankPairsPart = x > y
172 | ? RankPair.suited(
173 | high: ranksInStrongnessOrder[y],
174 | kicker: ranksInStrongnessOrder[x],
175 | )
176 | : RankPair.ofsuit(
177 | high: ranksInStrongnessOrder[x],
178 | kicker: ranksInStrongnessOrder[y],
179 | );
180 |
181 | return Expanded(
182 | child: RankPairSelectGridItem(
183 | rankPairsPart: rankPairsPart,
184 | isSelected: selectedRange.contains(rankPairsPart),
185 | ),
186 | );
187 | }),
188 | ),
189 | );
190 | }),
191 | ),
192 | ),
193 | ),
194 | ),
195 | );
196 | }
197 |
198 | class RankPairSelectGridItem extends StatelessWidget {
199 | RankPairSelectGridItem({
200 | required this.rankPairsPart,
201 | this.isSelected = false,
202 | Key? key,
203 | }) : super(key: key);
204 |
205 | final RankPair rankPairsPart;
206 |
207 | final bool isSelected;
208 |
209 | @override
210 | Widget build(BuildContext context) {
211 | final style = AquaTheme.of(context).rankPairGridStyle;
212 |
213 | return LayoutBuilder(
214 | builder: (context, constraints) => DecoratedBox(
215 | decoration: BoxDecoration(
216 | color: isSelected
217 | ? style.selectedBackgroundColor
218 | : style.backgroundColor,
219 | borderRadius: BorderRadius.circular(3),
220 | ),
221 | child: Center(
222 | child: Text(
223 | "${rankChars[rankPairsPart.high]}${rankChars[rankPairsPart.kicker]}",
224 | style: style.textStyle.copyWith(
225 | color: isSelected
226 | ? style.selectedForegroundColor
227 | : style.textStyle.color,
228 | fontSize: constraints.maxWidth / 2,
229 | ),
230 | ),
231 | ),
232 | ),
233 | );
234 | }
235 | }
236 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: aqua
2 | description: A new Flutter project.
3 |
4 | # The following line prevents the package from being accidentally published to
5 | # pub.dev using `pub publish`. This is preferred for private packages.
6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev
7 |
8 | # The following defines the version and build number for your application.
9 | # A version number is three numbers separated by dots, like 1.2.43
10 | # followed by an optional build number separated by a +.
11 | # Both the version and the builder number may be overridden in flutter
12 | # build by specifying --build-name and --build-number, respectively.
13 | # In Android, build-name is used as versionName while build-number used as versionCode.
14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
16 | # Read more about iOS versioning at
17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
18 | version: 3.0.0+31
19 |
20 | environment:
21 | sdk: ">=2.12.0 <3.0.0"
22 |
23 | dependencies:
24 | amplitude_flutter: ^3.3.0
25 | flutter:
26 | sdk: flutter
27 | firebase_analytics: ^8.3.1
28 | firebase_auth: ^3.1.0
29 | firebase_core: ^1.6.0
30 | flutter_launcher_icons: ^0.9.2
31 | package_info: ^2.0.2
32 | poker: 0.2.5
33 | sentry: ^6.0.0
34 | shared_preferences: ^2.0.7
35 |
36 | dev_dependencies:
37 | flutter_test:
38 | sdk: flutter
39 | test: ^1.16.8
40 |
41 | # For information on the generic Dart part of this file, see the
42 | # following page: https://dart.dev/tools/pub/pubspec
43 |
44 | # The following section is specific to Flutter.
45 | flutter:
46 |
47 | # The following line ensures that the Material Icons font is
48 | # included with your application, so that you can use the icons in
49 | # the material Icons class.
50 | uses-material-design: true
51 |
52 | # To add assets to your application, add an assets section, like this:
53 | # assets:
54 | # - images/a_dot_burr.jpeg
55 | # - images/a_dot_ham.jpeg
56 |
57 | # An image asset can refer to one or more resolution-specific "variants", see
58 | # https://flutter.dev/assets-and-images/#resolution-aware.
59 |
60 | # For details regarding adding assets from package dependencies, see
61 | # https://flutter.dev/assets-and-images/#from-packages
62 |
63 | # To add custom fonts to your application, add a fonts section here,
64 | # in this "flutter" section. Each entry in this list should have a
65 | # "family" key with the font family name, and a "fonts" key with a
66 | # list giving the asset and other descriptors for the font. For
67 | # example:
68 | # fonts:
69 | # - family: Schyler
70 | # fonts:
71 | # - asset: fonts/Schyler-Regular.ttf
72 | # - asset: fonts/Schyler-Italic.ttf
73 | # style: italic
74 | # - family: Trajan Pro
75 | # fonts:
76 | # - asset: fonts/TrajanPro.ttf
77 | # - asset: fonts/TrajanPro_Bold.ttf
78 | # weight: 700
79 | #
80 | # For details regarding fonts from package dependencies,
81 | # see https://flutter.dev/custom-fonts/#from-packages
82 | fonts:
83 | - family: AquaIcons
84 | fonts:
85 | - asset: assets/fonts/AquaIcons.ttf
86 | - family: Icons
87 | fonts:
88 | - asset: assets/fonts/Icons.ttf
89 | - family: Work Sans
90 | fonts:
91 | # - asset: assets/fonts/WorkSans-Thin.ttf
92 | # weight: 100
93 | # - asset: assets/fonts/WorkSans-ThinItalic.ttf
94 | # weight: 100
95 | # style: italic
96 | # - asset: assets/fonts/WorkSans-ExtraLight.ttf
97 | # weight: 200
98 | # - asset: assets/fonts/WorkSans-ExtraLightItalic.ttf
99 | # weight: 200
100 | # style: italic
101 | # - asset: assets/fonts/WorkSans-Light.ttf
102 | # weight: 300
103 | # - asset: assets/fonts/WorkSans-LightItalic.ttf
104 | # weight: 300
105 | # style: italic
106 | - asset: assets/fonts/WorkSans-Regular.ttf
107 | weight: 400
108 | # - asset: assets/fonts/WorkSans-Italic.ttf
109 | # weight: 400
110 | # style: italic
111 | # - asset: assets/fonts/WorkSans-Medium.ttf
112 | # weight: 500
113 | # - asset: assets/fonts/WorkSans-MediumItalic.ttf
114 | # weight: 500
115 | # style: italic
116 | - asset: assets/fonts/WorkSans-SemiBold.ttf
117 | weight: 600
118 | # - asset: assets/fonts/WorkSans-SemiBoldItalic.ttf
119 | # weight: 600
120 | # style: italic
121 | # - asset: assets/fonts/WorkSans-Bold.ttf
122 | # weight: 700
123 | # - asset: assets/fonts/WorkSans-BoldItalic.ttf
124 | # weight: 700
125 | # style: italic
126 | # - asset: assets/fonts/WorkSans-ExtraBold.ttf
127 | # weight: 800
128 | # - asset: assets/fonts/WorkSans-ExtraBoldItalic.ttf
129 | # weight: 800
130 | # style: italic
131 | # - asset: assets/fonts/WorkSans-Black.ttf
132 | # weight: 900
133 | # - asset: assets/fonts/WorkSans-BlackItalic.ttf
134 | # weight: 900
135 | # style: italic
136 | # - family: Source Code Pro
137 | # fonts:
138 | # - asset: assets/fonts/SourceCodePro-ExtraLight.ttf
139 | # weight: 200
140 | # - asset: assets/fonts/SourceCodePro-ExtraLightItalic.ttf
141 | # weight: 200
142 | # style: italic
143 | # - asset: assets/fonts/SourceCodePro-Light.ttf
144 | # weight: 300
145 | # - asset: assets/fonts/SourceCodePro-LightItalic.ttf
146 | # weight: 300
147 | # style: italic
148 | # - asset: assets/fonts/SourceCodePro-Regular.ttf
149 | # weight: 400
150 | # - asset: assets/fonts/SourceCodePro-Italic.ttf
151 | # weight: 400
152 | # style: italic
153 | # - asset: assets/fonts/SourceCodePro-Medium.ttf
154 | # weight: 500
155 | # - asset: assets/fonts/SourceCodePro-MediumItalic.ttf
156 | # weight: 500
157 | # style: italic
158 | # - asset: assets/fonts/SourceCodePro-SemiBold.ttf
159 | # weight: 600
160 | # - asset: assets/fonts/SourceCodePro-SemiBoldItalic.ttf
161 | # weight: 600
162 | # style: italic
163 | # - asset: assets/fonts/SourceCodePro-Bold.ttf
164 | # weight: 700
165 | # - asset: assets/fonts/SourceCodePro-BoldItalic.ttf
166 | # weight: 700
167 | # style: italic
168 | # - asset: assets/fonts/SourceCodePro-BlackItalic.ttf
169 | # weight: 900
170 | # style: italic
171 | # - asset: assets/fonts/SourceCodePro-Black.ttf
172 | # weight: 900
173 | - family: Poppins
174 | fonts:
175 | # - asset: assets/fonts/Poppins-Thin.ttf
176 | # weight: 100
177 | # - asset: assets/fonts/Poppins-ThinItalic.ttf
178 | # weight: 100
179 | # style: italic
180 | # - asset: assets/fonts/Poppins-ExtraLight.ttf
181 | # weight: 200
182 | # - asset: assets/fonts/Poppins-ExtraLightItalic.ttf
183 | # weight: 200
184 | # style: italic
185 | # - asset: assets/fonts/Poppins-Light.ttf
186 | # weight: 300
187 | # - asset: assets/fonts/Poppins-LightItalic.ttf
188 | # weight: 300
189 | # style: italic
190 | - asset: assets/fonts/Poppins-Regular.ttf
191 | weight: 400
192 | # - asset: assets/fonts/Poppins-Italic.ttf
193 | # weight: 400
194 | # style: italic
195 | - asset: assets/fonts/Poppins-Medium.ttf
196 | weight: 500
197 | # - asset: assets/fonts/Poppins-MediumItalic.ttf
198 | # weight: 500
199 | # style: italic
200 | - asset: assets/fonts/Poppins-SemiBold.ttf
201 | weight: 600
202 | # - asset: assets/fonts/Poppins-SemiBoldItalic.ttf
203 | # weight: 600
204 | # style: italic
205 | - asset: assets/fonts/Poppins-Bold.ttf
206 | weight: 700
207 | # - asset: assets/fonts/Poppins-BoldItalic.ttf
208 | # weight: 700
209 | # style: italic
210 | # - asset: assets/fonts/Poppins-ExtraBold.ttf
211 | # weight: 800
212 | # - asset: assets/fonts/Poppins-ExtraBoldItalic.ttf
213 | # weight: 800
214 | # style: italic
215 | # - asset: assets/fonts/Poppins-Black.ttf
216 | # weight: 900
217 | # - asset: assets/fonts/Poppins-BlackItalic.ttf
218 | # weight: 900
219 | # style: italic
220 |
221 | flutter_icons:
222 | android: true
223 | ios: true
224 | image_path: "assets/launcher-icon/icon@1024px.png"
225 | adaptive_icon_background: "assets/launcher-icon/adaptive-icon-background@432px.png"
226 | adaptive_icon_foreground: "assets/launcher-icon/adaptive-icon-foreground@432px.png"
227 |
--------------------------------------------------------------------------------
/lib/src/constants/hand_range.dart:
--------------------------------------------------------------------------------
1 | import "package:poker/poker.dart";
2 |
3 | final rankPairsInStrongnessOrder = [
4 | RankPair.ofsuit(high: Rank.ace, kicker: Rank.ace),
5 | RankPair.ofsuit(high: Rank.king, kicker: Rank.king),
6 | RankPair.ofsuit(high: Rank.queen, kicker: Rank.queen),
7 | RankPair.ofsuit(high: Rank.jack, kicker: Rank.jack),
8 | RankPair.ofsuit(high: Rank.ten, kicker: Rank.ten),
9 | RankPair.ofsuit(high: Rank.nine, kicker: Rank.nine),
10 | RankPair.ofsuit(high: Rank.eight, kicker: Rank.eight),
11 | RankPair.suited(high: Rank.ace, kicker: Rank.king),
12 | RankPair.ofsuit(high: Rank.seven, kicker: Rank.seven),
13 | RankPair.suited(high: Rank.ace, kicker: Rank.queen),
14 | RankPair.ofsuit(high: Rank.ace, kicker: Rank.king),
15 | RankPair.suited(high: Rank.ace, kicker: Rank.jack),
16 | RankPair.suited(high: Rank.ace, kicker: Rank.ten),
17 | RankPair.ofsuit(high: Rank.ace, kicker: Rank.queen),
18 | RankPair.ofsuit(high: Rank.ace, kicker: Rank.jack),
19 | RankPair.suited(high: Rank.king, kicker: Rank.queen),
20 | RankPair.ofsuit(high: Rank.six, kicker: Rank.six),
21 | RankPair.suited(high: Rank.ace, kicker: Rank.nine),
22 | RankPair.ofsuit(high: Rank.ace, kicker: Rank.ten),
23 | RankPair.suited(high: Rank.king, kicker: Rank.jack),
24 | RankPair.suited(high: Rank.ace, kicker: Rank.eight),
25 | RankPair.suited(high: Rank.king, kicker: Rank.ten),
26 | RankPair.ofsuit(high: Rank.king, kicker: Rank.queen),
27 | RankPair.suited(high: Rank.ace, kicker: Rank.seven),
28 | RankPair.ofsuit(high: Rank.ace, kicker: Rank.nine),
29 | RankPair.ofsuit(high: Rank.king, kicker: Rank.jack),
30 | RankPair.suited(high: Rank.queen, kicker: Rank.jack),
31 | RankPair.ofsuit(high: Rank.five, kicker: Rank.five),
32 | RankPair.ofsuit(high: Rank.ace, kicker: Rank.eight),
33 | RankPair.suited(high: Rank.ace, kicker: Rank.six),
34 | RankPair.suited(high: Rank.king, kicker: Rank.nine),
35 | RankPair.suited(high: Rank.ace, kicker: Rank.five),
36 | RankPair.ofsuit(high: Rank.king, kicker: Rank.ten),
37 | RankPair.suited(high: Rank.queen, kicker: Rank.ten),
38 | RankPair.ofsuit(high: Rank.ace, kicker: Rank.seven),
39 | RankPair.suited(high: Rank.ace, kicker: Rank.four),
40 | RankPair.suited(high: Rank.king, kicker: Rank.eight),
41 | RankPair.ofsuit(high: Rank.queen, kicker: Rank.jack),
42 | RankPair.suited(high: Rank.ace, kicker: Rank.trey),
43 | RankPair.ofsuit(high: Rank.king, kicker: Rank.nine),
44 | RankPair.suited(high: Rank.queen, kicker: Rank.nine),
45 | RankPair.ofsuit(high: Rank.ace, kicker: Rank.six),
46 | RankPair.suited(high: Rank.king, kicker: Rank.seven),
47 | RankPair.ofsuit(high: Rank.ace, kicker: Rank.five),
48 | RankPair.suited(high: Rank.jack, kicker: Rank.ten),
49 | RankPair.ofsuit(high: Rank.queen, kicker: Rank.ten),
50 | RankPair.suited(high: Rank.ace, kicker: Rank.deuce),
51 | RankPair.ofsuit(high: Rank.four, kicker: Rank.four),
52 | RankPair.suited(high: Rank.king, kicker: Rank.six),
53 | RankPair.ofsuit(high: Rank.ace, kicker: Rank.four),
54 | RankPair.ofsuit(high: Rank.king, kicker: Rank.eight),
55 | RankPair.suited(high: Rank.queen, kicker: Rank.eight),
56 | RankPair.suited(high: Rank.king, kicker: Rank.five),
57 | RankPair.suited(high: Rank.jack, kicker: Rank.nine),
58 | RankPair.ofsuit(high: Rank.ace, kicker: Rank.trey),
59 | RankPair.ofsuit(high: Rank.queen, kicker: Rank.nine),
60 | RankPair.ofsuit(high: Rank.king, kicker: Rank.seven),
61 | RankPair.ofsuit(high: Rank.jack, kicker: Rank.ten),
62 | RankPair.suited(high: Rank.king, kicker: Rank.four),
63 | RankPair.ofsuit(high: Rank.ace, kicker: Rank.deuce),
64 | RankPair.suited(high: Rank.queen, kicker: Rank.seven),
65 | RankPair.ofsuit(high: Rank.king, kicker: Rank.six),
66 | RankPair.suited(high: Rank.ten, kicker: Rank.nine),
67 | RankPair.suited(high: Rank.jack, kicker: Rank.eight),
68 | RankPair.suited(high: Rank.king, kicker: Rank.trey),
69 | RankPair.ofsuit(high: Rank.queen, kicker: Rank.eight),
70 | RankPair.suited(high: Rank.queen, kicker: Rank.six),
71 | RankPair.ofsuit(high: Rank.trey, kicker: Rank.trey),
72 | RankPair.ofsuit(high: Rank.jack, kicker: Rank.nine),
73 | RankPair.ofsuit(high: Rank.king, kicker: Rank.five),
74 | RankPair.suited(high: Rank.king, kicker: Rank.deuce),
75 | RankPair.suited(high: Rank.queen, kicker: Rank.five),
76 | RankPair.suited(high: Rank.ten, kicker: Rank.eight),
77 | RankPair.suited(high: Rank.jack, kicker: Rank.seven),
78 | RankPair.ofsuit(high: Rank.king, kicker: Rank.four),
79 | RankPair.ofsuit(high: Rank.queen, kicker: Rank.seven),
80 | RankPair.suited(high: Rank.queen, kicker: Rank.four),
81 | RankPair.ofsuit(high: Rank.jack, kicker: Rank.eight),
82 | RankPair.ofsuit(high: Rank.ten, kicker: Rank.nine),
83 | RankPair.ofsuit(high: Rank.king, kicker: Rank.trey),
84 | RankPair.ofsuit(high: Rank.queen, kicker: Rank.six),
85 | RankPair.suited(high: Rank.nine, kicker: Rank.eight),
86 | RankPair.suited(high: Rank.ten, kicker: Rank.seven),
87 | RankPair.suited(high: Rank.jack, kicker: Rank.six),
88 | RankPair.suited(high: Rank.queen, kicker: Rank.trey),
89 | RankPair.ofsuit(high: Rank.deuce, kicker: Rank.deuce),
90 | RankPair.ofsuit(high: Rank.king, kicker: Rank.deuce),
91 | RankPair.ofsuit(high: Rank.queen, kicker: Rank.five),
92 | RankPair.suited(high: Rank.jack, kicker: Rank.five),
93 | RankPair.ofsuit(high: Rank.ten, kicker: Rank.eight),
94 | RankPair.suited(high: Rank.queen, kicker: Rank.deuce),
95 | RankPair.ofsuit(high: Rank.jack, kicker: Rank.seven),
96 | RankPair.suited(high: Rank.nine, kicker: Rank.seven),
97 | RankPair.suited(high: Rank.ten, kicker: Rank.six),
98 | RankPair.ofsuit(high: Rank.queen, kicker: Rank.four),
99 | RankPair.suited(high: Rank.jack, kicker: Rank.four),
100 | RankPair.ofsuit(high: Rank.nine, kicker: Rank.eight),
101 | RankPair.ofsuit(high: Rank.ten, kicker: Rank.seven),
102 | RankPair.suited(high: Rank.eight, kicker: Rank.seven),
103 | RankPair.ofsuit(high: Rank.queen, kicker: Rank.trey),
104 | RankPair.ofsuit(high: Rank.jack, kicker: Rank.six),
105 | RankPair.suited(high: Rank.jack, kicker: Rank.trey),
106 | RankPair.suited(high: Rank.nine, kicker: Rank.six),
107 | RankPair.suited(high: Rank.ten, kicker: Rank.five),
108 | RankPair.ofsuit(high: Rank.jack, kicker: Rank.five),
109 | RankPair.suited(high: Rank.jack, kicker: Rank.deuce),
110 | RankPair.ofsuit(high: Rank.queen, kicker: Rank.deuce),
111 | RankPair.ofsuit(high: Rank.nine, kicker: Rank.seven),
112 | RankPair.suited(high: Rank.eight, kicker: Rank.six),
113 | RankPair.suited(high: Rank.ten, kicker: Rank.four),
114 | RankPair.ofsuit(high: Rank.ten, kicker: Rank.six),
115 | RankPair.ofsuit(high: Rank.jack, kicker: Rank.four),
116 | RankPair.suited(high: Rank.nine, kicker: Rank.five),
117 | RankPair.suited(high: Rank.seven, kicker: Rank.six),
118 | RankPair.suited(high: Rank.ten, kicker: Rank.trey),
119 | RankPair.ofsuit(high: Rank.eight, kicker: Rank.seven),
120 | RankPair.ofsuit(high: Rank.jack, kicker: Rank.trey),
121 | RankPair.ofsuit(high: Rank.nine, kicker: Rank.six),
122 | RankPair.suited(high: Rank.eight, kicker: Rank.five),
123 | RankPair.suited(high: Rank.ten, kicker: Rank.deuce),
124 | RankPair.ofsuit(high: Rank.ten, kicker: Rank.five),
125 | RankPair.ofsuit(high: Rank.jack, kicker: Rank.deuce),
126 | RankPair.suited(high: Rank.nine, kicker: Rank.four),
127 | RankPair.suited(high: Rank.seven, kicker: Rank.five),
128 | RankPair.ofsuit(high: Rank.eight, kicker: Rank.six),
129 | RankPair.ofsuit(high: Rank.ten, kicker: Rank.four),
130 | RankPair.suited(high: Rank.nine, kicker: Rank.trey),
131 | RankPair.suited(high: Rank.six, kicker: Rank.five),
132 | RankPair.ofsuit(high: Rank.nine, kicker: Rank.five),
133 | RankPair.suited(high: Rank.eight, kicker: Rank.four),
134 | RankPair.ofsuit(high: Rank.seven, kicker: Rank.six),
135 | RankPair.ofsuit(high: Rank.ten, kicker: Rank.trey),
136 | RankPair.suited(high: Rank.nine, kicker: Rank.deuce),
137 | RankPair.suited(high: Rank.seven, kicker: Rank.four),
138 | RankPair.ofsuit(high: Rank.eight, kicker: Rank.five),
139 | RankPair.ofsuit(high: Rank.ten, kicker: Rank.deuce),
140 | RankPair.suited(high: Rank.six, kicker: Rank.four),
141 | RankPair.suited(high: Rank.five, kicker: Rank.four),
142 | RankPair.suited(high: Rank.eight, kicker: Rank.trey),
143 | RankPair.ofsuit(high: Rank.seven, kicker: Rank.five),
144 | RankPair.ofsuit(high: Rank.nine, kicker: Rank.four),
145 | RankPair.suited(high: Rank.eight, kicker: Rank.deuce),
146 | RankPair.ofsuit(high: Rank.six, kicker: Rank.five),
147 | RankPair.suited(high: Rank.seven, kicker: Rank.trey),
148 | RankPair.ofsuit(high: Rank.nine, kicker: Rank.trey),
149 | RankPair.ofsuit(high: Rank.eight, kicker: Rank.four),
150 | RankPair.suited(high: Rank.six, kicker: Rank.trey),
151 | RankPair.suited(high: Rank.five, kicker: Rank.trey),
152 | RankPair.ofsuit(high: Rank.nine, kicker: Rank.deuce),
153 | RankPair.ofsuit(high: Rank.seven, kicker: Rank.four),
154 | RankPair.suited(high: Rank.seven, kicker: Rank.deuce),
155 | RankPair.ofsuit(high: Rank.six, kicker: Rank.four),
156 | RankPair.suited(high: Rank.four, kicker: Rank.trey),
157 | RankPair.ofsuit(high: Rank.five, kicker: Rank.four),
158 | RankPair.ofsuit(high: Rank.eight, kicker: Rank.trey),
159 | RankPair.suited(high: Rank.six, kicker: Rank.deuce),
160 | RankPair.suited(high: Rank.five, kicker: Rank.deuce),
161 | RankPair.ofsuit(high: Rank.eight, kicker: Rank.deuce),
162 | RankPair.ofsuit(high: Rank.seven, kicker: Rank.trey),
163 | RankPair.suited(high: Rank.four, kicker: Rank.deuce),
164 | RankPair.ofsuit(high: Rank.six, kicker: Rank.trey),
165 | RankPair.ofsuit(high: Rank.five, kicker: Rank.trey),
166 | RankPair.suited(high: Rank.trey, kicker: Rank.deuce),
167 | RankPair.ofsuit(high: Rank.seven, kicker: Rank.deuce),
168 | RankPair.ofsuit(high: Rank.four, kicker: Rank.trey),
169 | RankPair.ofsuit(high: Rank.six, kicker: Rank.deuce),
170 | RankPair.ofsuit(high: Rank.five, kicker: Rank.deuce),
171 | RankPair.ofsuit(high: Rank.four, kicker: Rank.deuce),
172 | RankPair.ofsuit(high: Rank.trey, kicker: Rank.deuce),
173 | ];
174 |
--------------------------------------------------------------------------------
/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | _fe_analyzer_shared:
5 | dependency: transitive
6 | description:
7 | name: _fe_analyzer_shared
8 | url: "https://pub.dartlang.org"
9 | source: hosted
10 | version: "22.0.0"
11 | amplitude_flutter:
12 | dependency: "direct main"
13 | description:
14 | name: amplitude_flutter
15 | url: "https://pub.dartlang.org"
16 | source: hosted
17 | version: "3.3.0"
18 | analyzer:
19 | dependency: transitive
20 | description:
21 | name: analyzer
22 | url: "https://pub.dartlang.org"
23 | source: hosted
24 | version: "1.7.1"
25 | archive:
26 | dependency: transitive
27 | description:
28 | name: archive
29 | url: "https://pub.dartlang.org"
30 | source: hosted
31 | version: "3.1.2"
32 | args:
33 | dependency: transitive
34 | description:
35 | name: args
36 | url: "https://pub.dartlang.org"
37 | source: hosted
38 | version: "2.2.0"
39 | async:
40 | dependency: transitive
41 | description:
42 | name: async
43 | url: "https://pub.dartlang.org"
44 | source: hosted
45 | version: "2.6.1"
46 | boolean_selector:
47 | dependency: transitive
48 | description:
49 | name: boolean_selector
50 | url: "https://pub.dartlang.org"
51 | source: hosted
52 | version: "2.1.0"
53 | characters:
54 | dependency: transitive
55 | description:
56 | name: characters
57 | url: "https://pub.dartlang.org"
58 | source: hosted
59 | version: "1.1.0"
60 | charcode:
61 | dependency: transitive
62 | description:
63 | name: charcode
64 | url: "https://pub.dartlang.org"
65 | source: hosted
66 | version: "1.2.0"
67 | cli_util:
68 | dependency: transitive
69 | description:
70 | name: cli_util
71 | url: "https://pub.dartlang.org"
72 | source: hosted
73 | version: "0.3.3"
74 | clock:
75 | dependency: transitive
76 | description:
77 | name: clock
78 | url: "https://pub.dartlang.org"
79 | source: hosted
80 | version: "1.1.0"
81 | collection:
82 | dependency: transitive
83 | description:
84 | name: collection
85 | url: "https://pub.dartlang.org"
86 | source: hosted
87 | version: "1.15.0"
88 | convert:
89 | dependency: transitive
90 | description:
91 | name: convert
92 | url: "https://pub.dartlang.org"
93 | source: hosted
94 | version: "3.0.1"
95 | coverage:
96 | dependency: transitive
97 | description:
98 | name: coverage
99 | url: "https://pub.dartlang.org"
100 | source: hosted
101 | version: "1.0.3"
102 | crypto:
103 | dependency: transitive
104 | description:
105 | name: crypto
106 | url: "https://pub.dartlang.org"
107 | source: hosted
108 | version: "3.0.1"
109 | fake_async:
110 | dependency: transitive
111 | description:
112 | name: fake_async
113 | url: "https://pub.dartlang.org"
114 | source: hosted
115 | version: "1.2.0"
116 | ffi:
117 | dependency: transitive
118 | description:
119 | name: ffi
120 | url: "https://pub.dartlang.org"
121 | source: hosted
122 | version: "1.1.2"
123 | file:
124 | dependency: transitive
125 | description:
126 | name: file
127 | url: "https://pub.dartlang.org"
128 | source: hosted
129 | version: "6.1.2"
130 | firebase:
131 | dependency: transitive
132 | description:
133 | name: firebase
134 | url: "https://pub.dartlang.org"
135 | source: hosted
136 | version: "9.0.1"
137 | firebase_analytics:
138 | dependency: "direct main"
139 | description:
140 | name: firebase_analytics
141 | url: "https://pub.dartlang.org"
142 | source: hosted
143 | version: "8.3.1"
144 | firebase_analytics_platform_interface:
145 | dependency: transitive
146 | description:
147 | name: firebase_analytics_platform_interface
148 | url: "https://pub.dartlang.org"
149 | source: hosted
150 | version: "2.0.1"
151 | firebase_analytics_web:
152 | dependency: transitive
153 | description:
154 | name: firebase_analytics_web
155 | url: "https://pub.dartlang.org"
156 | source: hosted
157 | version: "0.3.0+1"
158 | firebase_auth:
159 | dependency: "direct main"
160 | description:
161 | name: firebase_auth
162 | url: "https://pub.dartlang.org"
163 | source: hosted
164 | version: "3.1.0"
165 | firebase_auth_platform_interface:
166 | dependency: transitive
167 | description:
168 | name: firebase_auth_platform_interface
169 | url: "https://pub.dartlang.org"
170 | source: hosted
171 | version: "6.1.0"
172 | firebase_auth_web:
173 | dependency: transitive
174 | description:
175 | name: firebase_auth_web
176 | url: "https://pub.dartlang.org"
177 | source: hosted
178 | version: "3.1.0"
179 | firebase_core:
180 | dependency: "direct main"
181 | description:
182 | name: firebase_core
183 | url: "https://pub.dartlang.org"
184 | source: hosted
185 | version: "1.6.0"
186 | firebase_core_platform_interface:
187 | dependency: transitive
188 | description:
189 | name: firebase_core_platform_interface
190 | url: "https://pub.dartlang.org"
191 | source: hosted
192 | version: "4.0.1"
193 | firebase_core_web:
194 | dependency: transitive
195 | description:
196 | name: firebase_core_web
197 | url: "https://pub.dartlang.org"
198 | source: hosted
199 | version: "1.1.0"
200 | flutter:
201 | dependency: "direct main"
202 | description: flutter
203 | source: sdk
204 | version: "0.0.0"
205 | flutter_launcher_icons:
206 | dependency: "direct main"
207 | description:
208 | name: flutter_launcher_icons
209 | url: "https://pub.dartlang.org"
210 | source: hosted
211 | version: "0.9.2"
212 | flutter_test:
213 | dependency: "direct dev"
214 | description: flutter
215 | source: sdk
216 | version: "0.0.0"
217 | flutter_web_plugins:
218 | dependency: transitive
219 | description: flutter
220 | source: sdk
221 | version: "0.0.0"
222 | glob:
223 | dependency: transitive
224 | description:
225 | name: glob
226 | url: "https://pub.dartlang.org"
227 | source: hosted
228 | version: "2.0.1"
229 | http:
230 | dependency: transitive
231 | description:
232 | name: http
233 | url: "https://pub.dartlang.org"
234 | source: hosted
235 | version: "0.13.3"
236 | http_multi_server:
237 | dependency: transitive
238 | description:
239 | name: http_multi_server
240 | url: "https://pub.dartlang.org"
241 | source: hosted
242 | version: "3.0.1"
243 | http_parser:
244 | dependency: transitive
245 | description:
246 | name: http_parser
247 | url: "https://pub.dartlang.org"
248 | source: hosted
249 | version: "4.0.0"
250 | image:
251 | dependency: transitive
252 | description:
253 | name: image
254 | url: "https://pub.dartlang.org"
255 | source: hosted
256 | version: "3.0.2"
257 | intl:
258 | dependency: transitive
259 | description:
260 | name: intl
261 | url: "https://pub.dartlang.org"
262 | source: hosted
263 | version: "0.17.0"
264 | io:
265 | dependency: transitive
266 | description:
267 | name: io
268 | url: "https://pub.dartlang.org"
269 | source: hosted
270 | version: "1.0.3"
271 | js:
272 | dependency: transitive
273 | description:
274 | name: js
275 | url: "https://pub.dartlang.org"
276 | source: hosted
277 | version: "0.6.3"
278 | logging:
279 | dependency: transitive
280 | description:
281 | name: logging
282 | url: "https://pub.dartlang.org"
283 | source: hosted
284 | version: "1.0.1"
285 | matcher:
286 | dependency: transitive
287 | description:
288 | name: matcher
289 | url: "https://pub.dartlang.org"
290 | source: hosted
291 | version: "0.12.10"
292 | meta:
293 | dependency: transitive
294 | description:
295 | name: meta
296 | url: "https://pub.dartlang.org"
297 | source: hosted
298 | version: "1.3.0"
299 | mime:
300 | dependency: transitive
301 | description:
302 | name: mime
303 | url: "https://pub.dartlang.org"
304 | source: hosted
305 | version: "1.0.0"
306 | node_preamble:
307 | dependency: transitive
308 | description:
309 | name: node_preamble
310 | url: "https://pub.dartlang.org"
311 | source: hosted
312 | version: "2.0.1"
313 | package_config:
314 | dependency: transitive
315 | description:
316 | name: package_config
317 | url: "https://pub.dartlang.org"
318 | source: hosted
319 | version: "2.0.0"
320 | package_info:
321 | dependency: "direct main"
322 | description:
323 | name: package_info
324 | url: "https://pub.dartlang.org"
325 | source: hosted
326 | version: "2.0.2"
327 | path:
328 | dependency: transitive
329 | description:
330 | name: path
331 | url: "https://pub.dartlang.org"
332 | source: hosted
333 | version: "1.8.0"
334 | path_provider_linux:
335 | dependency: transitive
336 | description:
337 | name: path_provider_linux
338 | url: "https://pub.dartlang.org"
339 | source: hosted
340 | version: "2.0.2"
341 | path_provider_platform_interface:
342 | dependency: transitive
343 | description:
344 | name: path_provider_platform_interface
345 | url: "https://pub.dartlang.org"
346 | source: hosted
347 | version: "2.0.1"
348 | path_provider_windows:
349 | dependency: transitive
350 | description:
351 | name: path_provider_windows
352 | url: "https://pub.dartlang.org"
353 | source: hosted
354 | version: "2.0.3"
355 | pedantic:
356 | dependency: transitive
357 | description:
358 | name: pedantic
359 | url: "https://pub.dartlang.org"
360 | source: hosted
361 | version: "1.11.1"
362 | petitparser:
363 | dependency: transitive
364 | description:
365 | name: petitparser
366 | url: "https://pub.dartlang.org"
367 | source: hosted
368 | version: "4.1.0"
369 | platform:
370 | dependency: transitive
371 | description:
372 | name: platform
373 | url: "https://pub.dartlang.org"
374 | source: hosted
375 | version: "3.0.2"
376 | plugin_platform_interface:
377 | dependency: transitive
378 | description:
379 | name: plugin_platform_interface
380 | url: "https://pub.dartlang.org"
381 | source: hosted
382 | version: "2.0.1"
383 | poker:
384 | dependency: "direct main"
385 | description:
386 | name: poker
387 | url: "https://pub.dartlang.org"
388 | source: hosted
389 | version: "0.2.5"
390 | pool:
391 | dependency: transitive
392 | description:
393 | name: pool
394 | url: "https://pub.dartlang.org"
395 | source: hosted
396 | version: "1.5.0"
397 | process:
398 | dependency: transitive
399 | description:
400 | name: process
401 | url: "https://pub.dartlang.org"
402 | source: hosted
403 | version: "4.2.3"
404 | pub_semver:
405 | dependency: transitive
406 | description:
407 | name: pub_semver
408 | url: "https://pub.dartlang.org"
409 | source: hosted
410 | version: "2.0.0"
411 | sentry:
412 | dependency: "direct main"
413 | description:
414 | name: sentry
415 | url: "https://pub.dartlang.org"
416 | source: hosted
417 | version: "6.0.0"
418 | shared_preferences:
419 | dependency: "direct main"
420 | description:
421 | name: shared_preferences
422 | url: "https://pub.dartlang.org"
423 | source: hosted
424 | version: "2.0.7"
425 | shared_preferences_linux:
426 | dependency: transitive
427 | description:
428 | name: shared_preferences_linux
429 | url: "https://pub.dartlang.org"
430 | source: hosted
431 | version: "2.0.2"
432 | shared_preferences_macos:
433 | dependency: transitive
434 | description:
435 | name: shared_preferences_macos
436 | url: "https://pub.dartlang.org"
437 | source: hosted
438 | version: "2.0.2"
439 | shared_preferences_platform_interface:
440 | dependency: transitive
441 | description:
442 | name: shared_preferences_platform_interface
443 | url: "https://pub.dartlang.org"
444 | source: hosted
445 | version: "2.0.0"
446 | shared_preferences_web:
447 | dependency: transitive
448 | description:
449 | name: shared_preferences_web
450 | url: "https://pub.dartlang.org"
451 | source: hosted
452 | version: "2.0.2"
453 | shared_preferences_windows:
454 | dependency: transitive
455 | description:
456 | name: shared_preferences_windows
457 | url: "https://pub.dartlang.org"
458 | source: hosted
459 | version: "2.0.2"
460 | shelf:
461 | dependency: transitive
462 | description:
463 | name: shelf
464 | url: "https://pub.dartlang.org"
465 | source: hosted
466 | version: "1.2.0"
467 | shelf_packages_handler:
468 | dependency: transitive
469 | description:
470 | name: shelf_packages_handler
471 | url: "https://pub.dartlang.org"
472 | source: hosted
473 | version: "3.0.0"
474 | shelf_static:
475 | dependency: transitive
476 | description:
477 | name: shelf_static
478 | url: "https://pub.dartlang.org"
479 | source: hosted
480 | version: "1.1.0"
481 | shelf_web_socket:
482 | dependency: transitive
483 | description:
484 | name: shelf_web_socket
485 | url: "https://pub.dartlang.org"
486 | source: hosted
487 | version: "1.0.1"
488 | sky_engine:
489 | dependency: transitive
490 | description: flutter
491 | source: sdk
492 | version: "0.0.99"
493 | source_map_stack_trace:
494 | dependency: transitive
495 | description:
496 | name: source_map_stack_trace
497 | url: "https://pub.dartlang.org"
498 | source: hosted
499 | version: "2.1.0"
500 | source_maps:
501 | dependency: transitive
502 | description:
503 | name: source_maps
504 | url: "https://pub.dartlang.org"
505 | source: hosted
506 | version: "0.10.10"
507 | source_span:
508 | dependency: transitive
509 | description:
510 | name: source_span
511 | url: "https://pub.dartlang.org"
512 | source: hosted
513 | version: "1.8.1"
514 | stack_trace:
515 | dependency: transitive
516 | description:
517 | name: stack_trace
518 | url: "https://pub.dartlang.org"
519 | source: hosted
520 | version: "1.10.0"
521 | stream_channel:
522 | dependency: transitive
523 | description:
524 | name: stream_channel
525 | url: "https://pub.dartlang.org"
526 | source: hosted
527 | version: "2.1.0"
528 | string_scanner:
529 | dependency: transitive
530 | description:
531 | name: string_scanner
532 | url: "https://pub.dartlang.org"
533 | source: hosted
534 | version: "1.1.0"
535 | term_glyph:
536 | dependency: transitive
537 | description:
538 | name: term_glyph
539 | url: "https://pub.dartlang.org"
540 | source: hosted
541 | version: "1.2.0"
542 | test:
543 | dependency: "direct dev"
544 | description:
545 | name: test
546 | url: "https://pub.dartlang.org"
547 | source: hosted
548 | version: "1.16.8"
549 | test_api:
550 | dependency: transitive
551 | description:
552 | name: test_api
553 | url: "https://pub.dartlang.org"
554 | source: hosted
555 | version: "0.3.0"
556 | test_core:
557 | dependency: transitive
558 | description:
559 | name: test_core
560 | url: "https://pub.dartlang.org"
561 | source: hosted
562 | version: "0.3.19"
563 | typed_data:
564 | dependency: transitive
565 | description:
566 | name: typed_data
567 | url: "https://pub.dartlang.org"
568 | source: hosted
569 | version: "1.3.0"
570 | uuid:
571 | dependency: transitive
572 | description:
573 | name: uuid
574 | url: "https://pub.dartlang.org"
575 | source: hosted
576 | version: "3.0.4"
577 | vector_math:
578 | dependency: transitive
579 | description:
580 | name: vector_math
581 | url: "https://pub.dartlang.org"
582 | source: hosted
583 | version: "2.1.0"
584 | vm_service:
585 | dependency: transitive
586 | description:
587 | name: vm_service
588 | url: "https://pub.dartlang.org"
589 | source: hosted
590 | version: "6.2.0"
591 | watcher:
592 | dependency: transitive
593 | description:
594 | name: watcher
595 | url: "https://pub.dartlang.org"
596 | source: hosted
597 | version: "1.0.0"
598 | web_socket_channel:
599 | dependency: transitive
600 | description:
601 | name: web_socket_channel
602 | url: "https://pub.dartlang.org"
603 | source: hosted
604 | version: "2.1.0"
605 | webkit_inspection_protocol:
606 | dependency: transitive
607 | description:
608 | name: webkit_inspection_protocol
609 | url: "https://pub.dartlang.org"
610 | source: hosted
611 | version: "1.0.0"
612 | win32:
613 | dependency: transitive
614 | description:
615 | name: win32
616 | url: "https://pub.dartlang.org"
617 | source: hosted
618 | version: "2.2.8"
619 | xdg_directories:
620 | dependency: transitive
621 | description:
622 | name: xdg_directories
623 | url: "https://pub.dartlang.org"
624 | source: hosted
625 | version: "0.2.0"
626 | xml:
627 | dependency: transitive
628 | description:
629 | name: xml
630 | url: "https://pub.dartlang.org"
631 | source: hosted
632 | version: "5.1.2"
633 | yaml:
634 | dependency: transitive
635 | description:
636 | name: yaml
637 | url: "https://pub.dartlang.org"
638 | source: hosted
639 | version: "3.1.0"
640 | sdks:
641 | dart: ">=2.13.0 <3.0.0"
642 | flutter: ">=2.0.0"
643 |
--------------------------------------------------------------------------------