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

View file

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

View file

@ -21,6 +21,14 @@
"certificate_hash": "6df4f259b9c53ac01472c17df69e8c91494f3947" "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_id": "701767851968-vqvjgf1u79jg924inm25bfuv4dg41t2u.apps.googleusercontent.com",
"client_type": 1, "client_type": 1,

View file

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

View file

@ -12,6 +12,7 @@
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
android:launchMode="singleTop" android:launchMode="singleTop"
android:taskAffinity=""
android:theme="@style/LaunchTheme" android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
@ -35,9 +36,15 @@
android:name="flutterEmbedding" android:name="flutterEmbedding"
android:value="2" /> android:value="2" />
</application> </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> <queries>
<intent> <intent>
<action android:name="android.intent.action.TTS_SERVICE" /> <action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
</intent> </intent>
</queries> </queries>
</manifest> </manifest>

View file

@ -2,5 +2,4 @@ package com.cinnabarflower.mnemo_cards
import io.flutter.embedding.android.FlutterActivity 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 { allprojects {
repositories { repositories {
google() google()
@ -22,12 +5,12 @@ allprojects {
} }
} }
rootProject.buildDir = '../build' rootProject.buildDir = "../build"
subprojects { subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}" project.buildDir = "${rootProject.buildDir}/${project.name}"
} }
subprojects { subprojects {
project.evaluationDependsOn(':app') project.evaluationDependsOn(":app")
} }
tasks.register("clean", Delete) { tasks.register("clean", Delete) {

View file

@ -1,3 +1,3 @@
org.gradle.jvmargs=-Xmx4608M org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=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") def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties" assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath return flutterSdkPath
} }()
settings.ext.flutterSdkPath = flutterSdkPath()
includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
plugins { repositories {
id "dev.flutter.flutter-plugin-loader" version "1.0.0" google()
id "com.android.application" version "7.3.0" apply false mavenCentral()
// START: FlutterFire Configuration gradlePluginPortal()
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
} }
// For yoo kassa // 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 */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
10510DEBDDA8DED725BA2250 /* [CP] Copy Pods Resources */, 10510DEBDDA8DED725BA2250 /* [CP] Copy Pods Resources */,
EAA1E3F7E6B26606E42D1048 /* [CP] Embed Pods Frameworks */, EAA1E3F7E6B26606E42D1048 /* [CP] Embed Pods Frameworks */,
CDC7B89EEA6C1D8D4DC3C75E /* FlutterFire: "flutterfire upload-crashlytics-symbols" */,
); );
buildRules = ( buildRules = (
); );
@ -345,6 +346,24 @@
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
}; };
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 */ = { EAA1E3F7E6B26606E42D1048 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;

View file

@ -5,11 +5,10 @@ import 'package:flutter/material.dart';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:mnemo_cards/admin/admin_api.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_common/mnemo_cards_common.dart';
import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart';
import '../main.dart'; import '../main.dart';
import '../widgets/image_fade.dart';
class EditCard { class EditCard {
GameCardDto gameCardDto; GameCardDto gameCardDto;

View file

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

View file

@ -4,8 +4,8 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:mnemo_cards/admin/admin_api.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_common/mnemo_cards_common.dart';
import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart';
import '../main.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/di/injector.dart';
import 'package:mnemo_cards/managers/repository/dio_provider.dart'; import 'package:mnemo_cards/managers/repository/dio_provider.dart';
import 'package:mnemo_cards/theme/themes.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_common/mnemo_cards_common.dart';
import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart';
import '../main.dart'; import '../main.dart';
import 'add_package.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/injector.dart';
import 'package:mnemo_cards/di/locator.dart'; import 'package:mnemo_cards/di/locator.dart';
import 'package:mnemo_cards/managers/repository/dio_provider.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_common/mnemo_cards_common.dart';
import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart';
import '../main.dart'; import '../main.dart';

View file

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

View file

@ -1,6 +1,11 @@
import 'package:mnemo_cards/main.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';
const ADMIN_BUILD = false; const ADMIN_BUILD = CommonPrefs.ADMIN_BUILD;
const RUSTORE_BUILD = CommonPrefs.RUSTORE_BUILD;
final androidPaymentSystem = CommonPrefs.androidPaymentSystem;
bool get SHOW_ADMIN => 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, theme: theme2,
darkTheme: darkTheme2, darkTheme: darkTheme2,
themeMode: themeMode, 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, scaffoldMessengerKey: scaffoldMessengerKey,
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
); );
@ -199,6 +209,13 @@ class _MainTabsPageState extends State<MainTabsPage> {
_Tabs _currentTab = _Tabs.home; _Tabs _currentTab = _Tabs.home;
@override
void didChangeDependencies() {
super.didChangeDependencies();
print('CHANGE DEPS');
}
@override @override
void initState() { void initState() {
super.initState(); super.initState();

View file

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

View file

@ -39,7 +39,7 @@ class AuthPage extends StatelessWidget {
), ),
), ),
if (ADMIN_BUILD) if (ADMIN_BUILD)
SharedPrefButton<bool>( MobileSharedPrefButton<bool>(
builder: (v, _) => (v ?? false) ? Text('PROD') : Text('TEST'), builder: (v, _) => (v ?? false) ? Text('PROD') : Text('TEST'),
spKey: 'env', spKey: 'env',
setOnTap: (v) => !(v ?? false), 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/domain/router/app_router.gr.dart';
import 'package:mnemo_cards/flags.dart'; import 'package:mnemo_cards/flags.dart';
import 'package:mnemo_cards/main.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:shared_preferences/shared_preferences.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
@ -16,11 +16,8 @@ import '../admin/all_users.dart';
import '../di/locator.dart'; import '../di/locator.dart';
import '../features/analytics/analytics.dart'; import '../features/analytics/analytics.dart';
import '../theme/themes.dart'; import '../theme/themes.dart';
import '../widgets/big_back_button.dart';
import '../widgets/enter_promo_code_widget.dart'; import '../widgets/enter_promo_code_widget.dart';
import '../widgets/shared_pref_button.dart'; import '../widgets/shared_pref_button.dart';
import '../widgets/simple_tile.dart';
import '../widgets/user_statistics.dart';
@RoutePage() @RoutePage()
class ProfilePage extends StatelessWidget { class ProfilePage extends StatelessWidget {
@ -106,7 +103,7 @@ class ProfilePage extends StatelessWidget {
), ),
), ),
Expanded( Expanded(
child: SharedPrefButton<bool>( child: MobileSharedPrefButton<bool>(
builder: (v, _) => builder: (v, _) =>
(v ?? false) ? Text('PROD') : Text('TEST'), (v ?? false) ? Text('PROD') : Text('TEST'),
spKey: 'env', spKey: 'env',
@ -140,7 +137,7 @@ class ProfilePage extends StatelessWidget {
height: 5, height: 5,
color: borderGray.withOpacity(0.2), color: borderGray.withOpacity(0.2),
), ),
const EnterPromoCodeWidget(), const MobileEnterPromoCodeWidget(),
Divider( Divider(
height: 5, height: 5,
color: borderGray.withOpacity(0.2), 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/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:mnemo_cards/flags.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:shared_preferences/shared_preferences.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import '../di/locator.dart'; import '../di/locator.dart';
import '../features/analytics/analytics.dart'; import '../features/analytics/analytics.dart';
import '../theme/themes.dart'; import '../theme/themes.dart';
import '../widgets/big_back_button.dart';
import '../widgets/shared_pref_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() @RoutePage()
class SettingsPage extends StatelessWidget { class SettingsPage extends StatelessWidget {
@ -45,7 +40,7 @@ class SettingsPage extends StatelessWidget {
height: 1, height: 1,
color: borderGray.withOpacity(0.2), color: borderGray.withOpacity(0.2),
), ),
SharedPrefButton<bool>.builder( MobileSharedPrefButton<bool>.builder(
spKey: 'dark_theme', spKey: 'dark_theme',
builder: (enabled, _) => AbsorbPointer( builder: (enabled, _) => AbsorbPointer(
child: SwitchTile( child: SwitchTile(
@ -64,7 +59,7 @@ class SettingsPage extends StatelessWidget {
height: 1, height: 1,
color: borderGray.withOpacity(0.2), color: borderGray.withOpacity(0.2),
), ),
SharedPrefButton<bool>.builder( MobileSharedPrefButton<bool>.builder(
spKey: 'sound_on', spKey: 'sound_on',
builder: (enabled, _) => AbsorbPointer( builder: (enabled, _) => AbsorbPointer(
child: SwitchTile( child: SwitchTile(
@ -78,7 +73,7 @@ class SettingsPage extends StatelessWidget {
height: 1, height: 1,
color: borderGray.withOpacity(0.2), color: borderGray.withOpacity(0.2),
), ),
SharedPrefButton<bool>.builder( MobileSharedPrefButton<bool>.builder(
spKey: 'auto_play_sound_view', spKey: 'auto_play_sound_view',
builder: (enabled, _) => AbsorbPointer( builder: (enabled, _) => AbsorbPointer(
child: SwitchTile( child: SwitchTile(
@ -92,7 +87,7 @@ class SettingsPage extends StatelessWidget {
height: 1, height: 1,
color: borderGray.withOpacity(0.2), color: borderGray.withOpacity(0.2),
), ),
SharedPrefButton<bool>.builder( MobileSharedPrefButton<bool>.builder(
spKey: 'auto_play_sound_tests', spKey: 'auto_play_sound_tests',
builder: (enabled, _) => AbsorbPointer( builder: (enabled, _) => AbsorbPointer(
child: SwitchTile( child: SwitchTile(
@ -108,7 +103,7 @@ class SettingsPage extends StatelessWidget {
), ),
SimpleTile.text( SimpleTile.text(
text: 'Скорость чтения', text: 'Скорость чтения',
trail: SharedPrefButton<double>.builder( trail: MobileSharedPrefButton<double>.builder(
spKey: 'sound_speed', spKey: 'sound_speed',
builder: (v, s) => MnemoSlider( builder: (v, s) => MnemoSlider(
onChanged: (value) => s(value), onChanged: (value) => s(value),
@ -122,7 +117,7 @@ class SettingsPage extends StatelessWidget {
color: borderGray.withOpacity(0.2), color: borderGray.withOpacity(0.2),
), ),
if (ADMIN_BUILD) if (ADMIN_BUILD)
SharedPrefButton<bool>.builder( MobileSharedPrefButton<bool>.builder(
spKey: 'show_admin', spKey: 'show_admin',
builder: (enabled, _) => AbsorbPointer( builder: (enabled, _) => AbsorbPointer(
child: SwitchTile( 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:flutter/material.dart';
import 'package:mnemo_cards_common/mnemo_cards_common.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 '../../features/card_swiper/src/card_swiper_controller.dart';
import '../header.dart';
class CardGameHeader extends StatelessWidget { class CardGameHeader extends StatelessWidget {
final CardSwiperController controller; final CardSwiperController controller;

View file

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

View file

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

View file

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:mnemo_cards_common/mnemo_cards_common.dart'; import 'package:mnemo_cards_common/mnemo_cards_common.dart';
import 'package:mnemo_cards/utils/string_helper.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 { class CardPackHeader extends StatelessWidget {
final String? title; final String? title;

View file

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

View file

@ -1,88 +1,21 @@
import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart';
import 'package:mnemo_cards/di/locator.dart';
import 'package:mnemo_cards/theme/themes.dart';
import 'package:mnemo_cards/widgets/simple_tile.dart';
class EnterPromoCodeWidget extends StatefulWidget { import '../di/locator.dart';
const EnterPromoCodeWidget();
@override class MobileEnterPromoCodeWidget extends StatelessWidget {
State<StatefulWidget> createState() => _EnterPromoCodeState();
}
class _EnterPromoCodeState extends State<EnterPromoCodeWidget> {
final TextEditingController controller = TextEditingController();
String? message;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return EnterPromoCodeWidget(onApplyPromoCode: onApplyPromoCode);
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();
}
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/managers/audio_player.dart';
import 'package:mnemo_cards/utils/color_helper.dart'; import 'package:mnemo_cards/utils/color_helper.dart';
import 'package:mnemo_cards_common/mnemo_cards_common.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 'package:yandex_mobileads/mobile_ads.dart';
import '../features/card_flipper/flip_card.dart'; import '../features/card_flipper/flip_card.dart';
import '../features/card_flipper/flip_card_controllers.dart'; import '../features/card_flipper/flip_card_controllers.dart';
import '../features/card_flipper/flip_side.dart'; import '../features/card_flipper/flip_side.dart';
import '../theme/themes.dart'; import '../theme/themes.dart';
import 'image_fade.dart';
import 'mnemo_text.dart';
class GameCardWidget extends StatefulWidget { class GameCardWidget extends StatefulWidget {
static const borderRadius = 32.0; 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:flutter_screenutil/flutter_screenutil.dart';
import 'package:mnemo_cards/features/packs/pack_manager.dart'; import 'package:mnemo_cards/features/packs/pack_manager.dart';
import 'package:mnemo_cards/utils/string_helper.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_common/mnemo_cards_common.dart';
import 'package:mnemo_cards_frontend_common/mnemo_cards_frontend_common.dart';
import '../di/locator.dart'; import '../di/locator.dart';
import '../domain/router/app_router.dart'; import '../domain/router/app_router.dart';

View file

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

View file

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