mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-23 12:09:43 +00:00
other fixes from monero.dart and monero_c
This commit is contained in:
parent
dcf6140444
commit
5b42130e85
9 changed files with 154 additions and 151 deletions
2
Makefile
2
Makefile
|
@ -1,6 +1,6 @@
|
|||
# TODO(mrcyjanek): Cleanup, this is borrowed from unnamed_monero_wallet repo.
|
||||
|
||||
MONERO_C_TAG=v0.18.3.3-RC27
|
||||
MONERO_C_TAG=v0.18.3.3-RC31
|
||||
LIBCPP_SHARED_SO_TAG=latest-RC1
|
||||
LIBCPP_SHARED_SO_NDKVERSION=r17c
|
||||
|
||||
|
|
|
@ -245,8 +245,8 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
path: "."
|
||||
ref: ada81417cb53d8b0e34022df1dd3d9dc1fe62ab5
|
||||
resolved-ref: ada81417cb53d8b0e34022df1dd3d9dc1fe62ab5
|
||||
ref: e2149153ecbaa6cc4d7d3970d8fa8ce1099b63af
|
||||
resolved-ref: e2149153ecbaa6cc4d7d3970d8fa8ce1099b63af
|
||||
url: "https://git.mrcyjanek.net/mrcyjanek/monero.dart"
|
||||
source: git
|
||||
version: "0.0.0"
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
|
||||
import 'dart:ffi';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:cw_monero/api/account_list.dart';
|
||||
import 'package:cw_monero/api/exceptions/creation_transaction_exception.dart';
|
||||
import 'package:cw_monero/api/monero_output.dart';
|
||||
import 'package:cw_monero/api/structs/pending_transaction.dart';
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:monero/monero.dart' as monero;
|
||||
import 'package:monero/src/generated_bindings_monero.g.dart' as monero_gen;
|
||||
|
||||
|
||||
String getTxKey(String txId) {
|
||||
|
@ -30,31 +35,48 @@ Transaction getTransaction(String txId) {
|
|||
return Transaction(txInfo: monero.TransactionHistory_transactionById(txhistory!, txid: txId));
|
||||
}
|
||||
|
||||
PendingTransactionDescription createTransactionSync(
|
||||
Future<PendingTransactionDescription> createTransactionSync(
|
||||
{required String address,
|
||||
required String paymentId,
|
||||
required int priorityRaw,
|
||||
String? amount,
|
||||
int accountIndex = 0,
|
||||
List<String> preferredInputs = const []}) {
|
||||
List<String> preferredInputs = const []}) async {
|
||||
|
||||
final amt = amount == null ? 0 : monero.Wallet_amountFromString(amount);
|
||||
final pendingTx = monero.Wallet_createTransaction(
|
||||
wptr!,
|
||||
dst_addr: address,
|
||||
payment_id: paymentId,
|
||||
amount: amt,
|
||||
mixin_count: 1,
|
||||
pendingTransactionPriority: priorityRaw,
|
||||
subaddr_account: accountIndex,
|
||||
preferredInputs: preferredInputs,
|
||||
|
||||
final address_ = address.toNativeUtf8();
|
||||
final paymentId_ = paymentId.toNativeUtf8();
|
||||
final preferredInputs_ = preferredInputs.join(monero.defaultSeparatorStr).toNativeUtf8();
|
||||
|
||||
final waddr = wptr!.address;
|
||||
final addraddr = address_.address;
|
||||
final paymentIdAddr = paymentId_.address;
|
||||
final preferredInputsAddr = preferredInputs_.address;
|
||||
final spaddr = monero.defaultSeparator.address;
|
||||
final pendingTx = Pointer<Void>.fromAddress(await Isolate.run(() {
|
||||
final tx = monero_gen.MoneroC(DynamicLibrary.open(monero.libPath)).MONERO_Wallet_createTransaction(
|
||||
Pointer.fromAddress(waddr),
|
||||
Pointer.fromAddress(addraddr).cast(),
|
||||
Pointer.fromAddress(paymentIdAddr).cast(),
|
||||
amt,
|
||||
1,
|
||||
priorityRaw,
|
||||
accountIndex,
|
||||
Pointer.fromAddress(preferredInputsAddr).cast(),
|
||||
Pointer.fromAddress(spaddr),
|
||||
);
|
||||
return tx.address;
|
||||
}));
|
||||
calloc.free(address_);
|
||||
calloc.free(paymentId_);
|
||||
calloc.free(preferredInputs_);
|
||||
final String? error = (() {
|
||||
final status = monero.Wallet_status(wptr!);
|
||||
final status = monero.PendingTransaction_status(pendingTx);
|
||||
if (status == 0) {
|
||||
return null;
|
||||
}
|
||||
return monero.Wallet_errorString(wptr!);
|
||||
return monero.PendingTransaction_errorString(pendingTx);
|
||||
})();
|
||||
|
||||
if (error != null) {
|
||||
|
@ -83,68 +105,27 @@ PendingTransactionDescription createTransactionMultDestSync(
|
|||
required int priorityRaw,
|
||||
int accountIndex = 0,
|
||||
List<String> preferredInputs = const []}) {
|
||||
// final int size = outputs.length;
|
||||
// final List<Pointer<Utf8>> addressesPointers =
|
||||
// outputs.map((output) => output.address.toNativeUtf8()).toList();
|
||||
// final Pointer<Pointer<Utf8>> addressesPointerPointer = calloc(size);
|
||||
// final List<Pointer<Utf8>> amountsPointers =
|
||||
// outputs.map((output) => output.amount.toNativeUtf8()).toList();
|
||||
// final Pointer<Pointer<Utf8>> amountsPointerPointer = calloc(size);
|
||||
|
||||
// for (int i = 0; i < size; i++) {
|
||||
// addressesPointerPointer[i] = addressesPointers[i];
|
||||
// amountsPointerPointer[i] = amountsPointers[i];
|
||||
// }
|
||||
|
||||
// final int preferredInputsSize = preferredInputs.length;
|
||||
// final List<Pointer<Utf8>> preferredInputsPointers =
|
||||
// preferredInputs.map((output) => output.toNativeUtf8()).toList();
|
||||
// final Pointer<Pointer<Utf8>> preferredInputsPointerPointer = calloc(preferredInputsSize);
|
||||
|
||||
// for (int i = 0; i < preferredInputsSize; i++) {
|
||||
// preferredInputsPointerPointer[i] = preferredInputsPointers[i];
|
||||
// }
|
||||
|
||||
// final paymentIdPointer = paymentId.toNativeUtf8();
|
||||
// final errorMessagePointer = calloc<Utf8Box>();
|
||||
// final pendingTransactionRawPointer = calloc<PendingTransactionRaw>();
|
||||
// final created = transactionCreateMultDestNative(
|
||||
// addressesPointerPointer,
|
||||
// paymentIdPointer,
|
||||
// amountsPointerPointer,
|
||||
// size,
|
||||
// priorityRaw,
|
||||
// accountIndex,
|
||||
// preferredInputsPointerPointer,
|
||||
// preferredInputsSize,
|
||||
// errorMessagePointer,
|
||||
// pendingTransactionRawPointer) !=
|
||||
// 0;
|
||||
|
||||
// calloc.free(addressesPointerPointer);
|
||||
// calloc.free(amountsPointerPointer);
|
||||
// calloc.free(preferredInputsPointerPointer);
|
||||
|
||||
// addressesPointers.forEach((element) => calloc.free(element));
|
||||
// amountsPointers.forEach((element) => calloc.free(element));
|
||||
// preferredInputsPointers.forEach((element) => calloc.free(element));
|
||||
|
||||
// calloc.free(paymentIdPointer);
|
||||
|
||||
// if (!created) {
|
||||
// final message = errorMessagePointer.ref.getValue();
|
||||
// calloc.free(errorMessagePointer);
|
||||
// throw CreationTransactionException(message: message);
|
||||
// }
|
||||
|
||||
// return PendingTransactionDescription(
|
||||
// amount: pendingTransactionRawPointer.ref.amount,
|
||||
// fee: pendingTransactionRawPointer.ref.fee,
|
||||
// hash: pendingTransactionRawPointer.ref.getHash(),
|
||||
// hex: pendingTransactionRawPointer.ref.getHex(),
|
||||
// txKey: pendingTransactionRawPointer.ref.getKey(),
|
||||
// pointerAddress: pendingTransactionRawPointer.address);
|
||||
throw CreationTransactionException(message: "Unimplemented in monero_c");
|
||||
final txptr = monero.Wallet_createTransactionMultDest(
|
||||
wptr!,
|
||||
dstAddr: outputs.map((e) => e.address).toList(),
|
||||
isSweepAll: false,
|
||||
amounts: outputs.map((e) => monero.Wallet_amountFromString(e.amount)).toList(),
|
||||
mixinCount: 0,
|
||||
pendingTransactionPriority: priorityRaw,
|
||||
subaddr_account: accountIndex,
|
||||
);
|
||||
if (monero.PendingTransaction_status(txptr) != 0) {
|
||||
throw CreationTransactionException(message: monero.PendingTransaction_errorString(txptr));
|
||||
}
|
||||
return PendingTransactionDescription(
|
||||
amount: monero.PendingTransaction_amount(txptr),
|
||||
fee: monero.PendingTransaction_fee(txptr),
|
||||
hash: monero.PendingTransaction_txid(txptr, ''),
|
||||
hex: monero.PendingTransaction_txid(txptr, ''),
|
||||
txKey: monero.PendingTransaction_txid(txptr, ''),
|
||||
pointerAddress: txptr.address,
|
||||
);
|
||||
}
|
||||
|
||||
void commitTransactionFromPointerAddress({required int address}) =>
|
||||
|
@ -168,7 +149,7 @@ void commitTransaction({required monero.PendingTransaction transactionPointer})
|
|||
}
|
||||
}
|
||||
|
||||
PendingTransactionDescription _createTransactionSync(Map args) {
|
||||
Future<PendingTransactionDescription> _createTransactionSync(Map args) async {
|
||||
final address = args['address'] as String;
|
||||
final paymentId = args['paymentId'] as String;
|
||||
final amount = args['amount'] as String?;
|
||||
|
@ -244,6 +225,7 @@ class Transaction {
|
|||
final int confirmations;
|
||||
late final bool isPending = confirmations < 10;
|
||||
final int blockheight;
|
||||
final int addressIndex = 0;
|
||||
final int accountIndex;
|
||||
final String paymentId;
|
||||
final int amount;
|
||||
|
@ -251,6 +233,7 @@ class Transaction {
|
|||
late DateTime timeStamp;
|
||||
late final bool isConfirmed = !isPending;
|
||||
final String hash;
|
||||
final String key;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
|
@ -263,6 +246,7 @@ class Transaction {
|
|||
"isPending": isPending,
|
||||
"blockheight": blockheight,
|
||||
"accountIndex": accountIndex,
|
||||
"addressIndex": addressIndex,
|
||||
"paymentId": paymentId,
|
||||
"amount": amount,
|
||||
"isSpend": isSpend,
|
||||
|
@ -291,5 +275,6 @@ class Transaction {
|
|||
blockheight = monero.TransactionInfo_blockHeight(txInfo),
|
||||
confirmations = monero.TransactionInfo_confirmations(txInfo),
|
||||
fee = monero.TransactionInfo_fee(txInfo),
|
||||
description = monero.TransactionInfo_description(txInfo);
|
||||
description = monero.TransactionInfo_description(txInfo),
|
||||
key = monero.Wallet_getTxKey(wptr!, txid: monero.TransactionInfo_hash(txInfo));
|
||||
}
|
|
@ -4,10 +4,12 @@ import 'package:cw_monero/api/account_list.dart';
|
|||
import 'package:cw_monero/api/exceptions/setup_wallet_exception.dart';
|
||||
import 'package:monero/monero.dart' as monero;
|
||||
|
||||
int _boolToInt(bool value) => value ? 1 : 0;
|
||||
|
||||
int getSyncingHeight() => monero.MONERO_cw_WalletListener_height(getWlptr());
|
||||
|
||||
int getSyncingHeight() {
|
||||
// final height = monero.MONERO_cw_WalletListener_height(getWlptr());
|
||||
final h2 = monero.Wallet_blockChainHeight(wptr!);
|
||||
// print("height: $height / $h2");
|
||||
return h2;
|
||||
}
|
||||
bool isNeededToRefresh() {
|
||||
final ret = monero.MONERO_cw_WalletListener_isNeedToRefresh(getWlptr());
|
||||
monero.MONERO_cw_WalletListener_resetNeedToRefresh(getWlptr());
|
||||
|
@ -22,7 +24,14 @@ bool isNewTransactionExist() {
|
|||
String getFilename() => monero.Wallet_filename(wptr!);
|
||||
|
||||
// TODO(mrcyjanek): Cake polyseed support
|
||||
String getSeed() => monero.Wallet_seed(wptr!, seedOffset: '');
|
||||
String getSeed() {
|
||||
final legacy = monero.Wallet_seed(wptr!, seedOffset: '');
|
||||
final polyseed = monero.Wallet_getPolyseed(wptr!, passphrase: '');
|
||||
if (polyseed == "") {
|
||||
return legacy;
|
||||
}
|
||||
return polyseed;
|
||||
}
|
||||
|
||||
String getAddress({int accountIndex = 0, int addressIndex = 1}) => monero.Wallet_address(wptr!, accountIndex: accountIndex, addressIndex: addressIndex);
|
||||
|
||||
|
@ -62,8 +71,6 @@ bool setupNodeSync(
|
|||
daemonPassword: password ?? ''
|
||||
);
|
||||
// monero.Wallet_init3(wptr!, argv0: '', defaultLogBaseName: 'moneroc', console: true);
|
||||
monero.Wallet_startRefresh(wptr!);
|
||||
monero.Wallet_refreshAsync(wptr!);
|
||||
|
||||
final status = monero.Wallet_status(wptr!);
|
||||
|
||||
|
@ -76,7 +83,10 @@ bool setupNodeSync(
|
|||
return status == 0;
|
||||
}
|
||||
|
||||
void startRefreshSync() {}
|
||||
void startRefreshSync() {
|
||||
monero.Wallet_refreshAsync(wptr!);
|
||||
monero.Wallet_startRefresh(wptr!);
|
||||
}
|
||||
|
||||
Future<bool> connectToNode() async {
|
||||
return true;
|
||||
|
|
|
@ -7,7 +7,6 @@ import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart';
|
|||
import 'package:cw_monero/api/exceptions/wallet_restore_from_keys_exception.dart';
|
||||
import 'package:cw_monero/api/exceptions/wallet_restore_from_seed_exception.dart';
|
||||
import 'package:cw_monero/api/wallet.dart';
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:monero/monero.dart' as monero;
|
||||
|
||||
monero.WalletManager? _wmPtr;
|
||||
|
@ -77,13 +76,7 @@ void restoreWalletFromKeysSync(
|
|||
required String spendKey,
|
||||
int nettype = 0,
|
||||
int restoreHeight = 0}) {
|
||||
final pathPointer = path.toNativeUtf8();
|
||||
final passwordPointer = password.toNativeUtf8();
|
||||
final languagePointer = language.toNativeUtf8();
|
||||
final addressPointer = address.toNativeUtf8();
|
||||
final viewKeyPointer = viewKey.toNativeUtf8();
|
||||
final spendKeyPointer = spendKey.toNativeUtf8();
|
||||
final errorMessagePointer = ''.toNativeUtf8();
|
||||
|
||||
wptr = monero.WalletManager_createWalletFromKeys(
|
||||
wmPtr,
|
||||
path: path,
|
||||
|
@ -109,28 +102,35 @@ void restoreWalletFromSpendKeySync(
|
|||
required String spendKey,
|
||||
int nettype = 0,
|
||||
int restoreHeight = 0}) {
|
||||
final pathPointer = path.toNativeUtf8();
|
||||
final passwordPointer = password.toNativeUtf8();
|
||||
final seedPointer = seed.toNativeUtf8();
|
||||
final languagePointer = language.toNativeUtf8();
|
||||
final spendKeyPointer = spendKey.toNativeUtf8();
|
||||
final errorMessagePointer = ''.toNativeUtf8();
|
||||
|
||||
wptr = monero.WalletManager_createWalletFromKeys(
|
||||
// wptr = monero.WalletManager_createWalletFromKeys(
|
||||
// wmPtr,
|
||||
// path: path,
|
||||
// password: password,
|
||||
// restoreHeight: restoreHeight,
|
||||
// addressString: '',
|
||||
// spendKeyString: spendKey,
|
||||
// viewKeyString: '',
|
||||
// nettype: 0,
|
||||
// );
|
||||
|
||||
wptr = monero.WalletManager_createWalletFromPolyseed(
|
||||
wmPtr,
|
||||
path: path,
|
||||
password: password,
|
||||
mnemonic: seed,
|
||||
seedOffset: '',
|
||||
newWallet: false,
|
||||
restoreHeight: restoreHeight,
|
||||
addressString: '',
|
||||
spendKeyString: spendKey,
|
||||
viewKeyString: '',
|
||||
nettype: 0,
|
||||
kdfRounds: 1,
|
||||
);
|
||||
|
||||
final status = monero.Wallet_status(wptr!);
|
||||
|
||||
if (status == 0) {
|
||||
throw WalletRestoreFromKeysException(message: monero.Wallet_errorString(wptr!));
|
||||
if (status != 0) {
|
||||
final err = monero.Wallet_errorString(wptr!);
|
||||
print("err: $err");
|
||||
throw WalletRestoreFromKeysException(message: err);
|
||||
}
|
||||
|
||||
storeSync();
|
||||
|
|
|
@ -561,7 +561,14 @@ abstract class MoneroWalletBase
|
|||
row.accountIndex,
|
||||
0,
|
||||
row.fee,
|
||||
row.confirmations))
|
||||
row.confirmations,
|
||||
|
||||
)..additionalInfo = <String, dynamic>{
|
||||
'key': row.key,
|
||||
'accountIndex': row.accountIndex,
|
||||
'addressIndex': row.addressIndex
|
||||
},
|
||||
)
|
||||
.where((element) => element.accountIndex == (accountIndex ?? 0))
|
||||
.toList();
|
||||
|
||||
|
|
|
@ -414,8 +414,8 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: ada81417cb53d8b0e34022df1dd3d9dc1fe62ab5
|
||||
resolved-ref: ada81417cb53d8b0e34022df1dd3d9dc1fe62ab5
|
||||
ref: e2149153ecbaa6cc4d7d3970d8fa8ce1099b63af
|
||||
resolved-ref: e2149153ecbaa6cc4d7d3970d8fa8ce1099b63af
|
||||
url: "https://git.mrcyjanek.net/mrcyjanek/monero.dart"
|
||||
source: git
|
||||
version: "0.0.0"
|
||||
|
|
|
@ -6,7 +6,7 @@ author: Cake Wallet
|
|||
homepage: https://cakewallet.com
|
||||
|
||||
environment:
|
||||
sdk: ">=2.17.5 <3.0.0"
|
||||
sdk: ">=2.19.0 <3.0.0"
|
||||
flutter: ">=1.20.0"
|
||||
|
||||
dependencies:
|
||||
|
@ -25,7 +25,7 @@ dependencies:
|
|||
monero:
|
||||
git:
|
||||
url: https://git.mrcyjanek.net/mrcyjanek/monero.dart
|
||||
ref: ada81417cb53d8b0e34022df1dd3d9dc1fe62ab5
|
||||
ref: e2149153ecbaa6cc4d7d3970d8fa8ce1099b63af
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
|
@ -1,46 +1,47 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
|
||||
import 'package:cake_wallet/core/auth_service.dart';
|
||||
import 'package:cake_wallet/entities/language_service.dart';
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:cake_wallet/core/auth_service.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/entities/contact.dart';
|
||||
import 'package:cake_wallet/entities/default_settings_migration.dart';
|
||||
import 'package:cake_wallet/entities/get_encryption_key.dart';
|
||||
import 'package:cake_wallet/entities/language_service.dart';
|
||||
import 'package:cake_wallet/entities/template.dart';
|
||||
import 'package:cake_wallet/entities/transaction_description.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_template.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/locales/locale.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/reactions/bootstrap.dart';
|
||||
import 'package:cake_wallet/router.dart' as Router;
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/root/root.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/store/authentication_store.dart';
|
||||
import 'package:cake_wallet/store/yat/yat_store.dart';
|
||||
import 'package:cake_wallet/themes/theme_base.dart';
|
||||
import 'package:cake_wallet/utils/device_info.dart';
|
||||
import 'package:cake_wallet/utils/exception_handler.dart';
|
||||
import 'package:cw_core/address_info.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cw_core/address_info.dart';
|
||||
import 'package:cw_core/cake_hive.dart';
|
||||
import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_core/unspent_coins_info.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/themes/theme_base.dart';
|
||||
import 'package:cake_wallet/router.dart' as Router;
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/reactions/bootstrap.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/store/authentication_store.dart';
|
||||
import 'package:cake_wallet/entities/transaction_description.dart';
|
||||
import 'package:cake_wallet/entities/get_encryption_key.dart';
|
||||
import 'package:cake_wallet/entities/contact.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cake_wallet/entities/default_settings_migration.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cake_wallet/entities/template.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_template.dart';
|
||||
import 'package:cake_wallet/src/screens/root/root.dart';
|
||||
import 'package:uni_links/uni_links.dart';
|
||||
import 'package:cw_core/unspent_coins_info.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cw_core/cake_hive.dart';
|
||||
|
||||
final navigatorKey = GlobalKey<NavigatorState>();
|
||||
final rootKey = GlobalKey<RootState>();
|
||||
|
|
Loading…
Reference in a new issue