app
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 12 KiB |
BIN
images/icon.png
Normal file
|
After Width: | Height: | Size: 172 KiB |
|
Before Width: | Height: | Size: 540 KiB After Width: | Height: | Size: 177 KiB |
|
Before Width: | Height: | Size: 890 B After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 4 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 8 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 6 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 6 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 9.8 KiB |
|
|
@ -44,7 +44,7 @@ class _AllCardsState extends State<AllCards> {
|
|||
_cache.clear();
|
||||
while (loadingIds.isNotEmpty) {
|
||||
try {
|
||||
final ids = loadingIds.take(20).toList();
|
||||
final ids = loadingIds.take(10).toList();
|
||||
loadingIds.removeRange(0, ids.length);
|
||||
final cardsPage = await api.getCards(ids);
|
||||
for (final card in cardsPage) {
|
||||
|
|
|
|||
|
|
@ -24,31 +24,45 @@ import '../managers/user_manager.dart';
|
|||
GetIt getIt = GetIt.I;
|
||||
|
||||
Future<void> setInjections() async {
|
||||
getIt.registerSingletonAsync<DioProvider>(
|
||||
() => DioProvider().init(),
|
||||
);
|
||||
final _dio = await DioProvider().init();
|
||||
getIt.registerSingleton<DioProvider>(_dio);
|
||||
print('set dio');
|
||||
|
||||
getIt.registerLazySingleton<HttpRepository>(
|
||||
() => HttpRepository(getIt.get<DioProvider>().dio),
|
||||
);
|
||||
print('set repo');
|
||||
|
||||
getIt.registerLazySingleton<PacksApi>(
|
||||
() => PacksApi(getIt.get<DioProvider>().dio),
|
||||
);
|
||||
if (ADMIN_BUILD)
|
||||
print('set pack');
|
||||
|
||||
if (ADMIN_BUILD) {
|
||||
getIt.registerLazySingleton<AdminApi>(
|
||||
() => AdminApi(getIt.get<DioProvider>().dio),
|
||||
);
|
||||
() => AdminApi(getIt.get<DioProvider>().dio));
|
||||
print('set admin');
|
||||
}
|
||||
|
||||
getIt.registerLazySingleton<PackHolder>(
|
||||
() => PackHolder(),
|
||||
);
|
||||
print('set pack holder');
|
||||
|
||||
getIt.registerLazySingleton<PreviewPackHolder>(
|
||||
() => PreviewPackHolder(),
|
||||
);
|
||||
print('set preview');
|
||||
|
||||
getIt.registerLazySingleton<PackCacheManager>(
|
||||
() => PackCacheManager(),
|
||||
);
|
||||
print('set cache');
|
||||
|
||||
getIt.registerLazySingleton<ImagesHolder>(
|
||||
() => ImagesHolder(),
|
||||
);
|
||||
print('set images holder');
|
||||
|
||||
getIt.registerLazySingleton<PreviewPacksPoller>(
|
||||
() => PreviewPacksPoller(
|
||||
|
|
@ -56,6 +70,7 @@ Future<void> setInjections() async {
|
|||
getIt.get<PreviewPackHolder>(),
|
||||
),
|
||||
);
|
||||
print('set preview poller');
|
||||
|
||||
getIt.registerLazySingleton<PackManager>(
|
||||
() => PackManager(
|
||||
|
|
@ -66,6 +81,7 @@ Future<void> setInjections() async {
|
|||
getIt.get<PackCacheManager>(),
|
||||
),
|
||||
);
|
||||
print('set pack manager');
|
||||
|
||||
getIt.registerLazySingleton<PackUpdater>(
|
||||
() => PackUpdater(
|
||||
|
|
@ -75,28 +91,39 @@ Future<void> setInjections() async {
|
|||
getIt.get<PreviewPackHolder>(),
|
||||
),
|
||||
);
|
||||
print('set pack updater');
|
||||
|
||||
getIt.registerLazySingleton<UserManager>(
|
||||
() => UserManager(
|
||||
getIt.get<HttpRepository>(),
|
||||
),
|
||||
);
|
||||
print('set user manager');
|
||||
|
||||
getIt.registerLazySingleton<FavoriteCardsController>(
|
||||
() => FavoriteCardsController(),
|
||||
);
|
||||
print('set fav cards container');
|
||||
|
||||
getIt.registerLazySingleton<TestManager>(
|
||||
() =>
|
||||
TestManager(getIt.get<PackCacheManager>(), getIt.get<HttpRepository>()),
|
||||
);
|
||||
print('set test manager');
|
||||
|
||||
getIt.registerLazySingleton<InAppPurchaseService>(
|
||||
() => InAppPurchaseService(),
|
||||
);
|
||||
print('set in app');
|
||||
|
||||
getIt.registerLazySingleton(
|
||||
() => PurchaseDetailsStreamSubscription(),
|
||||
);
|
||||
await getIt.allReady();
|
||||
print('set purchase');
|
||||
try {
|
||||
await getIt.allReady();
|
||||
print('injector all ready');
|
||||
} catch (e, s) {
|
||||
print('injector error $e $s');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import 'dart:developer';
|
|||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
|
||||
|
||||
|
|
@ -89,6 +90,9 @@ class PackCacheManager {
|
|||
}
|
||||
|
||||
Future<List<String>> _updateSavedPacks() async {
|
||||
if (kIsWeb) {
|
||||
return [];
|
||||
}
|
||||
final packsDir =
|
||||
Directory('${(await getApplicationDocumentsDirectory()).path}/packs');
|
||||
List<String> savedPackIds = [];
|
||||
|
|
@ -172,7 +176,7 @@ class PackCacheManager {
|
|||
log('Saved ${dto.id}');
|
||||
}
|
||||
|
||||
Future<Directory> _packDirectory(String id) async => Directory(
|
||||
Future<Directory> _packDirectory(String id) async => kIsWeb ? Directory.current : Directory(
|
||||
'${(await getApplicationDocumentsDirectory()).path}/packs/$id',
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,12 @@ import 'dart:async';
|
|||
import 'dart:developer';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:archive/archive.dart';
|
||||
import 'package:archive/archive_io.dart' as a;
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:mnemo_cards/features/packs/packs_api.dart';
|
||||
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import '../../di/locator.dart';
|
||||
import '../../features/packs/pack_cache_manager.dart';
|
||||
import '../../features/packs/pack_holder.dart';
|
||||
import 'preview_pack_holder.dart';
|
||||
|
|
@ -29,7 +30,7 @@ class PackUpdater {
|
|||
);
|
||||
|
||||
Future<void> init() async {
|
||||
final pollStream = Stream.periodic(const Duration(seconds: 60));
|
||||
final pollStream = Stream.periodic(const Duration(seconds: 600));
|
||||
pollStream.listen((event) async {
|
||||
await updateAvailablePacks();
|
||||
try {
|
||||
|
|
@ -108,20 +109,42 @@ class PackUpdater {
|
|||
Future<CardPackDto> updatePackAndSaveImages(String packId) async {
|
||||
try {
|
||||
log('Updating with cards ${packId}', name: 'updatePackWithCards');
|
||||
final s = Stopwatch()..start();
|
||||
final packDto = await _packsApi.getPackDto(packId);
|
||||
print('get from api ${s.elapsedMilliseconds} mills');
|
||||
if (packDto == null) {
|
||||
throw Exception('Cant update pack $packId');
|
||||
}
|
||||
final imagesData = await _packsApi.packImagesArchive(packId);
|
||||
Archive imagesArchive = ZipDecoder().decodeBytes(imagesData);
|
||||
print('get cards from api ${s.elapsedMilliseconds} mills');
|
||||
final userId = locator.userManager.userId!;
|
||||
final appVersion = (await PackageInfo.fromPlatform()).version;
|
||||
final password = TokenGenerator.generateArchivePassword(
|
||||
appVersion: appVersion,
|
||||
packId: packId,
|
||||
);
|
||||
print('generate token ${s.elapsedMilliseconds} mills');
|
||||
final imagesArchive = a.ZipDecoder().decodeBytes(
|
||||
imagesData,
|
||||
password: password,
|
||||
);
|
||||
print('deflate archive ${s.elapsedMilliseconds} mills');
|
||||
Map<String, MemoryImage> images = {};
|
||||
for (final file in imagesArchive) {
|
||||
|
||||
// todo optimize, make really async
|
||||
Future<void> setImage(a.ArchiveFile file) async {
|
||||
final fileData = file.content as List<int>;
|
||||
images[file.name] = MemoryImage(
|
||||
Uint8List.fromList(fileData),
|
||||
);
|
||||
final data = Uint8List.fromList(fileData);
|
||||
images[file.name] = MemoryImage(data);
|
||||
}
|
||||
|
||||
await Future.wait([
|
||||
for (final file in imagesArchive) setImage(file),
|
||||
]);
|
||||
|
||||
print('loaded images ${s.elapsedMilliseconds} mills');
|
||||
_cacheManager.savePack(packDto, images: images);
|
||||
print('saved pack ${s.elapsedMilliseconds} mills');
|
||||
log('Pack ${packId} updated with cards');
|
||||
return packDto;
|
||||
} catch (e, s) {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ class PreviewPacksPoller {
|
|||
PreviewPacksPoller(this._api, this._previewPackHolder);
|
||||
|
||||
Future<void> init() async {
|
||||
Stream.periodic(Duration(seconds: 60), (_) async {
|
||||
Stream.periodic(Duration(seconds: 600), (_) async {
|
||||
poll();
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,10 +124,6 @@ class _ProgressWidgetState extends State<ProgressWidget> {
|
|||
),
|
||||
],
|
||||
),
|
||||
if (kDebugMode)
|
||||
Text(
|
||||
'${(results.where((r) => r != Result.not_visited).length)}/${results.length}',
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -60,7 +60,8 @@ class _TestPageState extends State<TestPage> {
|
|||
print('AUDIO $_lastAudioQuestionId ${question.id}');
|
||||
_lastAudioQuestionId = question.id;
|
||||
try {
|
||||
if (globalSharedPreferences.getBool('auto_play_sound_tests') != false) {
|
||||
if (globalSharedPreferences.getBool('auto_play_sound_tests') !=
|
||||
false) {
|
||||
AudioPlayer.playAudio((question as dynamic).audio);
|
||||
}
|
||||
} catch (e, s) {}
|
||||
|
|
@ -156,39 +157,7 @@ class _TestPageState extends State<TestPage> {
|
|||
TestQuestionType.undefined =>
|
||||
throw UnimplementedError(),
|
||||
};
|
||||
}.let(
|
||||
(builder) => (c, i) =>
|
||||
builder.call(c, i)?.let((w) => kDebugMode
|
||||
? Stack(
|
||||
children: [
|
||||
w,
|
||||
Row(
|
||||
children: [
|
||||
Text('$i'),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
locator.testManager
|
||||
.pageController
|
||||
.animateToPage(
|
||||
locator
|
||||
.testManager
|
||||
.pageController
|
||||
.page!
|
||||
.toInt() +
|
||||
1,
|
||||
duration: Duration(
|
||||
milliseconds: 500),
|
||||
curve: Curves.easeIn,
|
||||
);
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.next_plan_outlined))
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
: w),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import 'dart:math' as m;
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:mnemo_cards/features/packs/images_holder.dart';
|
||||
import 'package:mnemo_cards/theme/themes.dart';
|
||||
|
||||
|
|
@ -95,8 +96,8 @@ class TestImage {
|
|||
if (data.length > 50) {
|
||||
Uint8List? bytes;
|
||||
try {
|
||||
bytes = base64Decode(data);
|
||||
return MemoryImage(bytes);
|
||||
bytes = await compute(base64Decode, data);
|
||||
return MemoryImage(bytes!);
|
||||
} catch (e) {
|
||||
log('Cant decode base64 test image');
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -7,12 +7,15 @@ import 'package:auto_route/auto_route.dart';
|
|||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:jailbreak_root_detection/jailbreak_root_detection.dart';
|
||||
import 'package:mnemo_cards/di/locator.dart';
|
||||
import 'package:mnemo_cards/domain/router/app_router.dart';
|
||||
import 'package:mnemo_cards/managers/repository/certificates.dart';
|
||||
import 'package:mnemo_cards/managers/repository/firebase_config_repository.dart';
|
||||
import 'package:mnemo_cards/features/packs/pack_manager.dart';
|
||||
import 'package:mnemo_cards/theme/themes.dart';
|
||||
|
|
@ -30,6 +33,7 @@ import 'managers/repository/repository.dart';
|
|||
final scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
final scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
|
||||
late final SharedPreferences globalSharedPreferences;
|
||||
late final FlutterSecureStorage secureStorage;
|
||||
|
||||
final appRouter = AppRouter(ADMIN_BUILD);
|
||||
|
||||
|
|
@ -45,7 +49,7 @@ void main() async {
|
|||
SystemChrome.setPreferredOrientations([
|
||||
DeviceOrientation.portraitUp,
|
||||
]);
|
||||
androidDeviceInfo = await DeviceInfoPlugin().androidInfo;
|
||||
androidDeviceInfo = kIsWeb ? null : await DeviceInfoPlugin().androidInfo;
|
||||
print('deviceInfo ${stopwatch.elapsedMilliseconds}');
|
||||
final isNotTrust = await JailbreakRootDetection.instance.isNotTrust;
|
||||
print('No trunst ${stopwatch.elapsedMilliseconds}');
|
||||
|
|
@ -65,7 +69,13 @@ void main() async {
|
|||
};
|
||||
|
||||
try {
|
||||
secureStorage = FlutterSecureStorage(
|
||||
aOptions: const AndroidOptions(
|
||||
encryptedSharedPreferences: true,
|
||||
),
|
||||
);
|
||||
globalSharedPreferences = await SharedPreferences.getInstance();
|
||||
|
||||
await _fillSp();
|
||||
print('global sp ${stopwatch.elapsedMilliseconds}');
|
||||
await setInjections();
|
||||
|
|
@ -81,32 +91,38 @@ void main() async {
|
|||
await locator.previewPackPoller.init();
|
||||
await locator.packUpdater.init();
|
||||
print('initing complete ${stopwatch.elapsedMilliseconds}');
|
||||
await MobileAds.initialize();
|
||||
if (!kIsWeb) await MobileAds.initialize();
|
||||
print('mobile ads ${stopwatch.elapsedMilliseconds}');
|
||||
print('runApp ${stopwatch.elapsedMilliseconds}');
|
||||
runApp(const MyApp());
|
||||
} on Object catch (e, s) {
|
||||
print('err: ${e.toString()}');
|
||||
print('stacktrace: ${s.toString()}');
|
||||
log(e.toString(), stackTrace: s);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _fillSp() async {
|
||||
final showAdmin = globalSharedPreferences.getBool('show_admin');
|
||||
if (showAdmin == null && ADMIN_BUILD) {
|
||||
await globalSharedPreferences.setBool('show_admin', true);
|
||||
}
|
||||
final soundOn = globalSharedPreferences.getBool('sound_on');
|
||||
if (soundOn == null) {
|
||||
globalSharedPreferences.setBool('sound_on', true);
|
||||
await globalSharedPreferences.setBool('sound_on', true);
|
||||
}
|
||||
final soundSpeed = globalSharedPreferences.getDouble('sound_speed');
|
||||
if (soundSpeed == null) {
|
||||
globalSharedPreferences.setDouble('sound_speed', 1.0);
|
||||
await globalSharedPreferences.setDouble('sound_speed', 1.0);
|
||||
}
|
||||
final autoPlay = globalSharedPreferences.getBool('auto_play_sound_tests');
|
||||
if (autoPlay == null) {
|
||||
globalSharedPreferences.setBool('auto_play_sound_tests', true);
|
||||
await globalSharedPreferences.setBool('auto_play_sound_tests', true);
|
||||
}
|
||||
final autoPlayView = globalSharedPreferences.getBool('auto_play_sound_view');
|
||||
if (autoPlayView == null) {
|
||||
globalSharedPreferences.setBool('auto_play_sound_view', false);
|
||||
await globalSharedPreferences.setBool('auto_play_sound_view', false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -135,6 +151,7 @@ class MyApp extends StatelessWidget with WidgetsBindingObserver {
|
|||
theme: lightTheme,
|
||||
routerConfig: appRouter.config(),
|
||||
scaffoldMessengerKey: scaffoldMessengerKey,
|
||||
debugShowCheckedModeBanner: false,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
36
lib/managers/repository/certificates.dart
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import 'dart:convert';
|
||||
|
||||
class Certificates {
|
||||
static get spKey =>
|
||||
'qAcZM68Q7cR6gmxvB0ar0tr6axm520BNRLv66vmpWZnMHLCmzvEKNZkqL1VD1XHt';
|
||||
|
||||
static final crt = utf8.encode(
|
||||
'''
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIB8zCCAZmgAwIBAgIUHgV3sAY8MClrgLTwmO5OG4JsMyAwCgYIKoZIzj0EAwIw
|
||||
UTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxFDASBgNVBAoMC01u
|
||||
ZW1vIGNhcmRzMRcwFQYDVQQDDA4xOTIuMTY4LjMxLjE1ODAeFw0yNDA1MTgxMTUy
|
||||
MTJaFw0yNzAyMTIxMTUyMTJaMF4xCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21l
|
||||
LVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxFzAVBgNV
|
||||
BAMMDjE5Mi4xNjguMzEuMTU4MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvO1/
|
||||
PU/ayMFgZw9KBLKpLHUpKQseY6Q6R4G51DaT8LBMfxMgAALf7PMj5IMNcW8ThjLY
|
||||
MpYL8jZhufOtxCXdPKNCMEAwHQYDVR0OBBYEFBHLVqG6EfK9nrOeE1+JpUcUiEHv
|
||||
MB8GA1UdIwQYMBaAFDJb2MW2vBxpKETey+ful5v7Jp3nMAoGCCqGSM49BAMCA0gA
|
||||
MEUCIDwUzh9oXPE2xhArNimIpkCNyJViUf7B4kt9dgZtMLAtAiEAg3rqCqZXyys4
|
||||
mozimzUqDNc811KH3IFhC/Je4zQ3g68=
|
||||
-----END CERTIFICATE-----
|
||||
'''
|
||||
.trim(),
|
||||
);
|
||||
|
||||
static final key = utf8.encode(
|
||||
'''
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIDFGWXnWiqxMCYs34r5pJq2YVT3x1MoA07fnaj9iy1SYoAoGCCqGSM49
|
||||
AwEHoUQDQgAEvO1/PU/ayMFgZw9KBLKpLHUpKQseY6Q6R4G51DaT8LBMfxMgAALf
|
||||
7PMj5IMNcW8ThjLYMpYL8jZhufOtxCXdPA==
|
||||
-----END EC PRIVATE KEY-----
|
||||
'''
|
||||
.trim(),
|
||||
);
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
|
||||
|
|
@ -8,6 +9,7 @@ import 'package:flutter/foundation.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:mnemo_cards/domain/router/app_router.gr.dart';
|
||||
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:http_certificate_pinning/http_certificate_pinning.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
|
@ -24,18 +26,11 @@ class DioProvider {
|
|||
Dio get dio => _dio;
|
||||
|
||||
Future<DioProvider> init() async {
|
||||
PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
||||
String appVersion = packageInfo.version;
|
||||
PackageInfo appPackageInfo = await PackageInfo.fromPlatform();
|
||||
String appVersion = appPackageInfo.version;
|
||||
|
||||
_dio = Dio()
|
||||
..interceptors.addAll([
|
||||
InterceptorsWrapper(onResponse: (response, handler) {
|
||||
// if (response.data is List<int>) {
|
||||
// handler.next(response..data = utf8.decode(response.data));
|
||||
// return;
|
||||
// }
|
||||
handler.next(response);
|
||||
}),
|
||||
// CertificatePinningInterceptor(allowedSHAFingerprints: [
|
||||
// '28:21:5C:FB:54:4F:4A:DC:F8:5E:4C:FE:44:C8:E0:7B:0B:09:9D:D0:A9:FA:60:1D:3B:03:FF:EC:25:1A:C5:CC',
|
||||
// ]),
|
||||
|
|
@ -63,42 +58,60 @@ class DioProvider {
|
|||
// }
|
||||
handler.next(exception);
|
||||
}),
|
||||
InterceptorsWrapper(onRequest: (request, handler) {
|
||||
request.headers = {...request.headers, 'app_version': appVersion};
|
||||
InterceptorsWrapper(onRequest: (request, handler) async {
|
||||
final String? requestToken;
|
||||
try {
|
||||
requestToken = TokenGenerator.generateRequestToken(
|
||||
requestBody: (request.data as Object?)?.encode() ?? '',
|
||||
appVersion: appVersion,
|
||||
requestPath: request.uri.path,
|
||||
userToken: await locator.userManager.getAuthToken(),
|
||||
);
|
||||
} catch (e, s) {
|
||||
log(
|
||||
'Exception while generating request token',
|
||||
error: e,
|
||||
stackTrace: s,
|
||||
);
|
||||
throw Exception(
|
||||
'Exception while generating request token ${e} ${s}',
|
||||
);
|
||||
}
|
||||
request.headers = {
|
||||
...request.headers,
|
||||
AppHeaders.appVersion: appVersion,
|
||||
AppHeaders.requestToken: requestToken,
|
||||
};
|
||||
handler.next(request);
|
||||
})
|
||||
]);
|
||||
|
||||
// final sslCert = await rootBundle.load('assets/ca.crt');
|
||||
// final clientCert = await rootBundle.load('assets/client.crt');
|
||||
// final sslKey = await rootBundle.load('assets/client.key');
|
||||
SecurityContext securityContext = SecurityContext(withTrustedRoots: true);
|
||||
// securityContext.setTrustedCertificatesBytes(
|
||||
// sslCert.buffer.asInt8List(),
|
||||
// );
|
||||
// securityContext.setTrustedCertificatesBytes(
|
||||
// clientCert.buffer.asInt8List(),
|
||||
// );
|
||||
// securityContext.usePrivateKeyBytes(sslKey.buffer.asInt8List());
|
||||
if (!kIsWeb) {
|
||||
final securityContext = SecurityContext(withTrustedRoots: true);
|
||||
|
||||
(_dio.httpClientAdapter as IOHttpClientAdapter).createHttpClient = () {
|
||||
HttpClient httpClient = HttpClient(context: securityContext)
|
||||
..badCertificateCallback =
|
||||
(X509Certificate cert, String host, int port) {
|
||||
print(cert.issuer);
|
||||
print('$host $port');
|
||||
final now = DateTime.now();
|
||||
final valid =
|
||||
cert.startValidity.isBefore(now) && cert.endValidity.isAfter(now);
|
||||
return valid &&
|
||||
const DeepCollectionEquality().equals(
|
||||
cert.sha1,
|
||||
_cert,
|
||||
);
|
||||
return true;
|
||||
};
|
||||
return httpClient;
|
||||
};
|
||||
// securityContext.setTrustedCertificatesBytes(Certificates.crt);
|
||||
// securityContext.usePrivateKeyBytes(Certificates.key);
|
||||
|
||||
_dio.httpClientAdapter = IOHttpClientAdapter(
|
||||
createHttpClient: () {
|
||||
HttpClient httpClient = HttpClient(context: securityContext)
|
||||
..badCertificateCallback =
|
||||
(X509Certificate cert, String host, int port) {
|
||||
print(cert.issuer);
|
||||
print('$host $port');
|
||||
final now = DateTime.now();
|
||||
final valid = cert.startValidity.isBefore(now) &&
|
||||
cert.endValidity.isAfter(now);
|
||||
return valid &&
|
||||
const DeepCollectionEquality().equals(
|
||||
cert.sha1,
|
||||
_cert,
|
||||
);
|
||||
};
|
||||
return httpClient;
|
||||
},
|
||||
);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ class HttpRepository extends Repository with Api {
|
|||
void setAuthToken(String? authToken) {
|
||||
_dio.interceptors.add(
|
||||
InterceptorsWrapper(onRequest: (options, handler) {
|
||||
options.headers[HttpHeaders.authorizationHeader] = authToken;
|
||||
options.headers[AppHeaders.userToken] = authToken;
|
||||
handler.next(options);
|
||||
}),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ class UserManager {
|
|||
|
||||
bool get hasUser => userStateHolder.user != null;
|
||||
|
||||
int? get userId => userStateHolder.user?.id;
|
||||
|
||||
Future<void> login() async {
|
||||
if (await _googleSignIn.isSignedIn()) {
|
||||
await _googleSignIn.signOut();
|
||||
|
|
@ -61,17 +63,21 @@ class UserManager {
|
|||
) async {
|
||||
final (user, token) =
|
||||
await _repository.createOrGetUser(externalId, idType, name);
|
||||
_setAuthToken(token);
|
||||
await _setAuthToken(token);
|
||||
userStateHolder.setUser(user);
|
||||
}
|
||||
|
||||
void _setAuthToken(String authToken) async {
|
||||
Future<void> _setAuthToken(String authToken) async {
|
||||
_repository.setAuthToken(authToken);
|
||||
await _sharedPreferences!.setString('authToken', authToken);
|
||||
await secureStorage.write(key: 'authToken', value: authToken);
|
||||
}
|
||||
|
||||
void _clearAuthToken() async {
|
||||
await _sharedPreferences!.remove('authToken');
|
||||
Future<void> _clearAuthToken() async {
|
||||
await secureStorage.delete(key: 'authToken');
|
||||
}
|
||||
|
||||
Future<String?> getAuthToken() async {
|
||||
return secureStorage.read(key: 'authToken');
|
||||
}
|
||||
|
||||
Future<PromoCodeDto> applyPromocode(String code) async {
|
||||
|
|
@ -80,9 +86,9 @@ class UserManager {
|
|||
}
|
||||
|
||||
Future<UserDto?> updateUser() async {
|
||||
String? authToken = _sharedPreferences!.getString('authToken');
|
||||
String? authToken = await getAuthToken();
|
||||
if (authToken != null) {
|
||||
_setAuthToken(authToken);
|
||||
await _setAuthToken(authToken);
|
||||
try {
|
||||
final fetchedUser = await _repository.getUser();
|
||||
userStateHolder.setUser(fetchedUser!);
|
||||
|
|
@ -117,40 +123,44 @@ class UserManager {
|
|||
.listen((account) async {
|
||||
if (account != null) {
|
||||
final name = account.displayName;
|
||||
final token =
|
||||
final externalToken =
|
||||
await account.authentication.then((value) => value.idToken);
|
||||
Analytics.login(ExternalIdType.google);
|
||||
await _createUser(token!, ExternalIdType.google, name);
|
||||
await _createUser(externalToken!, ExternalIdType.google, name);
|
||||
}
|
||||
}))
|
||||
..add(userStateHolder.asNullableStream
|
||||
.distinct((a, b) => a?.id == b?.id)
|
||||
.listen((user) async {
|
||||
try {
|
||||
if (user == null) {
|
||||
AppRouter.openAuthOrProfile();
|
||||
} else {
|
||||
..add(
|
||||
userStateHolder.asNullableStream
|
||||
.distinct((a, b) => a?.id == b?.id)
|
||||
// don't want to clear cache on login
|
||||
.skip(1)
|
||||
.listen(
|
||||
(user) async {
|
||||
try {
|
||||
AppRouter.closeAuthPage();
|
||||
await locator.previewPackPoller.poll();
|
||||
await locator.packManager.clearCache();
|
||||
await locator.packUpdater.updateAvailablePacks();
|
||||
} catch (err) {
|
||||
log(err.toString());
|
||||
if (user == null) {
|
||||
AppRouter.openAuthOrProfile();
|
||||
} else {
|
||||
try {
|
||||
AppRouter.closeAuthPage();
|
||||
await locator.previewPackPoller.poll();
|
||||
await locator.packManager.clearCache();
|
||||
await locator.packUpdater.updateAvailablePacks();
|
||||
} catch (err) {
|
||||
log(err.toString());
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
_clearAuthToken();
|
||||
if (e is DioException) {
|
||||
if (e.response?.statusCode == 401) {
|
||||
AppRouter.openProfile();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
_clearAuthToken();
|
||||
if (e is DioException) {
|
||||
if (e.response?.statusCode == 401) {
|
||||
appRouter.push(
|
||||
PageRouteInfo(ProfilePage.name),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> dispose() async {
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ class AuthPage extends StatelessWidget {
|
|||
fit: BoxFit.fitWidth,
|
||||
),
|
||||
),
|
||||
if (SHOW_ADMIN)
|
||||
if (ADMIN_BUILD)
|
||||
SharedPrefButton<bool>(
|
||||
builder: (v, _) => (v ?? false) ? Text('PROD') : Text('TEST'),
|
||||
spKey: 'env',
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import 'dart:math';
|
|||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:mnemo_cards/main.dart';
|
||||
|
|
@ -270,6 +271,9 @@ class _AdTile extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (locator.userManager.userStateHolder.user?.subscription == true)
|
||||
return SizedBox.shrink();
|
||||
if (kIsWeb) return SizedBox.shrink();
|
||||
return LayoutBuilder(builder: (context, constraints) {
|
||||
final banner = YandexAds.createBanner(
|
||||
BoxConstraints.tight(Size(constraints.maxWidth - 16, 80 - 8)));
|
||||
|
|
|
|||
|
|
@ -78,24 +78,32 @@ class _PacksMenuWidgetState extends State<PacksMenuWidget> {
|
|||
backgroundColor: Colors.white,
|
||||
color: snapshot.data?.firstOrNull?.color?.asColor ??
|
||||
Colors.black,
|
||||
child: ListView(
|
||||
physics: BouncingScrollPhysics(
|
||||
decelerationRate: ScrollDecelerationRate.fast,
|
||||
parent: AlwaysScrollableScrollPhysics()),
|
||||
scrollDirection: Axis.vertical,
|
||||
children: [
|
||||
if (snapshot.data == null)
|
||||
...[1, 2, 3, 4].map(
|
||||
(e) => _PackButton4Shimmer(
|
||||
constraints.maxWidth,
|
||||
cardHeight,
|
||||
child: (!snapshot.hasData)
|
||||
? Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Image.asset(
|
||||
'images/cerdo.jpg',
|
||||
width: 100,
|
||||
),
|
||||
),
|
||||
Text('Загрузка...'),
|
||||
],
|
||||
),
|
||||
)
|
||||
: ListView(
|
||||
physics: BouncingScrollPhysics(
|
||||
decelerationRate: ScrollDecelerationRate.fast,
|
||||
parent: AlwaysScrollableScrollPhysics(),
|
||||
),
|
||||
scrollDirection: Axis.vertical,
|
||||
children: snapshot.requireData!
|
||||
.map((pack) => _PackButton4(pack, cardHeight))
|
||||
.toList(),
|
||||
),
|
||||
if (snapshot.hasData)
|
||||
...snapshot.data!
|
||||
.map((pack) => _PackButton4(pack, cardHeight)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -110,7 +118,7 @@ class _PacksMenuWidgetState extends State<PacksMenuWidget> {
|
|||
for (final pack in packs) {
|
||||
if (pack.imageBase64 != null) {
|
||||
try {
|
||||
final bytes = base64Decode(pack.imageBase64!);
|
||||
final bytes = await compute(base64Decode, pack.imageBase64!);
|
||||
final image = MemoryImage(bytes);
|
||||
locator.imagesHolder.setImage('pack_${pack.id}', image);
|
||||
await precacheImage(image, context);
|
||||
|
|
@ -165,18 +173,11 @@ class _PackButton4 extends StatelessWidget {
|
|||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
// border: Border.all(
|
||||
// color: pack.dto.color?.asColor?.withOpacity(0.9) ??
|
||||
// Colors.grey.withOpacity(0.5),
|
||||
// ),
|
||||
// borderRadius: BorderRadius.circular(12.0),
|
||||
image: pack.imageBase64 == null
|
||||
? null
|
||||
: DecorationImage(
|
||||
image: locator.imagesHolder.get('pack_${pack.id}') ??
|
||||
MemoryImage(
|
||||
base64Decode(pack.imageBase64!),
|
||||
),
|
||||
MemoryImage(base64Decode(pack.imageBase64!)),
|
||||
),
|
||||
),
|
||||
margin: EdgeInsets.all(2.0.w),
|
||||
|
|
@ -253,125 +254,3 @@ class _PackButton4 extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PackButton4Shimmer extends StatelessWidget {
|
||||
final double cardHeight;
|
||||
final double cardWidth;
|
||||
|
||||
const _PackButton4Shimmer(
|
||||
this.cardWidth,
|
||||
this.cardHeight,
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Shimmer.fromColors(
|
||||
baseColor: Colors.white,
|
||||
highlightColor: borderGray,
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
height: cardHeight,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
border: Border.all(
|
||||
color: Colors.grey.withOpacity(0.5),
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Container(
|
||||
color: Colors.white,
|
||||
width: cardHeight,
|
||||
height: cardHeight,
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
height: 30,
|
||||
width: (cardWidth - cardHeight) * 0.7,
|
||||
color: Colors.white,
|
||||
),
|
||||
Container(
|
||||
height: 20,
|
||||
width: (cardWidth - cardHeight) * 0.5,
|
||||
color: Colors.white,
|
||||
),
|
||||
Spacer(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Container(
|
||||
height: 20,
|
||||
width: (cardWidth - cardHeight) * 0.1,
|
||||
color: Colors.white,
|
||||
),
|
||||
Container(
|
||||
height: 25,
|
||||
width: (cardWidth - cardHeight) * 0.1,
|
||||
color: Colors.white,
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PackButtonShimmer extends StatelessWidget {
|
||||
final double cardWidth;
|
||||
final double cardHeight;
|
||||
|
||||
const _PackButtonShimmer(
|
||||
this.cardWidth,
|
||||
this.cardHeight,
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Shimmer.fromColors(
|
||||
baseColor: Colors.white,
|
||||
highlightColor: Colors.grey[200]!,
|
||||
child: Container(
|
||||
width: cardWidth,
|
||||
height: cardHeight,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
width: cardWidth,
|
||||
height: cardWidth,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
)),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(8.0)),
|
||||
color: Colors.white,
|
||||
),
|
||||
width: cardWidth * 0.7,
|
||||
height: 30,
|
||||
alignment: Alignment.center,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
52
pubspec.lock
|
|
@ -494,6 +494,54 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.9.0"
|
||||
flutter_secure_storage:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_secure_storage
|
||||
sha256: "165164745e6afb5c0e3e3fcc72a012fb9e58496fb26ffb92cf22e16a821e85d0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.2.2"
|
||||
flutter_secure_storage_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_secure_storage_linux
|
||||
sha256: "4d91bfc23047422cbcd73ac684bc169859ee766482517c22172c86596bf1464b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
flutter_secure_storage_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_secure_storage_macos
|
||||
sha256: "1693ab11121a5f925bbea0be725abfcfbbcf36c1e29e571f84a0c0f436147a81"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
flutter_secure_storage_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_secure_storage_platform_interface
|
||||
sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
flutter_secure_storage_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_secure_storage_web
|
||||
sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
flutter_secure_storage_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_secure_storage_windows
|
||||
sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
|
|
@ -732,10 +780,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: js
|
||||
sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
|
||||
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.1"
|
||||
version: "0.6.7"
|
||||
json_annotation:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
|
|||
53
pubspec.yaml
|
|
@ -1,32 +1,12 @@
|
|||
name: mnemo_cards
|
||||
description: Mnemo
|
||||
# The following line prevents the package from being accidentally published to
|
||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
publish_to: 'none'
|
||||
|
||||
# The following defines the version and build number for your application.
|
||||
# A version number is three numbers separated by dots, like 1.2.43
|
||||
# followed by an optional build number separated by a +.
|
||||
# Both the version and the builder number may be overridden in flutter
|
||||
# build by specifying --build-name and --build-number, respectively.
|
||||
# In Android, build-name is used as versionName while build-number used as versionCode.
|
||||
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
|
||||
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
|
||||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
# In Windows, build-name is used as the major, minor, and patch parts
|
||||
# of the product and file versions while build-number is used as the build suffix.
|
||||
version: 1.0.0+6
|
||||
version: 1.0.1
|
||||
|
||||
environment:
|
||||
sdk: '>=3.1.3 <4.0.0'
|
||||
|
||||
# Dependencies specify other packages that your package needs in order to work.
|
||||
# To automatically upgrade your package dependencies to the latest versions
|
||||
# consider running `flutter pub upgrade --major-versions`. Alternatively,
|
||||
# dependencies can be manually updated by changing the version numbers below to
|
||||
# the latest version available on pub.dev. To see which dependencies have newer
|
||||
# versions available, run `flutter pub outdated`.
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
|
@ -35,11 +15,12 @@ dependencies:
|
|||
path: ../mnemo_cards_common
|
||||
|
||||
get_it:
|
||||
shared_preferences:
|
||||
shared_preferences: ^2.2.3
|
||||
flutter_secure_storage: ^9.2.2
|
||||
rxdart:
|
||||
firebase_core: ^2.30.1
|
||||
# firebase_crashlytics:
|
||||
# firebase_storage:
|
||||
# firebase_crashlytics:
|
||||
# firebase_storage:
|
||||
cloud_firestore: ^4.17.2
|
||||
freezed:
|
||||
json_serializable:
|
||||
|
|
@ -78,6 +59,8 @@ dependencies:
|
|||
http_certificate_pinning: ^2.1.3
|
||||
url_launcher: ^6.2.6
|
||||
|
||||
# requests_inspector: ^4.0.3
|
||||
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
@ -103,11 +86,11 @@ flutter:
|
|||
flutter_launcher_icons:
|
||||
android: "launcher_icon"
|
||||
ios: true
|
||||
image_path: "images/cerdo.jpg"
|
||||
image_path: "images/icon.png"
|
||||
min_sdk_android: 21 # android min sdk min:16, default 21
|
||||
|
||||
flutter_native_splash:
|
||||
# Only one parameter can be used, color and background_image cannot both be set.
|
||||
# Only one parameter can be used, color and background_image cannot both be set.
|
||||
color: "#ffffff"
|
||||
#background_image: "assets/background.png"
|
||||
|
||||
|
|
@ -141,16 +124,16 @@ flutter_native_splash:
|
|||
# Please visit https://developer.android.com/guide/topics/ui/splash-screen
|
||||
# Following are specific parameters for Android 12+.
|
||||
android_12:
|
||||
# The image parameter sets the splash screen icon image. If this parameter is not specified,
|
||||
# the app's launcher icon will be used instead.
|
||||
# Please note that the splash screen will be clipped to a circle on the center of the screen.
|
||||
# App icon with an icon background: This should be 960×960 pixels, and fit within a circle
|
||||
# 640 pixels in diameter.
|
||||
# App icon without an icon background: This should be 1152×1152 pixels, and fit within a circle
|
||||
# 768 pixels in diameter.
|
||||
# The image parameter sets the splash screen icon image. If this parameter is not specified,
|
||||
# the app's launcher icon will be used instead.
|
||||
# Please note that the splash screen will be clipped to a circle on the center of the screen.
|
||||
# App icon with an icon background: This should be 960×960 pixels, and fit within a circle
|
||||
# 640 pixels in diameter.
|
||||
# App icon without an icon background: This should be 1152×1152 pixels, and fit within a circle
|
||||
# 768 pixels in diameter.
|
||||
image: images/cerdo_big.jpg
|
||||
|
||||
# Splash screen background color.
|
||||
# Splash screen background color.
|
||||
color: "#ffffff"
|
||||
|
||||
# App icon background color.
|
||||
|
|
|
|||