[skip ci] Merge branch 'main' of https://github.com/cake-tech/cake_wallet into breez

This commit is contained in:
Matthew Fosse 2024-05-06 13:06:57 -07:00
commit 4c73dec92f
22 changed files with 205 additions and 75 deletions

View file

@ -1,4 +1,8 @@
-
uri: api.trongrid.io
is_default: true
useSSL: true
-
uri: tron-rpc.publicnode.com:443
is_default: false
useSSL: true

View file

@ -54,6 +54,9 @@ dependency_overrides:
dependency_overrides:
watcher: ^1.1.0
dependency_overrides:
watcher: ^1.1.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

View file

@ -49,6 +49,9 @@ dependency_overrides:
dependency_overrides:
watcher: ^1.1.0
dependency_overrides:
watcher: ^1.1.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

View file

@ -0,0 +1,22 @@
import 'dart:io';
import 'package:flutter/services.dart';
const MethodChannel _channel = MethodChannel('com.cake_wallet/native_utils');
Future<void> setDefaultMinimumWindowSize() async {
if (!Platform.isMacOS) return;
try {
final result = await _channel.invokeMethod(
'setMinWindowSize',
{'width': 500, 'height': 700},
) as bool;
if (!result) {
print("Failed to set minimum window size.");
}
} on PlatformException catch (e) {
print("Failed to set minimum window size: '${e.message}'.");
}
}

View file

@ -35,6 +35,9 @@ dependency_overrides:
dependency_overrides:
watcher: ^1.1.0
dependency_overrides:
watcher: ^1.1.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

View file

@ -37,6 +37,9 @@ dependency_overrides:
dependency_overrides:
watcher: ^1.1.0
dependency_overrides:
watcher: ^1.1.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

View file

@ -42,6 +42,9 @@ dependency_overrides:
dependency_overrides:
watcher: ^1.1.0
dependency_overrides:
watcher: ^1.1.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

View file

@ -367,7 +367,7 @@ class TronClient {
) async {
// This is introduce to server as a limit in cases where feeLimit is 0
// The transaction signing will fail if the feeLimit is explicitly 0.
int defaultFeeLimit = 100000;
int defaultFeeLimit = 269000;
final block = await _provider!.request(TronRequestGetNowBlock());
// Create the transfer contract
@ -401,8 +401,9 @@ class TronClient {
final tronBalanceInt = tronBalance.toInt();
if (feeLimit > tronBalanceInt) {
final feeInTrx = TronHelper.fromSun(BigInt.parse(feeLimit.toString()));
throw Exception(
'You don\'t have enough TRX to cover the transaction fee for this transaction. Kindly top up.',
'You don\'t have enough TRX to cover the transaction fee for this transaction. Kindly top up.\nTransaction fee: $feeInTrx TRX',
);
}
@ -442,6 +443,9 @@ class TronClient {
if (!request.isSuccess) {
log("Tron TRC20 error: ${request.error} \n ${request.respose}");
throw Exception(
'An error occured while creating the transfer request. Please try again.',
);
}
final feeLimit = await getFeeLimit(
@ -454,8 +458,9 @@ class TronClient {
final tronBalanceInt = tronBalance.toInt();
if (feeLimit > tronBalanceInt) {
final feeInTrx = TronHelper.fromSun(BigInt.parse(feeLimit.toString()));
throw Exception(
'You don\'t have enough TRX to cover the transaction fee for this transaction. Kindly top up.',
'You don\'t have enough TRX to cover the transaction fee for this transaction. Kindly top up. Transaction fee: $feeInTrx TRX',
);
}

View file

@ -5,6 +5,7 @@ import 'package:cake_wallet/entities/openalias_record.dart';
import 'package:cake_wallet/entities/parsed_address.dart';
import 'package:cake_wallet/entities/unstoppable_domain_address.dart';
import 'package:cake_wallet/entities/emoji_string_extension.dart';
import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
import 'package:cake_wallet/mastodon/mastodon_api.dart';
import 'package:cake_wallet/nostr/nostr_api.dart';
import 'package:cake_wallet/store/settings_store.dart';
@ -71,8 +72,8 @@ class AddressResolver {
return emailRegex.hasMatch(address);
}
// TODO: refactor this to take Crypto currency instead of ticker, or at least pass in the tag as well
Future<ParsedAddress> resolve(BuildContext context, String text, String ticker) async {
Future<ParsedAddress> resolve(BuildContext context, String text, CryptoCurrency currency) async {
final ticker = currency.title;
try {
if (text.startsWith('@') && !text.substring(1).contains('@')) {
if (settingsStore.lookupsTwitter) {
@ -116,8 +117,7 @@ class AddressResolver {
await MastodonAPI.lookupUserByUserName(userName: userName, apiHost: hostName);
if (mastodonUser != null) {
String? addressFromBio = extractAddressByType(
raw: mastodonUser.note, type: CryptoCurrency.fromString(ticker));
String? addressFromBio = extractAddressByType(raw: mastodonUser.note, type: currency);
if (addressFromBio != null) {
return ParsedAddress.fetchMastodonAddress(
@ -131,8 +131,8 @@ class AddressResolver {
if (pinnedPosts.isNotEmpty) {
final userPinnedPostsText = pinnedPosts.map((item) => item.content).join('\n');
String? addressFromPinnedPost = extractAddressByType(
raw: userPinnedPostsText, type: CryptoCurrency.fromString(ticker));
String? addressFromPinnedPost =
extractAddressByType(raw: userPinnedPostsText, type: currency);
if (addressFromPinnedPost != null) {
return ParsedAddress.fetchMastodonAddress(
@ -162,6 +162,16 @@ class AddressResolver {
}
}
}
final thorChainAddress = await ThorChainExchangeProvider.lookupAddressByName(text);
if (thorChainAddress != null) {
String? address =
thorChainAddress[ticker] ?? (ticker == 'RUNE' ? thorChainAddress['THOR'] : null);
if (address != null) {
return ParsedAddress.thorChainAddress(address: address, name: text);
}
}
final formattedName = OpenaliasRecord.formatDomainName(text);
final domainParts = formattedName.split('.');
final name = domainParts.last;
@ -204,7 +214,7 @@ class AddressResolver {
if (nostrUserData != null) {
String? addressFromBio = extractAddressByType(
raw: nostrUserData.about, type: CryptoCurrency.fromString(ticker));
raw: nostrUserData.about, type: currency);
if (addressFromBio != null) {
return ParsedAddress.nostrAddress(
address: addressFromBio,

View file

@ -11,7 +11,8 @@ enum ParseFrom {
ens,
contact,
mastodon,
nostr
nostr,
thorChain
}
class ParsedAddress {
@ -133,6 +134,14 @@ class ParsedAddress {
);
}
factory ParsedAddress.thorChainAddress({required String address, required String name}) {
return ParsedAddress(
addresses: [address],
name: name,
parseFrom: ParseFrom.thorChain,
);
}
final List<String> addresses;
final String name;
final String description;

View file

@ -34,11 +34,13 @@ class ThorChainExchangeProvider extends ExchangeProvider {
static final isRefundAddressSupported = [CryptoCurrency.eth];
static const _baseURL = 'thornode.ninerealms.com';
static const _baseNodeURL = 'thornode.ninerealms.com';
static const _baseURL = 'midgard.ninerealms.com';
static const _quotePath = '/thorchain/quote/swap';
static const _txInfoPath = '/thorchain/tx/status/';
static const _affiliateName = 'cakewallet';
static const _affiliateBps = '175';
static const _nameLookUpPath= 'v2/thorname/lookup/';
final Box<Trade> tradesStore;
@ -154,7 +156,7 @@ class ThorChainExchangeProvider extends ExchangeProvider {
Future<Trade> findTradeById({required String id}) async {
if (id.isEmpty) throw Exception('Trade id is empty');
final formattedId = id.startsWith('0x') ? id.substring(2) : id;
final uri = Uri.https(_baseURL, '$_txInfoPath$formattedId');
final uri = Uri.https(_baseNodeURL, '$_txInfoPath$formattedId');
final response = await http.get(uri);
if (response.statusCode == 404) {
@ -206,8 +208,35 @@ class ThorChainExchangeProvider extends ExchangeProvider {
);
}
static Future<Map<String, String>?>? lookupAddressByName(String name) async {
final uri = Uri.https(_baseURL, '$_nameLookUpPath$name');
final response = await http.get(uri);
if (response.statusCode != 200) {
return null;
}
final body = json.decode(response.body) as Map<String, dynamic>;
final entries = body['entries'] as List<dynamic>?;
if (entries == null || entries.isEmpty) {
return null;
}
Map<String, String> chainToAddressMap = {};
for (final entry in entries) {
final chain = entry['chain'] as String;
final address = entry['address'] as String;
chainToAddressMap[chain] = address;
}
return chainToAddressMap;
}
Future<Map<String, dynamic>> _getSwapQuote(Map<String, String> params) async {
Uri uri = Uri.https(_baseURL, _quotePath, params);
Uri uri = Uri.https(_baseNodeURL, _quotePath, params);
final response = await http.get(uri);

View file

@ -41,6 +41,7 @@ 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';
import 'package:cw_core/window_size.dart';
final navigatorKey = GlobalKey<NavigatorState>();
final rootKey = GlobalKey<RootState>();
@ -60,6 +61,8 @@ Future<void> main() async {
return true;
};
await setDefaultMinimumWindowSize();
await CakeHive.close();
await initializeAppConfigs();

View file

@ -22,7 +22,7 @@ class DropDownItemWidget extends StatelessWidget {
child: Text(
title,
style: TextStyle(
fontSize: 22,
fontSize: 18,
fontWeight: FontWeight.w500,
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),

View file

@ -330,10 +330,12 @@ class ExchangePage extends BasePage {
void applyTemplate(
BuildContext context, ExchangeViewModel exchangeViewModel, ExchangeTemplate template) async {
exchangeViewModel.changeDepositCurrency(
currency: CryptoCurrency.fromString(template.depositCurrency));
exchangeViewModel.changeReceiveCurrency(
currency: CryptoCurrency.fromString(template.receiveCurrency));
final depositCryptoCurrency = CryptoCurrency.fromString(template.depositCurrency);
final receiveCryptoCurrency = CryptoCurrency.fromString(template.receiveCurrency);
exchangeViewModel.changeDepositCurrency(currency: depositCryptoCurrency);
exchangeViewModel.changeReceiveCurrency(currency: receiveCryptoCurrency);
exchangeViewModel.changeDepositAmount(amount: template.amount);
exchangeViewModel.depositAddress = template.depositAddress;
@ -342,12 +344,10 @@ class ExchangePage extends BasePage {
exchangeViewModel.isFixedRateMode = false;
var domain = template.depositAddress;
var ticker = template.depositCurrency.toLowerCase();
exchangeViewModel.depositAddress = await fetchParsedAddress(context, domain, ticker);
exchangeViewModel.depositAddress = await fetchParsedAddress(context, domain, depositCryptoCurrency);
domain = template.receiveAddress;
ticker = template.receiveCurrency.toLowerCase();
exchangeViewModel.receiveAddress = await fetchParsedAddress(context, domain, ticker);
exchangeViewModel.receiveAddress = await fetchParsedAddress(context, domain, receiveCryptoCurrency);
}
void _setReactions(BuildContext context, ExchangeViewModel exchangeViewModel) {
@ -519,16 +519,14 @@ class ExchangePage extends BasePage {
_depositAddressFocus.addListener(() async {
if (!_depositAddressFocus.hasFocus && depositAddressController.text.isNotEmpty) {
final domain = depositAddressController.text;
final ticker = exchangeViewModel.depositCurrency.title.toLowerCase();
exchangeViewModel.depositAddress = await fetchParsedAddress(context, domain, ticker);
exchangeViewModel.depositAddress = await fetchParsedAddress(context, domain, exchangeViewModel.depositCurrency);
}
});
_receiveAddressFocus.addListener(() async {
if (!_receiveAddressFocus.hasFocus && receiveAddressController.text.isNotEmpty) {
final domain = receiveAddressController.text;
final ticker = exchangeViewModel.receiveCurrency.title.toLowerCase();
exchangeViewModel.receiveAddress = await fetchParsedAddress(context, domain, ticker);
exchangeViewModel.receiveAddress = await fetchParsedAddress(context, domain, exchangeViewModel.receiveCurrency);
}
});
@ -575,8 +573,8 @@ class ExchangePage extends BasePage {
}
}
Future<String> fetchParsedAddress(BuildContext context, String domain, String ticker) async {
final parsedAddress = await getIt.get<AddressResolver>().resolve(context, domain, ticker);
Future<String> fetchParsedAddress(BuildContext context, String domain, CryptoCurrency currency) async {
final parsedAddress = await getIt.get<AddressResolver>().resolve(context, domain, currency);
final address = await extractAddressFromParsed(context, parsedAddress);
return address;
}
@ -664,15 +662,13 @@ class ExchangePage extends BasePage {
addressTextFieldValidator: AddressValidator(type: exchangeViewModel.depositCurrency),
onPushPasteButton: (context) async {
final domain = exchangeViewModel.depositAddress;
final ticker = exchangeViewModel.depositCurrency.title.toLowerCase();
exchangeViewModel.depositAddress =
await fetchParsedAddress(context, domain, ticker);
await fetchParsedAddress(context, domain, exchangeViewModel.depositCurrency);
},
onPushAddressBookButton: (context) async {
final domain = exchangeViewModel.depositAddress;
final ticker = exchangeViewModel.depositCurrency.title.toLowerCase();
exchangeViewModel.depositAddress =
await fetchParsedAddress(context, domain, ticker);
await fetchParsedAddress(context, domain, exchangeViewModel.depositCurrency);
},
));
@ -713,15 +709,13 @@ class ExchangePage extends BasePage {
addressTextFieldValidator: AddressValidator(type: exchangeViewModel.receiveCurrency),
onPushPasteButton: (context) async {
final domain = exchangeViewModel.receiveAddress;
final ticker = exchangeViewModel.receiveCurrency.title.toLowerCase();
exchangeViewModel.receiveAddress =
await fetchParsedAddress(context, domain, ticker);
await fetchParsedAddress(context, domain, exchangeViewModel.receiveCurrency);
},
onPushAddressBookButton: (context) async {
final domain = exchangeViewModel.receiveAddress;
final ticker = exchangeViewModel.receiveCurrency.title.toLowerCase();
exchangeViewModel.receiveAddress =
await fetchParsedAddress(context, domain, ticker);
await fetchParsedAddress(context, domain, exchangeViewModel.receiveCurrency);
},
));

View file

@ -56,6 +56,11 @@ Future<String> extractAddressFromParsed(
profileImageUrl = parsedAddress.profileImageUrl;
profileName = parsedAddress.profileName;
break;
case ParseFrom.thorChain:
title = S.of(context).address_detected;
content = S.of(context).extracted_address_content('${parsedAddress.name} (ThorChain)');
address = parsedAddress.addresses.first;
break;
case ParseFrom.yatRecord:
if (parsedAddress.name.isEmpty) {
title = S.of(context).yat_error;

View file

@ -12,7 +12,6 @@ final _settingsNavigatorKey = GlobalKey<NavigatorState>();
class DesktopSettingsPage extends StatefulWidget {
const DesktopSettingsPage({super.key});
@override
State<DesktopSettingsPage> createState() => _DesktopSettingsPageState();
}
@ -33,22 +32,21 @@ class _DesktopSettingsPageState extends State<DesktopSettingsPage> {
return Scaffold(
body: Container(
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
child: Row(
children: [
Padding(
padding: const EdgeInsets.all(24),
child: Text(
S.current.settings,
style: textXLarge(),
),
),
Expanded(
child: Row(
flex: 1,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(24, 24, 24, 4),
child: Text(
S.current.settings,
style: textXLarge(),
),
),
Expanded(
flex: 1,
child: ListView.separated(
padding: EdgeInsets.only(top: 0),
itemBuilder: (_, index) {
@ -78,27 +76,27 @@ class _DesktopSettingsPageState extends State<DesktopSettingsPage> {
itemCount: itemCount,
),
),
Flexible(
flex: 2,
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: 500),
child: Navigator(
key: _settingsNavigatorKey,
initialRoute: Routes.empty_no_route,
onGenerateRoute: (settings) => Router.createRoute(settings),
onGenerateInitialRoutes:
(NavigatorState navigator, String initialRouteName) {
return [
navigator
.widget.onGenerateRoute!(RouteSettings(name: initialRouteName))!
];
},
),
),
)
],
),
),
Flexible(
flex: 2,
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: 500),
child: Navigator(
key: _settingsNavigatorKey,
initialRoute: Routes.empty_no_route,
onGenerateRoute: (settings) => Router.createRoute(settings),
onGenerateInitialRoutes:
(NavigatorState navigator, String initialRouteName) {
return [
navigator
.widget.onGenerateRoute!(RouteSettings(name: initialRouteName))!
];
},
),
),
)
],
),
),

View file

@ -172,7 +172,7 @@ class WalletListBodyState extends State<WalletListBody> {
maxLines: null,
softWrap: true,
style: TextStyle(
fontSize: 20,
fontSize: DeviceInfo.instance.isDesktop ? 18 : 20,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.extension<CakeTextTheme>()!

View file

@ -296,8 +296,8 @@ abstract class OutputBase with Store {
Future<void> fetchParsedAddress(BuildContext context) async {
final domain = address;
final ticker = cryptoCurrencyHandler().title.toLowerCase();
parsedAddress = await getIt.get<AddressResolver>().resolve(context, domain, ticker);
final currency = cryptoCurrencyHandler();
parsedAddress = await getIt.get<AddressResolver>().resolve(context, domain, currency);
extractedAddress = await extractAddressFromParsed(context, parsedAddress);
note = parsedAddress.description;
}

View file

@ -247,7 +247,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
wallet.type != WalletType.banano &&
wallet.type != WalletType.solana &&
wallet.type != WalletType.tron;
@observable
CryptoCurrency selectedCryptoCurrency;
@ -363,7 +363,8 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
} catch (e) {
if (e is LedgerException) {
final errorCode = e.errorCode.toRadixString(16);
final fallbackMsg = e.message.isNotEmpty ? e.message : "Unexpected Ledger Error Code: $errorCode";
final fallbackMsg =
e.message.isNotEmpty ? e.message : "Unexpected Ledger Error Code: $errorCode";
final errorMsg = ledgerViewModel.interpretErrorCode(errorCode) ?? fallbackMsg;
state = FailureState(errorMsg);
@ -444,7 +445,10 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
Object _credentials() {
final priority = _settingsStore.priority[wallet.type];
if (priority == null && wallet.type != WalletType.nano && wallet.type != WalletType.banano && wallet.type != WalletType.solana &&
if (priority == null &&
wallet.type != WalletType.nano &&
wallet.type != WalletType.banano &&
wallet.type != WalletType.solana &&
wallet.type != WalletType.tron) {
throw Exception('Priority is null for wallet type: ${wallet.type}');
}
@ -570,6 +574,16 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
return errorMessage;
}
if (walletType == WalletType.tron) {
if (errorMessage.contains('balance is not sufficient')) {
return S.current.do_not_have_enough_gas_asset(currency.toString());
}
if (errorMessage.contains('Transaction expired')) {
return 'An error occurred while processing the transaction. Kindly retry the transaction';
}
}
if (walletType == WalletType.bitcoin ||
walletType == WalletType.litecoin ||
walletType == WalletType.bitcoinCash) {

View file

@ -10,6 +10,7 @@ import cw_monero
import device_info_plus
import devicelocale
import flutter_inappwebview_macos
import flutter_local_authentication
import flutter_secure_storage_macos
import in_app_review
import package_info
@ -26,6 +27,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
DevicelocalePlugin.register(with: registry.registrar(forPlugin: "DevicelocalePlugin"))
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
FlutterLocalAuthenticationPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalAuthenticationPlugin"))
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
InAppReviewPlugin.register(with: registry.registrar(forPlugin: "InAppReviewPlugin"))
FLTPackageInfoPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlugin"))

View file

@ -26,6 +26,8 @@ PODS:
- flutter_inappwebview_macos (0.0.1):
- FlutterMacOS
- OrderedSet (~> 5.0)
- flutter_local_authentication (1.2.0):
- FlutterMacOS
- flutter_secure_storage_macos (6.1.1):
- FlutterMacOS
- FlutterMacOS (1.0.0)
@ -56,6 +58,7 @@ DEPENDENCIES:
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
- devicelocale (from `Flutter/ephemeral/.symlinks/plugins/devicelocale/macos`)
- flutter_inappwebview_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos`)
- flutter_local_authentication (from `Flutter/ephemeral/.symlinks/plugins/flutter_local_authentication/macos`)
- flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`)
- FlutterMacOS (from `Flutter/ephemeral`)
- in_app_review (from `Flutter/ephemeral/.symlinks/plugins/in_app_review/macos`)
@ -83,6 +86,8 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/devicelocale/macos
flutter_inappwebview_macos:
:path: Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos
flutter_local_authentication:
:path: Flutter/ephemeral/.symlinks/plugins/flutter_local_authentication/macos
flutter_secure_storage_macos:
:path: Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos
FlutterMacOS:
@ -110,6 +115,7 @@ SPEC CHECKSUMS:
device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f
devicelocale: 9f0f36ac651cabae2c33f32dcff4f32b61c38225
flutter_inappwebview_macos: 9600c9df9fdb346aaa8933812009f8d94304203d
flutter_local_authentication: 85674893931e1c9cfa7c9e4f5973cb8c56b018b0
flutter_secure_storage_macos: d56e2d218c1130b262bef8b4a7d64f88d7f9c9ea
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
in_app_review: a850789fad746e89bce03d4aeee8078b45a53fd0

View file

@ -24,7 +24,21 @@ class AppDelegate: FlutterAppDelegate {
}
result(secRandom(count: count))
case "setMinWindowSize":
guard let self = self else {
result(false)
return
}
if let arguments = call.arguments as? [String: Any],
let width = arguments["width"] as? Double,
let height = arguments["height"] as? Double {
DispatchQueue.main.async {
self.mainFlutterWindow?.minSize = CGSize(width: width, height: height)
}
result(true)
} else {
result(false)
}
default:
result(FlutterMethodNotImplemented)
}