This commit is contained in:
Dmitry 2024-07-19 00:45:04 +03:00
parent d3e9f49c09
commit 6a5693952e
59 changed files with 383 additions and 1533 deletions

View file

@ -4,7 +4,7 @@
# This file should be version controlled and should not be manually edited.
version:
revision: "6c4930c4ac86fb286f30e31d0ec8bffbcbb9953e"
revision: "761747bfc538b5af34aa0d3fac380f1bc331ec49"
channel: "stable"
project_type: app
@ -13,11 +13,11 @@ project_type: app
migration:
platforms:
- platform: root
create_revision: 6c4930c4ac86fb286f30e31d0ec8bffbcbb9953e
base_revision: 6c4930c4ac86fb286f30e31d0ec8bffbcbb9953e
- platform: macos
create_revision: 6c4930c4ac86fb286f30e31d0ec8bffbcbb9953e
base_revision: 6c4930c4ac86fb286f30e31d0ec8bffbcbb9953e
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
- platform: android
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
# User provided section

View file

@ -2,27 +2,29 @@ plugins {
id "com.android.application"
// START: FlutterFire Configuration
id 'com.google.gms.google-services'
id 'com.google.firebase.crashlytics'
// END: FlutterFire Configuration
id "kotlin-android"
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id "dev.flutter.flutter-gradle-plugin"
}
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
def localPropertiesFile = rootProject.file("local.properties")
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localPropertiesFile.withReader("UTF-8") { reader ->
localProperties.load(reader)
}
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
def flutterVersionCode = localProperties.getProperty("flutter.versionCode")
if (flutterVersionCode == null) {
flutterVersionCode = '1'
flutterVersionCode = "1"
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
def flutterVersionName = localProperties.getProperty("flutter.versionName")
if (flutterVersionName == null) {
flutterVersionName = '1.0'
flutterVersionName = "1.0"
}
def keystoreProperties = new Properties()
@ -31,34 +33,25 @@ if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
android {
namespace "com.cinnabarflower.mnemo_cards"
compileSdkVersion flutter.compileSdkVersion
ndkVersion flutter.ndkVersion
namespace = "com.cinnabarflower.mnemo_cards"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.cinnabarflower.mnemo_cards"
applicationId = "com.cinnabarflower.mnemo_cards"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion 25
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
minSdk = 25
targetSdk = flutter.targetSdkVersion
versionCode = flutterVersionCode.toInteger()
versionName = flutterVersionName
}
signingConfigs {
@ -69,6 +62,7 @@ android {
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
release {
signingConfig signingConfigs.release
@ -77,7 +71,5 @@ android {
}
flutter {
source '../..'
source = "../.."
}
dependencies {}

View file

@ -21,6 +21,14 @@
"certificate_hash": "6df4f259b9c53ac01472c17df69e8c91494f3947"
}
},
{
"client_id": "701767851968-5e6v0b5humhj0iu22t21nb9dtgt4tjus.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.cinnabarflower.mnemo_cards",
"certificate_hash": "49bb5b6863c9e6d8db43e7eb4d54796a300d87fe"
}
},
{
"client_id": "701767851968-vqvjgf1u79jg924inm25bfuv4dg41t2u.apps.googleusercontent.com",
"client_type": 1,

View file

@ -3,8 +3,5 @@
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<application
android:networkSecurityConfig="@xml/network_security_config"
/>
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

View file

@ -12,6 +12,7 @@
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:taskAffinity=""
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
@ -35,9 +36,15 @@
android:name="flutterEmbedding"
android:value="2" />
</application>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
<queries>
<intent>
<action android:name="android.intent.action.TTS_SERVICE" />
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
</intent>
</queries>
</manifest>

View file

@ -2,5 +2,4 @@ package com.cinnabarflower.mnemo_cards
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}
class MainActivity: FlutterActivity()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -1,20 +1,3 @@
buildscript {
ext.kotlin_version = '1.8.20'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.3.0'
// START: FlutterFire Configuration
classpath 'com.google.gms:google-services:4.3.14'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
// END: FlutterFire Configuration
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
@ -22,12 +5,12 @@ allprojects {
}
}
rootProject.buildDir = '../build'
rootProject.buildDir = "../build"
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
project.evaluationDependsOn(":app")
}
tasks.register("clean", Delete) {

View file

@ -1,3 +1,3 @@
org.gradle.jvmargs=-Xmx4608M
org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true

View file

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

4
android/key.properties Normal file
View file

@ -0,0 +1,4 @@
storePassword=345234
keyPassword=345234
keyAlias=upload
storeFile=../../../../upload-keystore.jks

BIN
android/pepk.jar Normal file

Binary file not shown.

View file

@ -5,18 +5,14 @@ pluginManagement {
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}
settings.ext.flutterSdkPath = flutterSdkPath()
}()
includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "7.3.0" apply false
// START: FlutterFire Configuration
id "com.google.gms.google-services" version "4.3.15" apply false
// END: FlutterFire Configuration
id "org.jetbrains.kotlin.android" version "1.7.10" apply false
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
// For yoo kassa
@ -33,6 +29,14 @@ pluginManagement {
}
}
include ":app"
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "7.3.0" apply false
// START: FlutterFire Configuration
id "com.google.gms.google-services" version "4.3.15" apply false
id "com.google.firebase.crashlytics" version "2.8.1" apply false
// END: FlutterFire Configuration
id "org.jetbrains.kotlin.android" version "1.9.10" apply false
}
apply from: "${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle/app_plugin_loader.gradle"
include ":app"

BIN
cerdo_1024x512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

BIN
cerdo_512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

View file

@ -203,6 +203,7 @@
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
10510DEBDDA8DED725BA2250 /* [CP] Copy Pods Resources */,
EAA1E3F7E6B26606E42D1048 /* [CP] Embed Pods Frameworks */,
CDC7B89EEA6C1D8D4DC3C75E /* FlutterFire: "flutterfire upload-crashlytics-symbols" */,
);
buildRules = (
);
@ -345,6 +346,24 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
CDC7B89EEA6C1D8D4DC3C75E /* FlutterFire: "flutterfire upload-crashlytics-symbols" */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "FlutterFire: \"flutterfire upload-crashlytics-symbols\"";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\n#!/bin/bash\nPATH=${PATH}:$FLUTTER_ROOT/bin:$HOME/.pub-cache/bin\nflutterfire upload-crashlytics-symbols --upload-symbols-script-path=$PODS_ROOT/FirebaseCrashlytics/upload-symbols --platform=ios --apple-project-path=${SRCROOT} --env-platform-name=${PLATFORM_NAME} --env-configuration=${CONFIGURATION} --env-project-dir=${PROJECT_DIR} --env-built-products-dir=${BUILT_PRODUCTS_DIR} --env-dwarf-dsym-folder-path=${DWARF_DSYM_FOLDER_PATH} --env-dwarf-dsym-file-name=${DWARF_DSYM_FILE_NAME} --env-infoplist-path=${INFOPLIST_PATH} --default-config=default\n";
};
EAA1E3F7E6B26606E42D1048 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;

View file

@ -5,11 +5,10 @@ import 'package:flutter/material.dart';
import 'package:file_picker/file_picker.dart';
import 'package:mnemo_cards/admin/admin_api.dart';
import 'package:mnemo_cards/widgets/header.dart';
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart';
import '../main.dart';
import '../widgets/image_fade.dart';
class EditCard {
GameCardDto gameCardDto;

View file

@ -5,13 +5,12 @@ import 'package:flutter/material.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
import 'package:mnemo_cards/admin/admin_api.dart';
import 'package:mnemo_cards/managers/repository/api.dart';
import 'package:mnemo_cards/widgets/header.dart';
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
import 'package:file_picker/file_picker.dart';
import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart';
import '../utils/string_helper.dart';
import '../main.dart';
import '../widgets/image_fade.dart';
class AddPackage with Api {
EditCardPackDto cardPackDto;
@ -132,6 +131,13 @@ class AddPackage with Api {
cardPackDto = cardPackDto.copyWith.googlePlayId(v);
},
),
_TextParam(
'Rustore id',
cardPackDto.rustoreId,
(v) {
cardPackDto = cardPackDto.copyWith.rustoreId(v);
},
),
_TextParam(
'Appstore id',
cardPackDto.appStoreId,

View file

@ -4,8 +4,8 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:mnemo_cards/admin/admin_api.dart';
import 'package:mnemo_cards/widgets/header.dart';
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart';
import '../main.dart';

View file

@ -9,8 +9,8 @@ import 'package:mnemo_cards/admin/admin_api.dart';
import 'package:mnemo_cards/di/injector.dart';
import 'package:mnemo_cards/managers/repository/dio_provider.dart';
import 'package:mnemo_cards/theme/themes.dart';
import 'package:mnemo_cards/widgets/header.dart';
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart';
import '../main.dart';
import 'add_package.dart';

View file

@ -6,8 +6,8 @@ import 'package:mnemo_cards/admin/admin_api.dart';
import 'package:mnemo_cards/di/injector.dart';
import 'package:mnemo_cards/di/locator.dart';
import 'package:mnemo_cards/managers/repository/dio_provider.dart';
import 'package:mnemo_cards/widgets/header.dart';
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart';
import '../main.dart';

View file

@ -3,13 +3,16 @@ import 'dart:developer';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_rustore_billing/pigeons/rustore.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
import 'package:webview_flutter/webview_flutter.dart';
import '../../di/injector.dart';
import '../../di/locator.dart';
import '../../flags.dart';
import '../../main.dart';
import '../analytics/analytics.dart';
import 'rustore/mnemo_rustore.dart';
enum StoreItemType {
ads,
@ -26,14 +29,18 @@ class InAppPurchaseService {
Future<bool> buyPack(CardPackBuyDto dto) async {
try {
final pack = await getPackStoreProduct(dto);
final result = await buyItemInStore(pack.first);
if (RUSTORE_BUILD) {
return RustorePurchaseService().buyPack(dto);
} else {
final pack = await _getPackStoreProduct(dto);
final result = await _buyItemInStore(pack.first);
return result;
}
} catch (e, s) {
log('err', error: e, stackTrace: s);
Analytics.buyError(
dto,
PaymentSystem.google,
androidPaymentSystem,
data: {
'e': e.toString(),
's': s.toString(),
@ -92,12 +99,12 @@ class InAppPurchaseService {
});
}
Future<List<ProductDetails>> getPackStoreProduct(CardPackBuyDto dto) async {
Future<List<ProductDetails>> _getPackStoreProduct(CardPackBuyDto dto) async {
final bool isAvailable = await _inAppPurchase.isAvailable();
if (!isAvailable) {
Analytics.buyError(
dto,
PaymentSystem.google,
androidPaymentSystem,
message: 'in app purchase not available',
);
return [];
@ -109,7 +116,7 @@ class InAppPurchaseService {
if (ids.isEmpty) {
Analytics.buyError(
dto,
PaymentSystem.google,
androidPaymentSystem,
message: 'product id not set',
);
return [];
@ -122,7 +129,7 @@ class InAppPurchaseService {
productDetailResponse.productDetails.isEmpty) {
Analytics.buyError(
dto,
PaymentSystem.google,
androidPaymentSystem,
data: {
'msg': 'cant get product details ${productDetailResponse.error}',
'not_found': productDetailResponse.notFoundIDs.join(','),
@ -134,7 +141,7 @@ class InAppPurchaseService {
return productDetailResponse.productDetails;
}
Future<bool> buyItemInStore(ProductDetails product) async {
Future<bool> _buyItemInStore(ProductDetails product) async {
final PurchaseParam purchaseParam = PurchaseParam(productDetails: product);
return InAppPurchase.instance
.buyNonConsumable(purchaseParam: purchaseParam);
@ -152,7 +159,7 @@ class PurchaseDetailsStreamSubscription {
try {
Analytics.buyEvent(
null,
PaymentSystem.google,
androidPaymentSystem,
message: 'onPurchased',
data: {
'status': purchaseDetails.status.name,
@ -163,12 +170,12 @@ class PurchaseDetailsStreamSubscription {
final result = await locator.repository.checkPayment(
purchaseDetails.productID,
purchaseDetails.verificationData.serverVerificationData,
PaymentSystem.google,
androidPaymentSystem,
);
if (result) {
Analytics.buyComplete(
null,
PaymentSystem.google,
androidPaymentSystem,
message: purchaseDetails.productID,
);
log('Purchased');

View file

@ -0,0 +1,50 @@
import 'package:flutter_rustore_billing/flutter_rustore_billing.dart';
import 'package:mnemo_cards/flags.dart';
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
import '../../../di/locator.dart';
class RustorePurchaseService {
Future<bool> init() async {
return RustoreBillingClient.initialize(
"2063563896",
"mnemocards://iamback",
true,
).then((value) {
print("initialize success: $value");
return true;
}, onError: (err) {
print("initialize err: $err");
return false;
});
}
Future<bool> buyPack(CardPackBuyDto dto) async {
if (dto.rustoreId == null) {
return false;
}
if (!await init()) {
return false;
}
final product = (await RustoreBillingClient.products([dto.rustoreId]))
.products
.firstOrNull;
if (product == null) {
return false;
}
final result = await RustoreBillingClient.purchase(dto.rustoreId!, null);
if (result.successPurchase == null && result.successInvoice == null) {
return false;
}
if (result.successPurchase != null) {
final purchase = result.successPurchase!;
final success = await locator.repository.checkPayment(
purchase.productId,
purchase.subscriptionToken ?? '',
androidPaymentSystem,
);
return success;
}
return true;
}
}

View file

@ -4,11 +4,10 @@ import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:mnemo_cards/utils/color_helper.dart';
import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart';
import 'package:rxdart/rxdart.dart';
import '../../di/locator.dart';
import '../../widgets/big_back_button.dart';
import '../../widgets/horizontal_progress.dart';
import 'question_states/test_question_state.dart';
class TestCompleteWidget extends StatelessWidget {

View file

@ -1,6 +1,11 @@
import 'package:mnemo_cards/main.dart';
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart';
const ADMIN_BUILD = false;
const ADMIN_BUILD = CommonPrefs.ADMIN_BUILD;
const RUSTORE_BUILD = CommonPrefs.RUSTORE_BUILD;
final androidPaymentSystem = CommonPrefs.androidPaymentSystem;
bool get SHOW_ADMIN =>
ADMIN_BUILD && (globalSharedPreferences.getBool('show_admin') ?? false);
ADMIN_BUILD && (globalSharedPreferences.getBool('show_admin') ?? true);

View file

@ -151,7 +151,17 @@ class MyApp extends StatelessWidget with WidgetsBindingObserver {
theme: theme2,
darkTheme: darkTheme2,
themeMode: themeMode,
routerConfig: appRouter.config(),
routerConfig: appRouter.config(
deepLinkBuilder: (link) {
// if(link.path.contains('/ru/reset-password')) {
// return const DeepLink(
// [ResetPassRoute()]
// );
// } else {
return DeepLink.defaultPath;
}
// }
),
scaffoldMessengerKey: scaffoldMessengerKey,
debugShowCheckedModeBanner: false,
);
@ -199,6 +209,13 @@ class _MainTabsPageState extends State<MainTabsPage> {
_Tabs _currentTab = _Tabs.home;
@override
void didChangeDependencies() {
super.didChangeDependencies();
print('CHANGE DEPS');
}
@override
void initState() {
super.initState();

View file

@ -171,5 +171,3 @@ class UserManager {
_subscription = null;
}
}
enum ExternalIdType { google, device }

View file

@ -39,7 +39,7 @@ class AuthPage extends StatelessWidget {
),
),
if (ADMIN_BUILD)
SharedPrefButton<bool>(
MobileSharedPrefButton<bool>(
builder: (v, _) => (v ?? false) ? Text('PROD') : Text('TEST'),
spKey: 'env',
setOnTap: (v) => !(v ?? false),

View file

@ -7,7 +7,7 @@ import 'package:mnemo_cards/domain/router/app_router.dart';
import 'package:mnemo_cards/domain/router/app_router.gr.dart';
import 'package:mnemo_cards/flags.dart';
import 'package:mnemo_cards/main.dart';
import 'package:mnemo_cards/widgets/header.dart';
import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:url_launcher/url_launcher.dart';
@ -16,11 +16,8 @@ import '../admin/all_users.dart';
import '../di/locator.dart';
import '../features/analytics/analytics.dart';
import '../theme/themes.dart';
import '../widgets/big_back_button.dart';
import '../widgets/enter_promo_code_widget.dart';
import '../widgets/shared_pref_button.dart';
import '../widgets/simple_tile.dart';
import '../widgets/user_statistics.dart';
@RoutePage()
class ProfilePage extends StatelessWidget {
@ -106,7 +103,7 @@ class ProfilePage extends StatelessWidget {
),
),
Expanded(
child: SharedPrefButton<bool>(
child: MobileSharedPrefButton<bool>(
builder: (v, _) =>
(v ?? false) ? Text('PROD') : Text('TEST'),
spKey: 'env',
@ -140,7 +137,7 @@ class ProfilePage extends StatelessWidget {
height: 5,
color: borderGray.withOpacity(0.2),
),
const EnterPromoCodeWidget(),
const MobileEnterPromoCodeWidget(),
Divider(
height: 5,
color: borderGray.withOpacity(0.2),

View file

@ -4,19 +4,14 @@ import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:mnemo_cards/flags.dart';
import 'package:mnemo_cards/widgets/header.dart';
import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:url_launcher/url_launcher.dart';
import '../di/locator.dart';
import '../features/analytics/analytics.dart';
import '../theme/themes.dart';
import '../widgets/big_back_button.dart';
import '../widgets/shared_pref_button.dart';
import '../widgets/simple_tile.dart';
import '../widgets/slider.dart';
import '../widgets/switch_tile.dart';
import '../widgets/text_button.dart';
@RoutePage()
class SettingsPage extends StatelessWidget {
@ -45,7 +40,7 @@ class SettingsPage extends StatelessWidget {
height: 1,
color: borderGray.withOpacity(0.2),
),
SharedPrefButton<bool>.builder(
MobileSharedPrefButton<bool>.builder(
spKey: 'dark_theme',
builder: (enabled, _) => AbsorbPointer(
child: SwitchTile(
@ -64,7 +59,7 @@ class SettingsPage extends StatelessWidget {
height: 1,
color: borderGray.withOpacity(0.2),
),
SharedPrefButton<bool>.builder(
MobileSharedPrefButton<bool>.builder(
spKey: 'sound_on',
builder: (enabled, _) => AbsorbPointer(
child: SwitchTile(
@ -78,7 +73,7 @@ class SettingsPage extends StatelessWidget {
height: 1,
color: borderGray.withOpacity(0.2),
),
SharedPrefButton<bool>.builder(
MobileSharedPrefButton<bool>.builder(
spKey: 'auto_play_sound_view',
builder: (enabled, _) => AbsorbPointer(
child: SwitchTile(
@ -92,7 +87,7 @@ class SettingsPage extends StatelessWidget {
height: 1,
color: borderGray.withOpacity(0.2),
),
SharedPrefButton<bool>.builder(
MobileSharedPrefButton<bool>.builder(
spKey: 'auto_play_sound_tests',
builder: (enabled, _) => AbsorbPointer(
child: SwitchTile(
@ -108,7 +103,7 @@ class SettingsPage extends StatelessWidget {
),
SimpleTile.text(
text: 'Скорость чтения',
trail: SharedPrefButton<double>.builder(
trail: MobileSharedPrefButton<double>.builder(
spKey: 'sound_speed',
builder: (v, s) => MnemoSlider(
onChanged: (value) => s(value),
@ -122,7 +117,7 @@ class SettingsPage extends StatelessWidget {
color: borderGray.withOpacity(0.2),
),
if (ADMIN_BUILD)
SharedPrefButton<bool>.builder(
MobileSharedPrefButton<bool>.builder(
spKey: 'show_admin',
builder: (enabled, _) => AbsorbPointer(
child: SwitchTile(

View file

@ -1,37 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:mnemo_cards/main.dart';
import '../theme/themes.dart';
class BigBackButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 10.0.w, vertical: 10.0.h),
child: InkWell(
onTap: appRouter.maybePop,
child: Container(
height: 90.h,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Theme.of(context).scaffoldBackgroundColor,
border: Border.all(color: borderGray),
borderRadius: BorderRadius.circular(16.0),
),
padding: EdgeInsets.all(10.0.w),
child: Text(
'Назад',
style: TextStyle(
fontSize: 25.sp,
fontWeight: FontWeight.w700,
height: 0.85,
),
),
),
),
);
}
const BigBackButton();
}

View file

@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart';
import '../../features/card_swiper/src/card_swiper_controller.dart';
import '../header.dart';
class CardGameHeader extends StatelessWidget {
final CardSwiperController controller;

View file

@ -5,8 +5,8 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:mnemo_cards/main.dart';
import 'package:mnemo_cards/widgets/fade_in_widget.dart';
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart';
import 'package:yandex_mobileads/mobile_ads.dart';
import '../../domain/router/app_router.gr.dart';
import '../../features/analytics/analytics.dart';
@ -14,7 +14,6 @@ import '../../features/yandex_ads/yandex_ads.dart';
import '../../di/locator.dart';
import '../../theme/themes.dart';
import '../horizontal_progress.dart';
import 'card_pack_header.dart';
import 'package:mnemo_cards/utils/string_helper.dart';

View file

@ -1,5 +1,6 @@
import 'dart:convert';
import 'dart:developer';
import 'package:mnemo_cards/flags.dart';
import 'package:mnemo_cards/utils/string_helper.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
@ -164,7 +165,7 @@ class _BuyButton extends StatelessWidget {
InkWell(
onTap: () async {
if (locator.userManager.hasUser) {
Analytics.buyPackPressed(dto, PaymentSystem.google);
Analytics.buyPackPressed(dto, androidPaymentSystem);
final result = await locator.purchaseService.buyPack(dto);
if (result) {
_updatePack();

View file

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
import 'package:mnemo_cards/utils/string_helper.dart';
import '../header.dart';
import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart';
class CardPackHeader extends StatelessWidget {
final String? title;

View file

@ -1,17 +1,17 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:mnemo_cards/features/packs/images_holder.dart';
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart'
as frontend;
import 'package:reorderables/reorderables.dart';
import 'package:rxdart/rxdart.dart';
import '../../utils/string_helper.dart';
import '../../domain/router/app_router.gr.dart';
import '../../main.dart';
import '../feedback_builder.dart';
import '../game_card_widget.dart';
import 'cards_view_controller.dart';
@ -157,7 +157,8 @@ class _CardsViewState extends State<CardsView>
onReorder: (int from, int to) {
controller.onOrderChanged(from, to);
},
buildDraggableFeedback: FeedbackBuilder.simple,
buildDraggableFeedback:
frontend.FeedbackBuilder.simple,
padding: EdgeInsets.symmetric(
horizontal: (cardWidth - imageWidth - 1),
vertical: (cardWidth - imageWidth - 1),

View file

@ -1,88 +1,21 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:mnemo_cards/di/locator.dart';
import 'package:mnemo_cards/theme/themes.dart';
import 'package:mnemo_cards/widgets/simple_tile.dart';
import 'package:flutter/cupertino.dart';
import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart';
class EnterPromoCodeWidget extends StatefulWidget {
const EnterPromoCodeWidget();
import '../di/locator.dart';
@override
State<StatefulWidget> createState() => _EnterPromoCodeState();
}
class MobileEnterPromoCodeWidget extends StatelessWidget {
class _EnterPromoCodeState extends State<EnterPromoCodeWidget> {
final TextEditingController controller = TextEditingController();
String? message;
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
SimpleTile(
title: Row(
children: [
Text('Промокод'),
SizedBox(
width: 16.w,
),
Expanded(
child: TextField(
// cursorHeight: 17.h,
controller: controller,
textAlignVertical: TextAlignVertical.center,
style: TextStyle(fontSize: 17.sp),
decoration: InputDecoration(
contentPadding: EdgeInsets.all(8.0),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
),
SizedBox(
width: 8.w,
),
],
),
trail: GestureDetector(
onTap: () async {
final result = await locator.userManager.applyPromocode(
controller.text,
);
if (result.success == true) {
controller.clear();
return EnterPromoCodeWidget(onApplyPromoCode: onApplyPromoCode);
}
setState(() {
message = result.message;
});
FocusScope.of(context).unfocus();
},
child: AspectRatio(
aspectRatio: 1.0,
child: Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: progressBlue,
borderRadius: BorderRadius.circular(12.0),
),
child: Image.asset(
'icons/gift.png',
width: 17.w,
color: Theme.of(context).colorScheme.primary,
),
),
),
),
),
if (message != null && message!.isNotEmpty)
Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: Text(message!),
),
],
);
Future<(bool, String?)> onApplyPromoCode(String code) async {
final result = await locator.userManager.applyPromocode(code);
return (result.success ?? false, result.message);
}
const MobileEnterPromoCodeWidget();
}

View file

@ -1,30 +0,0 @@
import 'package:flutter/cupertino.dart';
class FadeInWidget extends StatelessWidget {
final Widget child;
final Duration? delay;
final Future? future;
final Duration animationDuration;
FadeInWidget({
required this.child,
this.delay = const Duration(milliseconds: 200),
this.future,
this.animationDuration = const Duration(milliseconds: 200),
super.key,
}) : assert(future != null || delay != null);
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: future ?? Future.delayed(delay!),
builder: (context, s) {
return AnimatedOpacity(
duration: animationDuration,
opacity: s.connectionState == ConnectionState.done ? 1.0 : 0.0,
child: child,
);
},
);
}
}

View file

@ -1,31 +0,0 @@
import 'package:flutter/material.dart';
class FeedbackBuilder {
static Widget tests(Widget child, int index, Animation<double> animation) =>
Transform(
transform: new Matrix4.rotationZ(0),
child: Material(
child: child,
elevation: 1.0,
color: Colors.transparent,
borderRadius: BorderRadius.circular(16.0),
),
);
static Widget simple(
BuildContext context,
BoxConstraints constraints,
Widget child,
) {
return Transform(
transform: new Matrix4.rotationZ(0.05),
// alignment: FractionalOffset.topLeft,
child: Material(
child: child,
elevation: 6.0,
color: Colors.transparent,
borderRadius: BorderRadius.circular(12.0),
),
);
}
}

View file

@ -7,14 +7,13 @@ import 'package:mnemo_cards/features/yandex_ads/yandex_ads.dart';
import 'package:mnemo_cards/managers/audio_player.dart';
import 'package:mnemo_cards/utils/color_helper.dart';
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart';
import 'package:yandex_mobileads/mobile_ads.dart';
import '../features/card_flipper/flip_card.dart';
import '../features/card_flipper/flip_card_controllers.dart';
import '../features/card_flipper/flip_side.dart';
import '../theme/themes.dart';
import 'image_fade.dart';
import 'mnemo_text.dart';
class GameCardWidget extends StatefulWidget {
static const borderRadius = 32.0;

View file

@ -1,117 +0,0 @@
import 'dart:math';
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class Header extends StatelessWidget {
final String? title;
final String? subtitle;
final bool hasPopButton;
final String? popText;
final Widget? trail;
final Color? color;
Header(
this.title, {
this.subtitle,
this.hasPopButton = false,
this.popText,
this.trail,
this.color,
});
@override
Widget build(BuildContext context) {
final topPadding = 74.h -
MediaQuery.of(context).padding.top -
(hasPopButton ? 30.0.h : 0.0);
return Container(
color: color,
alignment: Alignment.centerLeft,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: max(
topPadding / 2,
0,
),
),
if (hasPopButton)
Padding(
padding: EdgeInsets.only(left: 10.0.w),
child: TapRegion(
onTapInside: AutoRouter.of(context).maybePop,
behavior: HitTestBehavior.opaque,
child: SizedBox(
height: 30.h,
child: Row(
children: [
Image.asset(
'icons/back.png',
color:
Theme.of(context).buttonTheme.colorScheme!.primary,
width: 17,
height: 23.h,
),
if (popText != null)
Padding(
padding: const EdgeInsets.only(left: 4.0),
child: Text(
popText!,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
),
),
),
],
),
),
),
),
SizedBox(
height: max(
topPadding / 2,
0,
),
),
Container(
padding: EdgeInsets.only(left: 20.0.w, right: 24.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (title != null)
Text(
title!,
style: TextStyle(
fontSize: 36.sp,
fontWeight: FontWeight.w700,
height: 0.85,
),
textAlign: TextAlign.start,
),
if (trail != null) trail!,
],
),
),
if (subtitle != null)
Container(
padding: EdgeInsets.only(top: 4.0.h, left: 22.0.w, bottom: 4.0.h),
alignment: Alignment.centerLeft,
child: Text(
subtitle!,
style: TextStyle(
fontSize: 20.sp,
fontWeight: FontWeight.w300,
height: 0.85,
),
),
),
],
),
);
}
}

View file

@ -1,44 +0,0 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../theme/themes.dart';
class HorizontalProgressWidget extends StatelessWidget {
final int value;
final int length;
final Color color;
final Color? borderColor;
final double? height;
HorizontalProgressWidget(
this.value,
this.length,
this.color, {
this.borderColor,
this.height,
});
@override
Widget build(BuildContext context) {
return Container(
height: height ?? 16.h,
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
color: white,
border: borderColor != null ? Border.all(color: borderColor!) : null,
borderRadius: BorderRadius.circular(16.h),
),
child: AnimatedFractionallySizedBox(
alignment: Alignment.centerLeft,
widthFactor: length == 0 ? 0 : value.toDouble() / length,
duration: Duration(milliseconds: 600),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16.h),
color: color,
),
),
),
);
}
}

View file

@ -1,356 +0,0 @@
import 'package:flutter/widgets.dart';
import 'dart:ui' as ui;
/// Signature used by [ImageFade.errorBuilder] to build the widget that will be displayed
/// if an error occurs while loading an image.
typedef ImageFadeErrorBuilder = Widget Function(
BuildContext context,
Object exception,
);
/// Signature used by [ImageFade.loadingBuilder] to build the widget that will be displayed
/// while an image is loading. `progress` returns a value between 0 and 1 indicating load progress.
typedef ImageFadeLoadingBuilder = Widget Function(
BuildContext context,
double progress,
ImageChunkEvent? chunkEvent,
);
/// A widget that displays a [placeholder] widget while a specified [image] loads,
/// then cross-fades to the loaded image. Can optionally display loading progress
/// and errors.
///
/// If [image] is subsequently changed, it will cross-fade to the new image once it
/// finishes loading.
///
/// Setting [image] to null will cross-fade back to the [placeholder].
///
/// ```dart
/// ImageFade(
/// placeholder: Image.asset('assets/myPlaceholder.png'),
/// image: NetworkImage('https://backend.example.com/image.png'),
/// )
/// ```
class ImageFade extends StatefulWidget {
/// Creates a widget that displays a [placeholder] widget while a specified [image] loads,
/// then cross-fades to the loaded image.
const
ImageFade({
Key? key,
this.placeholder,
this.image,
this.curve = Curves.linear,
this.duration = const Duration(milliseconds: 300),
this.syncDuration,
this.width,
this.height,
this.scale = 1,
this.fit = BoxFit.scaleDown,
this.alignment = Alignment.center,
this.repeat = ImageRepeat.noRepeat,
this.matchTextDirection = false,
this.excludeFromSemantics = false,
this.semanticLabel,
this.loadingBuilder,
this.errorBuilder,
}) : super(key: key);
/// Widget layered behind the loaded images. Displayed when [image] is null or is loading initially.
final Widget? placeholder;
/// The image to display. Subsequently changing the image will fade the new image over the previous one.
final ImageProvider? image;
/// The curve of the fade-in animation.
final Curve curve;
/// The duration of the fade-in animation.
final Duration duration;
/// An optional duration for fading in a synchronously loaded image (ex. from memory), error, or placeholder.
/// For example, you could set this to `Duration.zero` to immediately display images that are already loaded.
/// If omitted, [duration] will be used.
final Duration? syncDuration;
/// The width to display at. See [Image.width] for more information.
final double? width;
/// The height to display at. See [Image.height] for more information.
final double? height;
/// The scale factor for drawing this image at its intended size. See [RawImage.scale] for more information.
final double scale;
/// How to draw the image within its bounds. Defaults to [BoxFit.scaleDown]. See [Image.fit] for more information.
final BoxFit fit;
/// How to align the image within its bounds. See [Image.alignment] for more information.
final Alignment alignment;
/// How to paint any portions of the layout bounds not covered by the image. See [Image.repeat] for more information.
final ImageRepeat repeat;
/// Whether to paint the image in the direction of the [TextDirection]. See [Image.matchTextDirection] for more information.
final bool matchTextDirection;
/// Whether to exclude this image from semantics. See [Image.excludeFromSemantics] for more information.
final bool excludeFromSemantics;
/// A Semantic description of the image. See [Image.semanticLabel] for more information.
final String? semanticLabel;
/// A builder that specifies the widget to display while an image is loading.
/// See [ImageFadeLoadingBuilder] for more information.
final ImageFadeLoadingBuilder? loadingBuilder;
/// A builder that specifies the widget to display if an error occurs while an image is loading.
/// This will be faded in over previous content, so you may want to set an opaque background on it.
final ImageFadeErrorBuilder? errorBuilder;
@override
State<StatefulWidget> createState() => _ImageFadeState();
}
class _ImageFadeState extends State<ImageFade> with TickerProviderStateMixin {
_ImageResolver? _resolver;
Widget? _front;
Widget? _back;
late final AnimationController _controller;
Widget? _fadeFront;
Widget? _fadeBack;
bool? _sync; // could use onImage synchronousCall, but this is more forgiving
bool _shouldBuildFront = false;
@override
void initState() {
_controller = AnimationController(vsync: this);
super.initState();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
// Can't call this in initState because createLocalImageConfiguration throws errors:
_update(context);
}
@override
void didUpdateWidget(ImageFade old) {
// not called on init
super.didUpdateWidget(old);
_update(context, old);
}
void _update(BuildContext context, [ImageFade? old]) {
final ImageProvider? image = widget.image;
final ImageProvider? oldImage = old?.image;
if (image == oldImage) return;
_back = null;
_shouldBuildFront = false;
if (_resolver != null) {
// move previous loaded image to back & cancel any active loads.
if (_resolver!.complete) _back = _fadeBack = _front;
_resolver!.dispose();
}
// load the new image:
_front = _sync = null;
_resolver = image == null
? null
: _ImageResolver(
image,
context,
onError: _handleComplete,
onComplete: _handleComplete,
width: widget.width,
height: widget.height,
);
// start transition to placeholder if there's no new image:
if (_back != null && _resolver == null) _buildTransition();
}
void _handleComplete(_ImageResolver resolver) {
if (_sync == null) _sync = true;
// defer building the front content until build so we have an active context.
setState(() => _shouldBuildFront = true);
}
void _buildFront(BuildContext context) {
_shouldBuildFront = false;
_ImageResolver resolver = _resolver!;
_front = resolver.error
? widget.errorBuilder?.call(context, resolver.exception!)
: _getImage(resolver.image);
_buildTransition();
}
void _buildTransition() {
final bool out = _front == null; // no new image
// use the "fast" duration if sync load, error, or placeholder:
bool fast = (_sync != false || _resolver?.error == true || out);
Duration duration = (fast ? widget.syncDuration : null) ?? widget.duration;
// Fade in for duration, out for 1/2 as long:
_controller.duration = duration * (out ? 1 : 3 / 2);
_fadeFront = _buildFade(
child: _front,
opacity: CurvedAnimation(
parent: _controller,
curve: Interval(0.0, 2 / 3, curve: widget.curve),
),
);
_fadeBack = _buildFade(
child: _back,
opacity: Tween<double>(begin: 1.0, end: 0).animate(
CurvedAnimation(
parent: _controller,
curve: Interval(out ? 0.0 : 2 / 3, 1.0),
),
),
);
if (_front != null || _back != null) _controller.forward(from: 0);
}
Widget? _buildFade({Widget? child, required Animation<double> opacity}) {
if (child == null) return null;
// if the child is a loaded image, we can fade its opacity directly for better performance:
return (child is RawImage)
? _getImage(child.image, opacity: opacity)
: FadeTransition(child: child, opacity: opacity);
}
RawImage _getImage(ui.Image? image, {Animation<double>? opacity}) {
return RawImage(
image: image,
width: widget.width,
height: widget.height,
scale: widget.scale,
fit: widget.fit,
alignment: widget.alignment,
repeat: widget.repeat,
matchTextDirection: widget.matchTextDirection,
opacity: opacity,
);
}
@override
Widget build(BuildContext context) {
if (_sync == null) _sync = false;
if (_shouldBuildFront) _buildFront(context);
Widget? front = _fadeFront, back = _fadeBack;
bool inLoad = _resolver != null && !_resolver!.complete;
if (inLoad && widget.loadingBuilder != null) {
_ImageResolver resolver = _resolver!;
front = AnimatedBuilder(
animation: resolver.notifier,
builder: (_, __) => widget.loadingBuilder!(
context,
resolver.notifier.value,
resolver.chunkEvent,
),
);
}
List<Widget> kids = [];
if (widget.placeholder != null) kids.add(widget.placeholder!);
if (back != null) kids.add(back);
if (front != null) kids.add(front);
Widget content = Container(
width: widget.width,
height: widget.height,
child: kids.isEmpty
? null
: Stack(fit: StackFit.passthrough, children: kids),
);
if (widget.excludeFromSemantics) return content;
String? label = widget.semanticLabel;
return Semantics(
container: label != null,
image: true,
label: label ?? "",
child: content,
);
}
@override
void dispose() {
_resolver?.dispose();
_controller.dispose();
super.dispose();
}
}
// Simplifies working with image loading events and states.
class _ImageResolver {
_ImageResolver(
ImageProvider provider,
BuildContext context, {
required this.onComplete,
required this.onError,
double? width,
double? height,
}) {
Size? size = width != null && height != null ? Size(width, height) : null;
ImageConfiguration config =
createLocalImageConfiguration(context, size: size);
_listener = ImageStreamListener(_handleComplete,
onChunk: _handleProgress, onError: _handleError);
_stream = provider.resolve(config);
_stream.addListener(_listener); // Called sync if already completed.
notifier = ValueNotifier(0);
}
Object? exception;
ImageChunkEvent? chunkEvent;
late final ValueNotifier<double> notifier;
final Function(_ImageResolver resolver) onComplete;
final Function(_ImageResolver resolver) onError;
late final ImageStream _stream;
late final ImageStreamListener _listener;
ImageInfo? _imageInfo;
bool _complete = false;
ui.Image? get image => _imageInfo?.image;
bool get complete => _complete;
bool get error => exception != null;
void _handleComplete(ImageInfo imageInfo, bool sync) {
_imageInfo = imageInfo;
_complete = true;
onComplete(this);
}
void _handleProgress(ImageChunkEvent event) {
chunkEvent = event;
notifier.value = event.expectedTotalBytes != null
? event.cumulativeBytesLoaded / event.expectedTotalBytes!
: 0.0;
}
void _handleError(Object exc, StackTrace? _) {
exception = exc;
_complete = true;
onError(this);
}
void dispose() {
_stream.removeListener(_listener);
}
}

View file

@ -1,83 +0,0 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
import 'package:mnemo_cards/utils/string_helper.dart';
class MnemoText extends StatelessWidget {
final String? mnemo;
final TextStyle? textStyle;
final TextAlign? textAlign;
final Color? color;
final AutoSizeGroup? group;
final int? maxLines;
final bool? softWrap;
const MnemoText(
this.mnemo, {
this.textStyle,
this.textAlign,
this.color,
this.group,
this.maxLines,
this.softWrap,
super.key,
});
@override
Widget build(BuildContext context) {
if (mnemo == null) return SizedBox.shrink();
final matches = RegExp(r'\{(.*?)\}').allMatches(mnemo!);
Color? color = this.color;
color ??= Colors.red;
List<TextSpan> spans = [];
RegExpMatch? lastMatch;
for (final match in matches) {
var mnemoText = matches.isEmpty
? ''
: mnemo!
.substring(match.start, match.end)
.replaceAll(RegExp(r'[\}\{]'), '');
if (mnemoText.startsWith('#')) {
// #123456mnemo
color = mnemoText.substring(0, 7).asColor;
mnemoText = mnemoText.substring(7);
}
spans.addAll([
if (lastMatch == null)
TextSpan(text: mnemo!.substring(0, match.start), style: textStyle)
else
TextSpan(
text: mnemo!.substring(lastMatch.end, match.start),
style: textStyle),
TextSpan(
text: mnemoText,
style: textStyle?.copyWith(color: color) ?? TextStyle(color: color),
),
]);
lastMatch = match;
}
if (lastMatch == null) {
spans.add(
TextSpan(text: mnemo, style: textStyle),
);
} else {
spans.add(TextSpan(
text: mnemo!.substring(lastMatch.end),
style: textStyle,
));
}
return AutoSizeText.rich(
maxLines: maxLines,
softWrap: softWrap,
TextSpan(
children: spans,
),
textAlign: textAlign ?? TextAlign.center,
group: group,
);
}
}

View file

@ -7,8 +7,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:mnemo_cards/features/packs/pack_manager.dart';
import 'package:mnemo_cards/utils/string_helper.dart';
import 'package:mnemo_cards/widgets/header.dart';
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart';
import '../di/locator.dart';
import '../domain/router/app_router.dart';

View file

@ -3,16 +3,17 @@ import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:mnemo_cards/main.dart';
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart';
import 'package:shared_preferences/shared_preferences.dart';
class SharedPrefButton<T> extends StatefulWidget {
class MobileSharedPrefButton<T> extends StatelessWidget {
final Function(T)? onClick;
final String spKey;
final T Function(T? current)? setOnTap;
final bool Function(T? current)? shouldRebuild;
final Widget Function(T? value, ValueSetter<T> setValue)? builder;
SharedPrefButton({
MobileSharedPrefButton({
this.builder,
required this.spKey,
this.setOnTap,
@ -20,7 +21,7 @@ class SharedPrefButton<T> extends StatefulWidget {
this.shouldRebuild,
});
SharedPrefButton.builder({
MobileSharedPrefButton.builder({
this.builder,
required this.spKey,
this.setOnTap,
@ -29,89 +30,12 @@ class SharedPrefButton<T> extends StatefulWidget {
});
@override
State<StatefulWidget> createState() => _SharedPrefButtonState<T>();
}
class _SharedPrefButtonState<T> extends State<SharedPrefButton<T>> {
SharedPreferences? _sp;
T? value;
StreamSubscription? _streamSubscription;
@override
void dispose() {
_streamSubscription?.cancel();
_streamSubscription = null;
super.dispose();
}
@override
void initState() {
super.initState();
_sp = globalSharedPreferences;
value = valueFromSp;
_streamSubscription =
Stream.periodic(const Duration(milliseconds: 500), (_) => valueFromSp)
.distinct()
.listen((v) {
if (value != v) {
value = v;
if (mounted && widget.shouldRebuild?.call(value) != false) {
setState(() {});
}
}
});
}
T? get valueFromSp {
if (T == bool) {
return (_sp?.getBool(widget.spKey) ?? false).as<T?>();
} else if (T == int) {
return _sp?.getInt(widget.spKey).as<T?>();
} else if (T == num || T == double) {
return _sp?.getDouble(widget.spKey).as<T?>();
} else if (T == String) {
return _sp?.getString(widget.spKey).as<T?>();
} else if (T == Iterable<String>) {
return _sp?.getStringList(widget.spKey).as<T?>();
}
return null;
}
Future<void> setValue(T value) async {
if (T == bool) {
_sp?.setBool(widget.spKey, value as bool);
} else if (T == int) {
_sp?.setInt(widget.spKey, value as int);
} else if (T == num || T == double) {
_sp?.setDouble(widget.spKey, value as double);
} else if (T == String) {
_sp?.setString(widget.spKey, value as String);
} else if (T == Iterable<String>) {
_sp?.setStringList(widget.spKey, (value as Iterable<String>).toList());
}
}
@override
Widget build(BuildContext context) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: widget.setOnTap != null
? () async {
value = widget.setOnTap!.call(value);
if (value != null) {
_setValue(value!);
}
}
: null,
child: widget.builder?.call(value, _setValue),
Widget build(BuildContext context) => SharedPrefButton(
sharedPreferences: globalSharedPreferences,
spKey: spKey,
setOnTap: setOnTap,
onClick: onClick,
shouldRebuild: shouldRebuild,
builder: builder,
);
}
Future<void> _setValue(T value) async {
await setValue(value!);
if (widget.shouldRebuild?.call(value) != false) {
setState(() {});
}
}
}

View file

@ -1,39 +0,0 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class SimpleTile extends StatelessWidget {
final Widget? trail;
final Widget title;
final VoidCallback? onTap;
SimpleTile({
required this.title,
this.trail,
this.onTap,
});
SimpleTile.text({
required String text,
this.trail,
this.onTap,
}) : title = Text(text);
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
height: 50.h,
padding: EdgeInsets.symmetric(vertical: 4.h),
alignment: Alignment.centerLeft,
child: Row(
children: [
Expanded(child: title),
if (trail != null) trail!,
],
),
),
);
}
}

View file

@ -1,208 +0,0 @@
import 'dart:math';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:mnemo_cards/theme/themes.dart';
class MnemoSlider extends StatelessWidget {
final double value;
final void Function(double)? onChanged;
final double min;
final double max;
MnemoSlider({
required this.value,
this.onChanged,
this.min = 0.2,
this.max = 2.0,
super.key,
});
@override
Widget build(BuildContext context) {
var v = this.value;
return Container(
decoration: BoxDecoration(
border: Border.all(color: borderGray),
borderRadius: BorderRadius.circular(12.0),
),
padding: EdgeInsets.symmetric(vertical: 3, horizontal: 2.0),
child: SliderTheme(
data: SliderTheme.of(context).copyWith(
activeTrackColor: Theme.of(context).scaffoldBackgroundColor,
inactiveTrackColor: Theme.of(context).scaffoldBackgroundColor,
thumbColor: progressBlue,
thumbShape: _RectSliderThumbShape(
enabledThumbRadius: 19.0,
borderRadius: Radius.circular(10),
textBuilder: (v) {
final value = ((v * (max - min) + min) * 10).toInt();
if (value % 10 == 0) {
return '${(value / 10).toStringAsFixed(0)}x';
} else {
return (value / 10).toStringAsFixed(1);
}
},
),
trackShape: RoundedRectSliderTrackShape(),
trackHeight: 1.h,
overlayColor: Colors.transparent,
overlappingShapeStrokeColor: Colors.transparent,
overlayShape: RoundSliderOverlayShape(overlayRadius: 2.0),
),
child: StatefulBuilder(builder: (context, setState) {
return Slider(
min: min,
max: max,
value: v,
onChanged: (updated) {
setState(() {
v = updated;
});
onChanged?.call((updated * 100).toInt() / 100.0);
},
);
}),
),
);
}
}
class _RectSliderThumbShape extends SliderComponentShape {
/// Create a slider thumb that draws a Rect.
const _RectSliderThumbShape({
this.enabledThumbRadius = 10.0,
this.disabledThumbRadius,
this.elevation = 1.0,
this.pressedElevation = 6.0,
this.borderRadius,
this.textBuilder,
});
final String Function(double v)? textBuilder;
/// The preferred radius of the round thumb shape when the slider is enabled.
///
/// If it is not provided, then the Material Design default of 10 is used.
final double enabledThumbRadius;
/// The preferred radius of the round thumb shape when the slider is disabled.
///
/// If no disabledRadius is provided, then it is equal to the
/// [enabledThumbRadius]
final double? disabledThumbRadius;
final Radius? borderRadius;
double get _disabledThumbRadius => disabledThumbRadius ?? enabledThumbRadius;
/// The resting elevation adds shadow to the unpressed thumb.
///
/// The default is 1.
///
/// Use 0 for no shadow. The higher the value, the larger the shadow. For
/// example, a value of 12 will create a very large shadow.
///
final double elevation;
/// The pressed elevation adds shadow to the pressed thumb.
///
/// The default is 6.
///
/// Use 0 for no shadow. The higher the value, the larger the shadow. For
/// example, a value of 12 will create a very large shadow.
final double pressedElevation;
@override
Size getPreferredSize(bool isEnabled, bool isDiscrete) {
return Size.fromRadius(
isEnabled == true ? enabledThumbRadius : _disabledThumbRadius);
}
@override
void paint(
PaintingContext context,
Offset center, {
required Animation<double> activationAnimation,
required Animation<double> enableAnimation,
required bool isDiscrete,
required TextPainter labelPainter,
required RenderBox parentBox,
required SliderThemeData sliderTheme,
required TextDirection textDirection,
required double value,
required double textScaleFactor,
required Size sizeWithOverflow,
}) {
final Canvas canvas = context.canvas;
final Tween<double> radiusTween = Tween<double>(
begin: _disabledThumbRadius,
end: enabledThumbRadius,
);
final ColorTween colorTween = ColorTween(
begin: sliderTheme.disabledThumbColor,
end: sliderTheme.thumbColor,
);
final Color color = colorTween.evaluate(enableAnimation)!;
final double radius = radiusTween.evaluate(enableAnimation);
final Tween<double> elevationTween = Tween<double>(
begin: elevation,
end: pressedElevation,
);
final double evaluatedElevation =
elevationTween.evaluate(activationAnimation);
final Path path = Path()
..addArc(
Rect.fromCenter(
center: center, width: 2 * radius, height: 2 * radius),
0,
pi * 2);
bool paintShadows = false;
assert(() {
if (debugDisableShadows) {
paintShadows = false;
}
return true;
}());
if (paintShadows) {
canvas.drawShadow(path, Colors.black, evaluatedElevation, true);
}
final TextPainter textPainter =
TextPainter(textDirection: TextDirection.rtl);
textPainter.text = TextSpan(
text: textBuilder?.call(value) ?? '',
style: TextStyle(
fontSize: radius,
color: Colors.black,
));
textPainter.layout();
final Offset textCenter = Offset(center.dx - (textPainter.width / 2),
center.dy - (textPainter.height / 2));
// Use drawRect instead of drawCircle
canvas.drawRRect(
RRect.fromRectAndRadius(
Rect.fromCircle(center: center, radius: radius),
borderRadius ?? Radius.zero,
),
Paint()..color = borderGray,
);
// Use drawRect instead of drawCircle
canvas.drawRRect(
RRect.fromRectAndRadius(
Rect.fromCircle(center: center, radius: radius - 1),
borderRadius ?? Radius.zero,
),
Paint()..color = color,
);
textPainter.paint(canvas, textCenter);
}
}

View file

@ -1,25 +0,0 @@
import 'package:flutter/material.dart';
import 'package:mnemo_cards/theme/themes.dart';
class SwitchButton extends StatelessWidget {
final bool value;
final void Function(bool)? onChanged;
SwitchButton({
required this.value,
required this.onChanged,
});
@override
Widget build(BuildContext context) {
return Switch(
value: value,
onChanged: onChanged,
activeColor: progressBlue,
activeTrackColor: Theme.of(context).scaffoldBackgroundColor,
inactiveTrackColor: Theme.of(context).scaffoldBackgroundColor,
trackOutlineColor: WidgetStatePropertyAll(borderGray),
inactiveThumbColor: borderGray,
);
}
}

View file

@ -1,28 +0,0 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:mnemo_cards/widgets/switch_button.dart';
class SwitchTile extends StatelessWidget {
final bool value;
final void Function(bool)? onChanged;
final String text;
SwitchTile({
required this.text,
required this.value,
this.onChanged,
});
@override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(child: Text(text, overflow: TextOverflow.ellipsis,)),
SwitchButton(
value: value,
onChanged: onChanged ?? (v) {},
),
],
);
}
}

View file

@ -1,28 +0,0 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class LinkButton extends StatelessWidget {
final VoidCallback? onPressed;
final String text;
LinkButton(this.text, this.onPressed);
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onPressed,
child: Padding(
padding: EdgeInsets.only(top: 2.0.h, bottom: 2.0.h),
child: Text(
text,
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 16,
decoration: TextDecoration.underline,
),
),
),
);
}
}

View file

@ -1,105 +0,0 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
import '../theme/themes.dart';
import 'horizontal_progress.dart';
class UserStatistics extends StatelessWidget {
final UserDataDto? userDataDto;
UserStatistics(this.userDataDto);
@override
Widget build(BuildContext context) {
final words = userDataDto?.allWordsStatistics?.words ?? [];
words.sort(
(p, n) => (n.correct / n.total).compareTo(p.correct / p.total),
);
final learnedWords = words
.where((w) => w.correct > 10 && w.correct / w.total > 0.9)
.map((e) => e.word)
.toSet();
final learnedWordsLength = learnedWords.length;
final lastDigit = learnedWordsLength % 10;
final String? totalWordsText;
if (learnedWordsLength == 0) {
totalWordsText = 'Словарик';
} else if (lastDigit == 0 || lastDigit >= 5) {
totalWordsText = 'Ты уже знаешь $learnedWordsLength слов!';
} else if (lastDigit == 1) {
totalWordsText = 'Ты уже знаешь $learnedWordsLength слово!';
} else {
totalWordsText = 'Ты уже знаешь $learnedWordsLength слова!';
}
return Column(
children: [
Padding(
padding: EdgeInsets.only(bottom: 8.0.h, top: 8.0.h),
child: Row(
children: [
Text(
totalWordsText,
style: TextStyle(
fontSize: 18,
),
maxLines: 1,
),
],
),
),
Expanded(
child: Container(
decoration: BoxDecoration(
border: Border.all(color: borderGray),
borderRadius: BorderRadius.circular(12),
),
padding: EdgeInsets.symmetric(horizontal: 8.0.w),
child: words.isEmpty
? Center(
child: AutoSizeText('Тут будут все пройденные слова'),
)
: ListView.builder(
itemCount: words.length,
shrinkWrap: true,
itemBuilder: (context, index) {
final word = words[index];
return Padding(
padding: EdgeInsets.symmetric(vertical: 2.0.h),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${word.word}',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w700,
),
),
Flexible(
child: SizedBox(
width: MediaQuery.of(context).size.width / 2.5,
child: HorizontalProgressWidget(
(word.correct * 100).floor(),
(word.total * 100).floor(),
learnedWords.contains(word.word)
? golden
: progressBlue,
borderColor: borderGray,
height: 20.h,
),
),
),
],
),
);
},
),
),
),
],
);
}
}

View file

@ -37,10 +37,10 @@ packages:
dependency: "direct main"
description:
name: archive
sha256: ecf4273855368121b1caed0d10d4513c7241dfc813f7d3c8933b36622ae9b265
sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d
url: "https://pub.dev"
source: hosted
version: "3.5.1"
version: "3.6.1"
args:
dependency: transitive
description:
@ -61,18 +61,18 @@ packages:
dependency: "direct main"
description:
name: auto_route
sha256: "6cad3f408863ffff2b5757967c802b18415dac4acb1b40c5cdd45d0a26e5080f"
sha256: a9001a90539ca3effc168f7e1029a5885c7326b9032c09ac895e303c1d137704
url: "https://pub.dev"
source: hosted
version: "8.1.3"
version: "8.3.0"
auto_route_generator:
dependency: "direct dev"
description:
name: auto_route_generator
sha256: ba28133d3a3bf0a66772bcc98dade5843753cd9f1a8fb4802b842895515b67d3
sha256: a21d7a936c917488653c972f62d884d8adcf8c5d37acc7cd24da33cf784546c0
url: "https://pub.dev"
source: hosted
version: "8.0.0"
version: "8.1.0"
auto_size_text:
dependency: "direct main"
description:
@ -109,10 +109,10 @@ packages:
dependency: transitive
description:
name: build_daemon
sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1"
sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9"
url: "https://pub.dev"
source: hosted
version: "4.0.1"
version: "4.0.2"
build_resolvers:
dependency: transitive
description:
@ -125,18 +125,18 @@ packages:
dependency: "direct dev"
description:
name: build_runner
sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22"
sha256: "644dc98a0f179b872f612d3eb627924b578897c629788e858157fa5e704ca0c7"
url: "https://pub.dev"
source: hosted
version: "2.4.9"
version: "2.4.11"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799"
sha256: e3c79f69a64bdfcd8a776a3c28db4eb6e3fb5356d013ae5eb2e52007706d5dbe
url: "https://pub.dev"
source: hosted
version: "7.3.0"
version: "7.3.1"
built_collection:
dependency: transitive
description:
@ -189,26 +189,26 @@ packages:
dependency: "direct main"
description:
name: cloud_firestore
sha256: e461ea9ab23959102a780efcbccfe33c2ac46269928bc57093bbc0b526afc801
sha256: a0f161b92610e078b4962d7e6ebeb66dc9cce0ada3514aeee442f68165d78185
url: "https://pub.dev"
source: hosted
version: "4.17.3"
version: "4.17.5"
cloud_firestore_platform_interface:
dependency: transitive
description:
name: cloud_firestore_platform_interface
sha256: "2e0b8db9a759ffc71086019f1bd27237e5e888ab1e99c507067ff8616acdfa24"
sha256: "6a55b319f8d33c307396b9104512e8130a61904528ab7bd8b5402678fca54b81"
url: "https://pub.dev"
source: hosted
version: "6.2.3"
version: "6.2.5"
cloud_firestore_web:
dependency: transitive
description:
name: cloud_firestore_web
sha256: "37b6974bef5b0a7ecd31037ffb7d7bfe6bb9d2ac6c064fbea395411ef0a64d55"
sha256: "89dfa1304d3da48b3039abbb2865e3d30896ef858e569a16804a99f4362283a9"
url: "https://pub.dev"
source: hosted
version: "3.12.3"
version: "3.12.5"
code_builder:
dependency: transitive
description:
@ -309,10 +309,18 @@ packages:
dependency: "direct main"
description:
name: dio
sha256: "11e40df547d418cc0c4900a9318b26304e665da6fa4755399a9ff9efd09034b5"
sha256: e17f6b3097b8c51b72c74c9f071a605c47bcc8893839bd66732457a5ebe73714
url: "https://pub.dev"
source: hosted
version: "5.4.3+1"
version: "5.5.0+1"
dio_web_adapter:
dependency: transitive
description:
name: dio_web_adapter
sha256: "36c5b2d79eb17cdae41e974b7a8284fec631651d2a6f39a8a2ff22327e90aeac"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
dot_navigation_bar:
dependency: "direct main"
description:
@ -357,10 +365,10 @@ packages:
dependency: "direct main"
description:
name: file_picker
sha256: "29c90806ac5f5fb896547720b73b17ee9aed9bba540dc5d91fe29f8c5745b10a"
sha256: "824f5b9f389bfc4dddac3dea76cd70c51092d9dff0b2ece7ef4f53db8547d258"
url: "https://pub.dev"
source: hosted
version: "8.0.3"
version: "8.0.6"
firebase_analytics:
dependency: "direct main"
description:
@ -397,18 +405,18 @@ packages:
dependency: transitive
description:
name: firebase_core_platform_interface
sha256: c437ae5d17e6b5cc7981cf6fd458a5db4d12979905f9aafd1fea930428a9fe63
sha256: "1003a5a03a61fc9a22ef49f37cbcb9e46c86313a7b2e7029b9390cf8c6fc32cb"
url: "https://pub.dev"
source: hosted
version: "5.0.0"
version: "5.1.0"
firebase_core_web:
dependency: transitive
description:
name: firebase_core_web
sha256: "43d9e951ac52b87ae9cc38ecdcca1e8fa7b52a1dd26a96085ba41ce5108db8e9"
sha256: "23509cb3cddfb3c910c143279ac3f07f06d3120f7d835e4a5d4b42558e978712"
url: "https://pub.dev"
source: hosted
version: "2.17.0"
version: "2.17.3"
firebase_crashlytics:
dependency: "direct main"
description:
@ -466,34 +474,42 @@ packages:
dependency: "direct dev"
description:
name: flutter_lints
sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04
sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c"
url: "https://pub.dev"
source: hosted
version: "2.0.3"
version: "4.0.0"
flutter_native_splash:
dependency: "direct dev"
description:
name: flutter_native_splash
sha256: edf39bcf4d74aca1eb2c1e43c3e445fd9f494013df7f0da752fefe72020eedc0
sha256: aa06fec78de2190f3db4319dd60fdc8d12b2626e93ef9828633928c2dcaea840
url: "https://pub.dev"
source: hosted
version: "2.4.0"
version: "2.4.1"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
sha256: "8cf40eebf5dec866a6d1956ad7b4f7016e6c0cc69847ab946833b7d43743809f"
sha256: c6b0b4c05c458e1c01ad9bcc14041dd7b1f6783d487be4386f793f47a8a4d03e
url: "https://pub.dev"
source: hosted
version: "2.0.19"
version: "2.0.20"
flutter_rustore_billing:
dependency: "direct main"
description:
name: flutter_rustore_billing
sha256: "95905fec5ab46f7d0e798469c847423aa4dac07461a6111892be3f34c48a5b75"
url: "https://pub.dev"
source: hosted
version: "6.0.1"
flutter_screenutil:
dependency: "direct main"
description:
name: flutter_screenutil
sha256: "8cf100b8e4973dc570b6415a2090b0bfaa8756ad333db46939efc3e774ee100d"
sha256: "8239210dd68bee6b0577aa4a090890342d04a136ce1c81f98ee513fc0ce891de"
url: "https://pub.dev"
source: hosted
version: "5.9.0"
version: "5.9.3"
flutter_secure_storage:
dependency: "direct main"
description:
@ -572,10 +588,10 @@ packages:
dependency: "direct main"
description:
name: freezed_annotation
sha256: c3fd9336eb55a38cc1bbd79ab17573113a8deccd0ecbbf926cca3c62803b5c2d
sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2
url: "https://pub.dev"
source: hosted
version: "2.4.1"
version: "2.4.4"
frontend_server_client:
dependency: transitive
description:
@ -600,6 +616,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.2"
go_router:
dependency: transitive
description:
name: go_router
sha256: b465e99ce64ba75e61c8c0ce3d87b66d8ac07f0b35d0a7e0263fcfc10f99e836
url: "https://pub.dev"
source: hosted
version: "13.2.5"
google_identity_services_web:
dependency: transitive
description:
@ -620,10 +644,10 @@ packages:
dependency: transitive
description:
name: google_sign_in_android
sha256: "7647893c65e6720973f0e579051c8f84b877b486614d9f70a404259c41a4632e"
sha256: d30fb34b659679ea74397e9748b4ab5d720720d57dcc79538f1b3c4a68654cb3
url: "https://pub.dev"
source: hosted
version: "6.1.23"
version: "6.1.27"
google_sign_in_ios:
dependency: transitive
description:
@ -644,18 +668,18 @@ packages:
dependency: transitive
description:
name: google_sign_in_web
sha256: fc0f14ed45ea616a6cfb4d1c7534c2221b7092cc4f29a709f0c3053cc3e821bd
sha256: d606264c7a1a526a3aa79d938b85a601d8589731a478bd4a3dcbdeb14a572228
url: "https://pub.dev"
source: hosted
version: "0.12.4"
version: "0.12.4+1"
graphs:
dependency: transitive
description:
name: graphs
sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19
sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0"
url: "https://pub.dev"
source: hosted
version: "2.3.1"
version: "2.3.2"
html:
dependency: transitive
description:
@ -668,12 +692,12 @@ packages:
dependency: transitive
description:
name: http
sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938"
sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
url: "https://pub.dev"
source: hosted
version: "1.2.1"
version: "1.2.2"
http_certificate_pinning:
dependency: "direct main"
dependency: transitive
description:
name: http_certificate_pinning
sha256: "12b4848113d50c570af93e94bed64004caba14dde87719310d1e4556686c0afa"
@ -700,10 +724,10 @@ packages:
dependency: transitive
description:
name: image
sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e"
sha256: "2237616a36c0d69aef7549ab439b833fb7f9fb9fc861af2cc9ac3eedddd69ca8"
url: "https://pub.dev"
source: hosted
version: "4.1.7"
version: "4.2.0"
in_app_purchase:
dependency: "direct main"
description:
@ -716,10 +740,10 @@ packages:
dependency: transitive
description:
name: in_app_purchase_android
sha256: "25b4bc74d4d990c18a889ea9486cb9029285955f38ca81ff335c8936edf9e66d"
sha256: f3f3ded26d08d13383b8c9952d1cf1422f0a3440b04d27f9838adee86f2ce899
url: "https://pub.dev"
source: hosted
version: "0.3.5"
version: "0.3.6+2"
in_app_purchase_platform_interface:
dependency: transitive
description:
@ -732,18 +756,18 @@ packages:
dependency: transitive
description:
name: in_app_purchase_storekit
sha256: c13e4fee493dff3e956ebd24f80656f5ddbf876a8d12817d6fbe52d90e7c2068
sha256: "187f12ba8030993d117d11d1a16d3607734140d572ce1ede088bfcec960b65df"
url: "https://pub.dev"
source: hosted
version: "0.3.15"
version: "0.3.17+1"
injectable:
dependency: transitive
description:
name: injectable
sha256: fb722c86cf8233008e4db41c696a6145721f45dc8aeba91103e3128c3d63c9c6
sha256: "3c8355a29d11ff28c0311bed754649761f345ef7a13ff66a714380954af51226"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
version: "2.4.2"
injectable_generator:
dependency: "direct dev"
description:
@ -764,10 +788,10 @@ packages:
dependency: "direct main"
description:
name: jailbreak_root_detection
sha256: "03c7bb8ba1c12ea2b75400b152e89ae8cf50abf0c88777a5e564af4a04aeebf2"
sha256: "7dd3418d1784892dbcb6cc25df53679aa7717a2d007b34f59d570ae2ec6edfa6"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
version: "1.1.4"
js:
dependency: transitive
description:
@ -820,10 +844,10 @@ packages:
dependency: transitive
description:
name: lints
sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "4.0.0"
logging:
dependency: transitive
description:
@ -871,6 +895,13 @@ packages:
relative: true
source: path
version: "0.0.1"
mnemo_cards_frontend_common:
dependency: "direct main"
description:
path: "../mnemo_cards_frontend_common"
relative: true
source: path
version: "0.0.1"
nested:
dependency: transitive
description:
@ -883,10 +914,10 @@ packages:
dependency: "direct main"
description:
name: no_screenshot
sha256: c8621208e3e01e5b9c6d8f4611241465436618ffd3da8340bd533f8241a33fc5
sha256: "8415601932f84d103edda5378cacd0e10c8d8ca40de4dd8ad0496d1eee71107a"
url: "https://pub.dev"
source: hosted
version: "0.0.1+6"
version: "0.0.1+7"
package_config:
dependency: transitive
description:
@ -931,10 +962,10 @@ packages:
dependency: transitive
description:
name: path_provider_android
sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d
sha256: "30c5aa827a6ae95ce2853cdc5fe3971daaac00f6f081c419c013f7f57bff2f5e"
url: "https://pub.dev"
source: hosted
version: "2.2.4"
version: "2.2.7"
path_provider_foundation:
dependency: transitive
description:
@ -963,10 +994,10 @@ packages:
dependency: transitive
description:
name: path_provider_windows
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
url: "https://pub.dev"
source: hosted
version: "2.2.1"
version: "2.3.0"
petitparser:
dependency: transitive
description:
@ -979,10 +1010,10 @@ packages:
dependency: transitive
description:
name: platform
sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
url: "https://pub.dev"
source: hosted
version: "3.1.4"
version: "3.1.5"
plugin_platform_interface:
dependency: transitive
description:
@ -1019,10 +1050,10 @@ packages:
dependency: transitive
description:
name: pubspec_parse
sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367
sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8
url: "https://pub.dev"
source: hosted
version: "1.2.3"
version: "1.3.0"
recase:
dependency: transitive
description:
@ -1051,10 +1082,10 @@ packages:
dependency: "direct main"
description:
name: rxdart
sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb"
sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962"
url: "https://pub.dev"
source: hosted
version: "0.27.7"
version: "0.28.0"
shared_preferences:
dependency: "direct main"
description:
@ -1067,10 +1098,10 @@ packages:
dependency: transitive
description:
name: shared_preferences_android
sha256: "1ee8bf911094a1b592de7ab29add6f826a7331fb854273d55918693d5364a1f2"
sha256: "93d0ec9dd902d85f326068e6a899487d1f65ffcd5798721a95330b26c8131577"
url: "https://pub.dev"
source: hosted
version: "2.2.2"
version: "2.2.3"
shared_preferences_foundation:
dependency: transitive
description:
@ -1091,10 +1122,10 @@ packages:
dependency: transitive
description:
name: shared_preferences_platform_interface
sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b"
sha256: "034650b71e73629ca08a0bd789fd1d83cc63c2d1e405946f7cef7bc37432f93a"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
version: "2.4.0"
shared_preferences_web:
dependency: transitive
description:
@ -1123,10 +1154,10 @@ packages:
dependency: transitive
description:
name: shelf_web_socket
sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1"
sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611"
url: "https://pub.dev"
source: hosted
version: "1.0.4"
version: "2.0.0"
shimmer:
dependency: "direct main"
description:
@ -1248,26 +1279,26 @@ packages:
dependency: "direct main"
description:
name: url_launcher
sha256: "6ce1e04375be4eed30548f10a315826fd933c1e493206eab82eed01f438c8d2e"
sha256: "21b704ce5fa560ea9f3b525b43601c678728ba46725bab9b01187b4831377ed3"
url: "https://pub.dev"
source: hosted
version: "6.2.6"
version: "6.3.0"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
sha256: "360a6ed2027f18b73c8d98e159dda67a61b7f2e0f6ec26e86c3ada33b0621775"
sha256: "95d8027db36a0e52caf55680f91e33ea6aa12a3ce608c90b06f4e429a21067ac"
url: "https://pub.dev"
source: hosted
version: "6.3.1"
version: "6.3.5"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
sha256: "7068716403343f6ba4969b4173cbf3b84fc768042124bc2c011e5d782b24fe89"
sha256: e43b677296fadce447e987a2f519dcf5f6d1e527dc35d01ffab4fff5b8a7063e
url: "https://pub.dev"
source: hosted
version: "6.3.0"
version: "6.3.1"
url_launcher_linux:
dependency: transitive
description:
@ -1348,30 +1379,38 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.5.1"
web_socket:
dependency: transitive
description:
name: web_socket
sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
url: "https://pub.dev"
source: hosted
version: "0.1.6"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42"
sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f"
url: "https://pub.dev"
source: hosted
version: "2.4.5"
version: "3.0.1"
webview_flutter:
dependency: "direct main"
description:
name: webview_flutter
sha256: "25e1b6e839e8cbfbd708abc6f85ed09d1727e24e08e08c6b8590d7c65c9a8932"
sha256: "6869c8786d179f929144b4a1f86e09ac0eddfe475984951ea6c634774c16b522"
url: "https://pub.dev"
source: hosted
version: "4.7.0"
version: "4.8.0"
webview_flutter_android:
dependency: transitive
description:
name: webview_flutter_android
sha256: dad3313c9ead95517bb1cae5e1c9d20ba83729d5a59e5e83c0a2d66203f27f91
sha256: f42447ca49523f11d8f70abea55ea211b3cafe172dd7a0e7ac007bb35dd356dc
url: "https://pub.dev"
source: hosted
version: "3.16.1"
version: "3.16.4"
webview_flutter_platform_interface:
dependency: transitive
description:
@ -1384,18 +1423,18 @@ packages:
dependency: transitive
description:
name: webview_flutter_wkwebview
sha256: f12f8d8a99784b863e8b85e4a9a5e3cf1839d6803d2c0c3e0533a8f3c5a992a7
sha256: "9c62cc46fa4f2d41e10ab81014c1de470a6c6f26051a2de32111b2ee55287feb"
url: "https://pub.dev"
source: hosted
version: "3.13.0"
version: "3.14.0"
win32:
dependency: transitive
description:
name: win32
sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb"
sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4
url: "https://pub.dev"
source: hosted
version: "5.5.0"
version: "5.5.1"
win32_registry:
dependency: transitive
description:
@ -1432,10 +1471,10 @@ packages:
dependency: "direct main"
description:
name: yandex_mobileads
sha256: "3c98919a96edb1c30f6407df1f599cf6ca24eb8e45e88477117d71ff3ee73147"
sha256: "836d886d1eab1e42dee819415e0d5320bb3003632b879a470a2190ec0b940e36"
url: "https://pub.dev"
source: hosted
version: "6.3.0"
version: "7.1.0"
yookassa_client:
dependency: "direct main"
description:
@ -1445,5 +1484,5 @@ packages:
source: hosted
version: "1.0.2"
sdks:
dart: ">=3.3.0 <4.0.0"
flutter: ">=3.19.0"
dart: ">=3.4.0 <4.0.0"
flutter: ">=3.22.0"

View file

@ -2,7 +2,7 @@ name: mnemo_cards
description: Mnemo
publish_to: 'none'
version: 1.0.1
version: 1.0.1+11
environment:
sdk: '>=3.1.3 <4.0.0'
@ -14,6 +14,11 @@ dependencies:
mnemo_cards_common:
path: ../mnemo_cards_common
mnemo_cards_frontend_common:
path: ../mnemo_cards_frontend_common
get_it:
shared_preferences: ^2.2.3
flutter_secure_storage: ^9.2.2
@ -37,7 +42,7 @@ dependencies:
google_sign_in:
device_info_plus: ^9.1.2
package_info_plus: ^8.0.0
yandex_mobileads: ^6.1.0
yandex_mobileads: ^7.1.0
firebase_crashlytics: ^3.5.7
firebase_analytics: ^10.10.7
in_app_purchase: ^3.2.0
@ -56,11 +61,10 @@ dependencies:
no_screenshot: ^0.0.1+6
file_picker:
http_certificate_pinning: ^2.1.3
url_launcher: ^6.2.6
# requests_inspector: ^4.0.3
# RUSTORE
flutter_rustore_billing: ^6.0.1
collection: any
freezed_annotation: any
@ -71,7 +75,7 @@ dev_dependencies:
auto_route_generator:
build_runner:
injectable_generator:
flutter_lints: ^2.0.0
flutter_lints: ^4.0.0
flutter_launcher_icons: ^0.13.1
flutter_native_splash: ^2.4.0