Dashboard desktop view (#737)

* Add build scripts for macOS. Add macos for cw_monero plugin. Add macos proj to the application.

* - Update Flutter secure storage to work with macos
- Enable uni links only on Mobile
- Update devcelocale to work with macos

* Add network access to mac

* Change Dashboard view on desktop size screens

* Add on Tap to desktop_action_button.dart
Remove unused functions

* Fix arch match for monero lib for darwin x86_64 -> x86-64

* Add Bundle ID in entitlements files through app config script

* Update deployment target to 10.13

* Revert back to Cake fork for secure storage

* Revert back to Cake fork for secure storage

* Revert mac os version

* Revert mac os version

* Add platform channel specific code for mac os

* Add desktop sidebar

* [skip ci] Add desktop sidebar

* [skip ci] Add desktop sidebar

* - Remove legacy migration from macos
- Remove wake lock native code and just use the ready made package

* Remove wake lock native code and just use the ready made package

* Remove unstoppable domain from macos since it's not supported

* Temporarily fetch unstoppable domains only on mobile

* refactor desktop settings sidebar

* Ignore increasing brightness for non-mobile platforms

* Add Wallet selection dropdown to dashboard desktop view

* Generate MacOS icons

* localize settings

* fix dashboard sidebar and responsive utils

* Change Mac os app name and bundle id

* Fix exchange page as fullScreenDialog

* Remove constants

* - Refactor onRamper to have a single point of modification
- Enlarge initial app size
- update Flutter and Packages

* Add pubspec.lock and Podfile.lock to gitignore

* Remove Podfile.lock from cache

* Fix bug on sidebar reset

* Fix issues from code review

* [skip ci] reformat desktop dashboard

* [skip ci] reformat desktop dashboard

* Revert removing .lock files

* Revert changes in .gitignore

* [skip ci] remove .project changes

* [skip ci] remove .project changes

* Separate Dashboard desktop view from mobile view

* constraint images and pincoded box

* Remove drawer from mac os

* - Listen to keyboard events in PIN screen
- Fix PIN buttons style

* Fix desktop nav bar UI

* Add Marketplace to dashboard view

* Update trailing icon to open transaction page

* Update widget contraints

* Add empty trailing to center page title on desktop

* Refresh desktop dashboard actions on wallet change

* Change ionia welcome page animation

* Fix Constrained width screens UI

* Refactor sidebar state management

* remove empty line

* Add max width constrain to Welcome page

* Change Exchange page UI depending on platform

* - Change design/paddings for Send page on desktop view
- Make AddTemplateButton instead of having it duplicated in send/exchange

* Fix Desktop dashboard actions background color

* Constrain primary Buttons width

* Make side menu items toggle back to dashboard

* Add padding to support page

* Add width constraints to desktop dashboard

* Fix UI issues, paddings and alignments

* Rename misleading variable
Change initial mac window size

* Fix wallet create in settings

* remove unnecessary code

* remove unnecessary code

* Remove duplicated constrains

* - Use close icon on main screens
- Minor UI fixes

* fix pageview controller reset index

* Add create and restore wallet options to dropdown menu

* Fix desktop background color and address book view issues

* Fix input field

* Add onFieldSubmitted to allow "enter" button interaction

* Fix issue from code review

* Fix Popup width constraint and add focus orders

* Fix variable name

* Fix issues from code review

* refactor dropdown items

* Fix alignment in create and restore wallet screens

* Fix dropdown change state bug

Hide scanner for desktop

* remove space

* override navbar with desktopnavbar

* Remove autofocus

* remove unused code

* Fix ionia input field alignment

* Replace removed code

* Add app lock feature on mac

* Add assertion to avoid null

* Add Nano currency image

* Enable adding contact from send screen

* Fix UI issues
Add missing translation

* pop only PIN screen after successful auth

* Add back wallet settings page to desktop settings actions

* Fix Navigation animation for settings screens

* Fixate MobX version to fix restore issue

* CW-324 Refresh current settings page if wallet changed (#811)

* Fix refresh current settings page if wallet changed

* Fix refresh current settings page if wallet changed

* Refresh Wallet Seeds/Keys List upon wallet change

---------

Co-authored-by: OmarHatem <omarh.ismail1@gmail.com>

* Remove navigation workaround for duplicate key, and fix the issue by handling creation/disposing of global key (#840)

* Cw 323 add wallet list to settings on mac (#843)

* Remove navigation workaround for duplicate key, and fix the issue by handling creation/disposing of global key

* - Register Wallet List as singleton in Desktop to be modify the same instance from settings and dropdown
- General Fixes and Enhancements

* Fix Changing/Restoring wallet from settings

* Fix Create wallet not showing seeds screens if launched from settings

* Add max width constraint for Alerts

* - Add Desktop API keys
- Fix Change back up password issue
- Fix Popup width

* Sync Mac with latest main updates

* Swap Transactions icon with lock icon

* Save backup file locally on desktop

* Sync with latest main updates

* Fix Navigation issues with anonpay

* Update macos build version

* Remove deprecated custom wake lock code for Android

* Remove Legacy CryptoSwift package from MacOS

* - Refactor Payfura page code
- Add OnRamper new configs to onramper_buy_provider.dart
- Fix Conflicts with main

* Updated device locale package

* Update android tools

* Revert changes and update only gradle version

* Downgrade android tools version

* Update gradle version

* Update package/gradle/plugin version

* - Fixate device locale version
- Downgrade gradle version

* Update kotlin version

* Update gradle version

* Trial for a custom fork from devicelocale

* Fixate shared preferences package version

* Revert gradle version

* Revert kotlin version

* Downgrade gradle version

* Downgrade gradle version

* Repair cache and clean before build

* Fixate flutter version

* update google services version

* revert google services version

* Force shared pref android version

* Override shared prefs android package version

* Override shared prefs android package [skip ci]

---------

Co-authored-by: M <m@cakewallet.com>
Co-authored-by: Godwin Asuquo <godilite@gmail.com>
Co-authored-by: Godwin Asuquo <41484542+godilite@users.noreply.github.com>
This commit is contained in:
Omar Hatem
2023-04-14 06:39:08 +02:00
committed by GitHub
co-authored by M Godwin Asuquo Godwin Asuquo
parent 5c4fd78951
commit c5477e4f9e
231 changed files with 9946 additions and 2653 deletions
+25 -8
View File
@@ -1,6 +1,8 @@
import 'dart:io';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/utils/exception_handler.dart';
import 'package:cake_wallet/utils/share_util.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
@@ -27,8 +29,7 @@ class BackupPage extends BasePage {
@override
Widget trailing(BuildContext context) => TrailButton(
caption: S.of(context).change_password,
onPressed: () =>
Navigator.of(context).pushNamed(Routes.editBackupPassword),
onPressed: () => Navigator.of(context).pushNamed(Routes.editBackupPassword),
textColor: Palette.blueCraiola);
@override
@@ -51,9 +52,8 @@ class BackupPage extends BasePage {
child: Observer(
builder: (_) => GestureDetector(
onTap: () {
Clipboard.setData(ClipboardData(
text:
backupViewModelBase.backupPassword));
Clipboard.setData(
ClipboardData(text: backupViewModelBase.backupPassword));
showBar<void>(
context,
S.of(context).transaction_details_copied(
@@ -108,8 +108,10 @@ class BackupPage extends BasePage {
if (Platform.isAndroid) {
onExportAndroid(context, backup);
} else {
} else if (Platform.isIOS) {
await share(backup, context);
} else {
await _saveFile(backup);
}
},
actionLeftButton: () => Navigator.of(dialogContext).pop());
@@ -133,8 +135,7 @@ class BackupPage extends BasePage {
return;
}
await backupViewModelBase.saveToDownload(
backup.name, backup.content);
await backupViewModelBase.saveToDownload(backup.name, backup.content);
Navigator.of(dialogContext).pop();
},
actionLeftButton: () async {
@@ -149,4 +150,20 @@ class BackupPage extends BasePage {
await ShareUtil.shareFile(filePath: path, fileName: backup.name, context: context);
await backupViewModelBase.removeBackupFileLocally(backup);
}
Future<void> _saveFile(BackupExportFile backup) async {
String? outputFile = await FilePicker.platform
.saveFile(dialogTitle: 'Save Your File to desired location', fileName: backup.name);
try {
File returnedFile = File(outputFile!);
await returnedFile.writeAsBytes(backup.content);
} catch (exception, stackTrace) {
ExceptionHandler.onError(FlutterErrorDetails(
exception: exception,
stack: stackTrace,
library: "Export Backup",
));
}
}
}
@@ -66,7 +66,8 @@ class EditBackupPasswordPage extends BasePage {
leftButtonText: S.of(context).cancel,
actionRightButton: () async {
await editBackupPasswordViewModel.save();
Navigator.of(dialogContext)..pop()..pop();
Navigator.of(dialogContext).pop();
Navigator.of(context).pop();
},
actionLeftButton: () => Navigator.of(dialogContext).pop());
});
+15 -10
View File
@@ -1,4 +1,5 @@
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart';
@@ -20,7 +21,7 @@ abstract class BasePage extends StatelessWidget {
String? get title => null;
bool get isModalBackButton => false;
bool get canUseCloseIcon => false;
Color get backgroundLightColor => Colors.white;
@@ -50,23 +51,27 @@ abstract class BasePage extends StatelessWidget {
}
final _backButton = Icon(Icons.arrow_back_ios,
color: titleColor ?? Theme.of(context).primaryTextTheme!.headline6!.color!,
color: titleColor ?? Theme.of(context).primaryTextTheme.headline6!.color!,
size: 16,);
final _closeButton = currentTheme.type == ThemeType.dark
? closeButtonImageDarkTheme : closeButtonImage;
bool isMobileView = ResponsiveLayoutUtil.instance.isMobile(context);
return SizedBox(
height: 37,
width: 37,
height: isMobileView ? 37 : 45,
width: isMobileView ? 37 : 45,
child: ButtonTheme(
minWidth: double.minPositive,
child: TextButton(
// FIX-ME: Style
//highlightColor: Colors.transparent,
//splashColor: Colors.transparent,
//padding: EdgeInsets.all(0),
style: ButtonStyle(
overlayColor: MaterialStateColor.resolveWith((states) => Colors.transparent),
),
onPressed: () => onClose(context),
child: isModalBackButton ? _closeButton : _backButton),
child: canUseCloseIcon && !isMobileView
? _closeButton
: _backButton,
),
),
);
}
@@ -92,7 +97,7 @@ abstract class BasePage extends StatelessWidget {
ObstructingPreferredSizeWidget appBar(BuildContext context) {
final appBarColor = currentTheme.type == ThemeType.dark
? backgroundDarkColor : backgroundLightColor;
switch (appBarStyle) {
case AppBarStyle.regular:
// FIX-ME: NavBar no context
+7 -37
View File
@@ -1,60 +1,30 @@
import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:permission_handler/permission_handler.dart';
class OnRamperPage extends BasePage {
OnRamperPage({required this.settingsStore, required this.wallet});
OnRamperPage(this._onRamperBuyProvider);
final SettingsStore settingsStore;
final WalletBase wallet;
final OnRamperBuyProvider _onRamperBuyProvider;
@override
String get title => S.current.buy;
@override
Widget body(BuildContext context) {
final darkMode = Theme.of(context).brightness == Brightness.dark;
return OnRamperPageBody(
settingsStore: settingsStore,
wallet: wallet,
darkMode: darkMode,
backgroundColor: darkMode ? backgroundDarkColor : backgroundLightColor,
supportSell: false,
supportSwap: false);
return OnRamperPageBody(_onRamperBuyProvider);
}
}
class OnRamperPageBody extends StatefulWidget {
OnRamperPageBody(
{required this.settingsStore,
required this.wallet,
required this.darkMode,
required this.supportSell,
required this.supportSwap,
required this.backgroundColor});
OnRamperPageBody(this.onRamperBuyProvider);
static const baseUrl = 'widget.onramper.com';
final SettingsStore settingsStore;
final WalletBase wallet;
final Color backgroundColor;
final bool darkMode;
final bool supportSell;
final bool supportSwap;
final OnRamperBuyProvider onRamperBuyProvider;
Uri get uri => Uri.https(baseUrl, '', <String, dynamic>{
'apiKey': secrets.onramperApiKey,
'defaultCrypto': wallet.currency.title,
'defaultFiat': settingsStore.fiatCurrency.title,
'wallets': '${wallet.currency.title}:${wallet.walletAddresses.address}',
'darkMode': darkMode.toString(),
'supportSell': supportSell.toString(),
'supportSwap': supportSwap.toString()
});
Uri get uri => onRamperBuyProvider.requestUrl();
@override
OnRamperPageBodyState createState() => OnRamperPageBodyState();
+7 -22
View File
@@ -1,45 +1,30 @@
import 'package:cake_wallet/buy/payfura/payfura_buy_provider.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:permission_handler/permission_handler.dart';
class PayFuraPage extends BasePage {
PayFuraPage({required this.settingsStore, required this.wallet});
PayFuraPage(this._PayfuraBuyProvider);
final SettingsStore settingsStore;
final WalletBase wallet;
final PayfuraBuyProvider _PayfuraBuyProvider;
@override
String get title => S.current.buy;
@override
Widget body(BuildContext context) {
return PayFuraPageBody(
settingsStore: settingsStore,
wallet: wallet);
return PayFuraPageBody(_PayfuraBuyProvider);
}
}
class PayFuraPageBody extends StatefulWidget {
PayFuraPageBody(
{required this.settingsStore,
required this.wallet});
PayFuraPageBody(this._PayfuraBuyProvider);
static const baseUrl = 'exchange.payfura.com';
final SettingsStore settingsStore;
final WalletBase wallet;
final PayfuraBuyProvider _PayfuraBuyProvider;
Uri get uri => Uri.https(baseUrl, '', <String, dynamic>{
'apiKey': secrets.payfuraApiKey,
'to': wallet.currency.title,
'from': settingsStore.fiatCurrency.title,
'walletAddress': '${wallet.currency.title}:${wallet.walletAddresses.address}',
'mode': 'buy'
});
Uri get uri => _PayfuraBuyProvider.requestUrl();
@override
PayFuraPageBodyState createState() => PayFuraPageBodyState();
@@ -24,10 +24,6 @@ class ContactListPage extends BasePage {
@override
Widget? trailing(BuildContext context) {
if (!contactListViewModel.isEditable) {
return null;
}
return Container(
width: 32.0,
height: 32.0,
+118 -194
View File
@@ -1,12 +1,15 @@
import 'dart:async';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/entities/main_actions.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/market_place_page.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/yat_emoji_id.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
@@ -21,17 +24,41 @@ import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
import 'package:cake_wallet/main.dart';
import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart';
import 'package:url_launcher/url_launcher.dart';
class DashboardPage extends BasePage {
class DashboardPage extends StatelessWidget {
DashboardPage({
required this.balancePage,
required this.walletViewModel,
required this.dashboardViewModel,
required this.addressListViewModel,
});
final BalancePage balancePage;
final DashboardViewModel dashboardViewModel;
final WalletAddressListViewModel addressListViewModel;
@override
Widget build(BuildContext context) {
return Scaffold(
body: ResponsiveLayoutUtil.instance.isMobile(context)
? _DashboardPageView(
balancePage: balancePage,
dashboardViewModel: dashboardViewModel,
addressListViewModel: addressListViewModel,
)
: getIt.get<DesktopSidebarWrapper>(),
);
}
}
class _DashboardPageView extends BasePage {
_DashboardPageView({
required this.balancePage,
required this.dashboardViewModel,
required this.addressListViewModel,
});
final BalancePage balancePage;
@override
Color get backgroundLightColor =>
currentTheme.type == ThemeType.bright ? Colors.transparent : Colors.white;
@@ -54,19 +81,19 @@ class DashboardPage extends BasePage {
bool get resizeToAvoidBottomInset => false;
@override
Widget get endDrawer => MenuWidget(walletViewModel);
Widget get endDrawer => MenuWidget(dashboardViewModel);
@override
Widget middle(BuildContext context) {
return SyncIndicator(dashboardViewModel: walletViewModel,
onTap: () => Navigator.of(context, rootNavigator: true)
.pushNamed(Routes.connectionSync));
return SyncIndicator(
dashboardViewModel: dashboardViewModel,
onTap: () => Navigator.of(context, rootNavigator: true).pushNamed(Routes.connectionSync));
}
@override
Widget trailing(BuildContext context) {
final menuButton = Image.asset('assets/images/menu.png',
color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!);
color: Theme.of(context).accentTextTheme.headline2!.backgroundColor!);
return Container(
alignment: Alignment.centerRight,
@@ -80,7 +107,7 @@ class DashboardPage extends BasePage {
child: menuButton));
}
final DashboardViewModel walletViewModel;
final DashboardViewModel dashboardViewModel;
final WalletAddressListViewModel addressListViewModel;
final controller = PageController(initialPage: 1);
@@ -90,141 +117,97 @@ class DashboardPage extends BasePage {
@override
Widget body(BuildContext context) {
final sendImage = Image.asset('assets/images/upload.png',
height: 24,
width: 24,
color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!);
final receiveImage = Image.asset('assets/images/received.png',
height: 24,
width: 24,
color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!);
_setEffects(context);
return SafeArea(
minimum: EdgeInsets.only(bottom: 24),
minimum: EdgeInsets.only(bottom: 24),
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(
child: PageView.builder(
controller: controller,
itemCount: pages.length,
itemBuilder: (context, index) => pages[index])),
Padding(
padding: EdgeInsets.only(bottom: 24, top: 10),
child: SmoothPageIndicator(
controller: controller,
count: pages.length,
effect: ColorTransitionEffect(
spacing: 6.0,
radius: 6.0,
dotWidth: 6.0,
dotHeight: 6.0,
dotColor: Theme.of(context).indicatorColor,
activeDotColor: Theme.of(context)
.accentTextTheme!
.headline4!
.backgroundColor!),
)),
Observer(builder: (_) {
return ClipRect(
child:Container(
margin: const EdgeInsets.only(left: 16, right: 16),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50.0),
border: Border.all(color: currentTheme.type == ThemeType.bright ? Color.fromRGBO(255, 255, 255, 0.2): Colors.transparent, width: 1, ),
color:Theme.of(context).textTheme!.headline6!.backgroundColor!),
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(
child: PageView.builder(
controller: controller,
itemCount: pages.length,
itemBuilder: (context, index) => pages[index])),
Padding(
padding: EdgeInsets.only(bottom: 24, top: 10),
child: SmoothPageIndicator(
controller: controller,
count: pages.length,
effect: ColorTransitionEffect(
spacing: 6.0,
radius: 6.0,
dotWidth: 6.0,
dotHeight: 6.0,
dotColor: Theme.of(context).indicatorColor,
activeDotColor:
Theme.of(context).accentTextTheme!.headline4!.backgroundColor!),
)),
Observer(builder: (_) {
return ClipRect(
child: Container(
padding: EdgeInsets.only(left: 32, right: 32),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
if (walletViewModel.hasBuyAction)
ActionButton(
image: Image.asset('assets/images/buy.png',
height: 24,
width: 24,
color: !walletViewModel.isEnabledBuyAction
? Theme.of(context)
.accentTextTheme!
.headline3!
.backgroundColor!
: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!),
title: S.of(context).buy,
onClick: () async => await _onClickBuyButton(context),
textColor: !walletViewModel.isEnabledBuyAction
? Theme.of(context)
.accentTextTheme!
.headline3!
.backgroundColor!
: null),
ActionButton(
image: receiveImage,
title: S.of(context).receive,
route: Routes.addressPage),
if (walletViewModel.hasExchangeAction)
ActionButton(
image: Image.asset('assets/images/transfer.png',
height: 24,
width: 24,
color: !walletViewModel.isEnabledExchangeAction
? Theme.of(context)
.accentTextTheme!
.headline3!
.backgroundColor!
: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!),
title: S.of(context).exchange,
onClick: () async => _onClickExchangeButton(context),
textColor: !walletViewModel.isEnabledExchangeAction
? Theme.of(context)
.accentTextTheme!
.headline3!
.backgroundColor!
: null),
ActionButton(
image: sendImage,
title: S.of(context).send,
route: Routes.send),
if (walletViewModel.hasSellAction)
ActionButton(
image: Image.asset('assets/images/sell.png',
height: 24,
width: 24,
color: !walletViewModel.isEnabledSellAction
? Theme.of(context)
.accentTextTheme!
.headline3!
.backgroundColor!
: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!),
title: S.of(context).sell,
onClick: () async => await _onClickSellButton(context),
textColor: !walletViewModel.isEnabledSellAction
? Theme.of(context)
.accentTextTheme!
.headline3!
.backgroundColor!
: null),
],
),),
),),);
}),
],
));
margin: const EdgeInsets.only(left: 16, right: 16),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50.0),
border: Border.all(
color: currentTheme.type == ThemeType.bright
? Color.fromRGBO(255, 255, 255, 0.2)
: Colors.transparent,
width: 1,
),
color: Theme.of(context).textTheme.headline6!.backgroundColor!,
),
child: Container(
padding: EdgeInsets.only(left: 32, right: 32),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: MainActions.all
.where((element) => element.canShow?.call(dashboardViewModel) ?? true)
.map((action) => ActionButton(
image: Image.asset(action.image,
height: 24,
width: 24,
color: action.isEnabled?.call(dashboardViewModel) ?? true
? Theme.of(context)
.accentTextTheme
.headline2!
.backgroundColor!
: Theme.of(context)
.accentTextTheme
.headline3!
.backgroundColor!),
title: action.name(context),
onClick: () async => await action.onTap(context, dashboardViewModel),
textColor: action.isEnabled?.call(dashboardViewModel) ?? true
? null
: Theme.of(context)
.accentTextTheme
.headline3!
.backgroundColor!,
))
.toList(),
),
),
),
),
);
}),
],
));
}
void _setEffects(BuildContext context) async {
if (_isEffectsInstalled) {
return;
}
pages.add(MarketPlacePage(dashboardViewModel: walletViewModel));
pages.add(MarketPlacePage(dashboardViewModel: dashboardViewModel));
pages.add(balancePage);
pages.add(TransactionsPage(dashboardViewModel: walletViewModel));
pages.add(TransactionsPage(dashboardViewModel: dashboardViewModel));
_isEffectsInstalled = true;
autorun((_) async {
if (!walletViewModel.isOutdatedElectrumWallet) {
if (!dashboardViewModel.isOutdatedElectrumWallet) {
return;
}
@@ -235,8 +218,7 @@ class DashboardPage extends BasePage {
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.of(context).pre_seed_title,
alertContent:
S.of(context).outdated_electrum_wallet_description,
alertContent: S.of(context).outdated_electrum_wallet_description,
buttonText: S.of(context).understand,
buttonAction: () => Navigator.of(context).pop());
});
@@ -253,13 +235,13 @@ class DashboardPage extends BasePage {
Future<void>.delayed(Duration(milliseconds: 500)).then((_) {
showPopUp<void>(
context: navigatorKey.currentContext!,
builder: (_) => YatEmojiId(walletViewModel.yatStore.emoji));
builder: (_) => YatEmojiId(dashboardViewModel.yatStore.emoji));
needToPresentYat = false;
});
}
});
walletViewModel.yatStore.emojiIncommingStream.listen((String emoji) {
dashboardViewModel.yatStore.emojiIncommingStream.listen((String emoji) {
if (!_isEffectsInstalled || emoji.isEmpty) {
return;
}
@@ -267,62 +249,4 @@ class DashboardPage extends BasePage {
needToPresentYat = true;
});
}
Future<void> _onClickBuyButton(BuildContext context) async {
final walletType = walletViewModel.type;
switch (walletType) {
case WalletType.bitcoin:
Navigator.of(context).pushNamed(Routes.onramperPage);
break;
case WalletType.litecoin:
Navigator.of(context).pushNamed(Routes.onramperPage);
break;
case WalletType.monero:
Navigator.of(context).pushNamed(Routes.payfuraPage);
break;
default:
await showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.of(context).buy,
alertContent: S.of(context).buy_alert_content,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
});
}
}
Future<void> _onClickSellButton(BuildContext context) async {
final walletType = walletViewModel.type;
switch (walletType) {
case WalletType.bitcoin:
final moonPaySellProvider = MoonPaySellProvider();
final uri = await moonPaySellProvider.requestUrl(
currency: walletViewModel.wallet.currency,
refundWalletAddress:
walletViewModel.wallet.walletAddresses.address);
await launch(uri);
break;
default:
await showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.of(context).sell,
alertContent: isMoneroOnly ? S.of(context).sell_monero_com_alert_content
: S.of(context).sell_alert_content,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
});
}
}
Future<void> _onClickExchangeButton(BuildContext context) async {
if (walletViewModel.isEnabledExchangeAction) {
await Navigator.of(context).pushNamed(Routes.exchange);
}
}
}
@@ -0,0 +1,111 @@
import 'dart:async';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/yat_emoji_id.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/balance_page.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/main.dart';
import 'package:cake_wallet/router.dart' as Router;
class DesktopDashboardPage extends StatelessWidget {
DesktopDashboardPage({
required this.balancePage,
required this.dashboardViewModel,
required this.addressListViewModel,
required this.desktopKey,
});
final BalancePage balancePage;
final DashboardViewModel dashboardViewModel;
final WalletAddressListViewModel addressListViewModel;
final GlobalKey<NavigatorState> desktopKey;
bool _isEffectsInstalled = false;
StreamSubscription<bool>? _onInactiveSub;
@override
Widget build(BuildContext context) {
_setEffects(context);
return Container(
color: Theme.of(context).backgroundColor,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 400,
child: balancePage,
),
Flexible(
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: 500),
child: Navigator(
key: desktopKey,
initialRoute: Routes.desktop_actions,
onGenerateRoute: (settings) => Router.createRoute(settings),
onGenerateInitialRoutes: (NavigatorState navigator, String initialRouteName) {
return [
navigator.widget.onGenerateRoute!(RouteSettings(name: initialRouteName))!
];
},
),
),
),
],
),
);
}
void _setEffects(BuildContext context) async {
if (_isEffectsInstalled) {
return;
}
_isEffectsInstalled = true;
autorun((_) async {
if (!dashboardViewModel.isOutdatedElectrumWallet) {
return;
}
await Future<void>.delayed(Duration(seconds: 1));
await showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.of(context).pre_seed_title,
alertContent: S.of(context).outdated_electrum_wallet_description,
buttonText: S.of(context).understand,
buttonAction: () => Navigator.of(context).pop());
});
});
var needToPresentYat = false;
var isInactive = false;
_onInactiveSub = rootKey.currentState!.isInactive.listen((inactive) {
isInactive = inactive;
if (needToPresentYat) {
Future<void>.delayed(Duration(milliseconds: 500)).then((_) {
showPopUp<void>(
context: navigatorKey.currentContext!,
builder: (_) => YatEmojiId(dashboardViewModel.yatStore.emoji));
needToPresentYat = false;
});
}
});
dashboardViewModel.yatStore.emojiIncommingStream.listen((String emoji) {
if (!_isEffectsInstalled || emoji.isEmpty) {
return;
}
needToPresentYat = true;
});
}
}
@@ -0,0 +1,69 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
class DesktopActionButton extends StatelessWidget {
final String image;
final String title;
final bool canShow;
final bool isEnabled;
final Function() onTap;
const DesktopActionButton({
Key? key,
required this.title,
required this.image,
required this.onTap,
bool? canShow,
bool? isEnabled,
}) : this.isEnabled = isEnabled ?? true,
this.canShow = canShow ?? true,
super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(8, 0, 8, 8),
child: GestureDetector(
onTap: onTap,
child: Container(
padding: EdgeInsets.symmetric(vertical: 25),
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15.0),
color: Theme.of(context).textTheme.headline6!.backgroundColor!,
),
child: Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset(
image,
height: 30,
width: 30,
color: isEnabled
? Theme.of(context).accentTextTheme.headline2!.backgroundColor!
: Theme.of(context).accentTextTheme.headline3!.backgroundColor!,
),
const SizedBox(width: 10),
AutoSizeText(
title,
style: TextStyle(
fontSize: 24,
fontFamily: 'Lato',
fontWeight: FontWeight.bold,
color: isEnabled
? Theme.of(context).accentTextTheme.headline2!.backgroundColor!
: null,
height: 1,
),
maxLines: 1,
textAlign: TextAlign.center,
)
],
),
),
),
),
);
}
}
@@ -0,0 +1,80 @@
import 'package:cake_wallet/entities/main_actions.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_action_button.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/market_place_page.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
class DesktopDashboardActions extends StatelessWidget {
final DashboardViewModel dashboardViewModel;
const DesktopDashboardActions(this.dashboardViewModel, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Observer(
builder: (_) {
return Column(
children: [
const SizedBox(height: 16),
DesktopActionButton(
title: MainActions.exchangeAction.name(context),
image: MainActions.exchangeAction.image,
canShow: MainActions.exchangeAction.canShow?.call(dashboardViewModel),
isEnabled: MainActions.exchangeAction.isEnabled?.call(dashboardViewModel),
onTap: () async => await MainActions.exchangeAction.onTap(context, dashboardViewModel),
),
Row(
children: [
Expanded(
child: DesktopActionButton(
title: MainActions.receiveAction.name(context),
image: MainActions.receiveAction.image,
canShow: MainActions.receiveAction.canShow?.call(dashboardViewModel),
isEnabled: MainActions.receiveAction.isEnabled?.call(dashboardViewModel),
onTap: () async =>
await MainActions.receiveAction.onTap(context, dashboardViewModel),
),
),
Expanded(
child: DesktopActionButton(
title: MainActions.sendAction.name(context),
image: MainActions.sendAction.image,
canShow: MainActions.sendAction.canShow?.call(dashboardViewModel),
isEnabled: MainActions.sendAction.isEnabled?.call(dashboardViewModel),
onTap: () async => await MainActions.sendAction.onTap(context, dashboardViewModel),
),
),
],
),
Row(
children: [
Expanded(
child: DesktopActionButton(
title: MainActions.buyAction.name(context),
image: MainActions.buyAction.image,
canShow: MainActions.buyAction.canShow?.call(dashboardViewModel),
isEnabled: MainActions.buyAction.isEnabled?.call(dashboardViewModel),
onTap: () async => await MainActions.buyAction.onTap(context, dashboardViewModel),
),
),
Expanded(
child: DesktopActionButton(
title: MainActions.sellAction.name(context),
image: MainActions.sellAction.image,
canShow: MainActions.sellAction.canShow?.call(dashboardViewModel),
isEnabled: MainActions.sellAction.isEnabled?.call(dashboardViewModel),
onTap: () async => await MainActions.sellAction.onTap(context, dashboardViewModel),
),
),
],
),
Expanded(
child: MarketPlacePage(dashboardViewModel: dashboardViewModel),
),
],
);
}
);
}
}
@@ -0,0 +1,48 @@
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class DesktopDashboardNavbar extends StatelessWidget implements ObstructingPreferredSizeWidget {
final Widget leading;
final Widget middle;
final Widget trailing;
DesktopDashboardNavbar({
super.key,
required this.leading,
required this.middle,
required this.trailing,
});
ThemeBase get currentTheme => getIt.get<SettingsStore>().currentTheme;
@override
Widget build(BuildContext context) {
final appBarColor =
currentTheme.type == ThemeType.dark ? Colors.black.withOpacity(0.1) : Colors.white;
return Container(
padding: const EdgeInsetsDirectional.only(end: 24),
color: appBarColor,
child: Center(
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(child: leading),
middle,
trailing,
],
),
),
);
}
@override
Size get preferredSize => Size.fromHeight(60);
@override
bool shouldFullyObstruct(BuildContext context) => false;
}
@@ -0,0 +1,32 @@
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar/side_menu_item.dart';
import 'package:flutter/material.dart';
class SideMenu extends StatelessWidget {
const SideMenu({
super.key,
required this.topItems,
required this.bottomItems,
required this.width,
});
final List<SideMenuItem> topItems;
final List<SideMenuItem> bottomItems;
final double width;
@override
Widget build(BuildContext context) {
return Container(
color: Colors.black.withOpacity(0.1),
width: width,
height: MediaQuery.of(context).size.height,
child: Column(
children: [
...topItems,
Spacer(),
...bottomItems,
SizedBox(height: 30),
],
),
);
}
}
@@ -0,0 +1,50 @@
import 'package:flutter/material.dart';
class SideMenuItem extends StatelessWidget {
const SideMenuItem({
Key? key,
required this.onTap,
this.imagePath,
this.icon,
this.isSelected = false,
}) : assert((icon != null && imagePath == null) || (icon == null && imagePath != null));
final void Function() onTap;
final String? imagePath;
final IconData? icon;
final bool isSelected;
Color _setColor(BuildContext context) {
if (isSelected) {
return Theme.of(context).primaryTextTheme.headline6!.color!;
} else {
return Theme.of(context).highlightColor;
}
}
@override
Widget build(BuildContext context) {
return InkWell(
child: Padding(
padding: EdgeInsets.all(20),
child: icon != null
? Icon(
icon,
color: _setColor(context),
)
: Image.asset(
imagePath ?? '',
fit: BoxFit.cover,
height: 30,
width: 30,
color: _setColor(context),
),
),
onTap: () => onTap.call(),
highlightColor: Colors.transparent,
focusColor: Colors.transparent,
hoverColor: Colors.transparent,
splashColor: Colors.transparent,
);
}
}
@@ -0,0 +1,175 @@
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_dashboard_navbar.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar/side_menu.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar/side_menu_item.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/desktop_sidebar_view_model.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/router.dart' as Router;
import 'package:mobx/mobx.dart';
class DesktopSidebarWrapper extends BasePage {
final Widget child;
final DesktopSidebarViewModel desktopSidebarViewModel;
final DashboardViewModel dashboardViewModel;
final GlobalKey<NavigatorState> desktopNavigatorKey;
DesktopSidebarWrapper({
required this.child,
required this.desktopSidebarViewModel,
required this.dashboardViewModel,
required this.desktopNavigatorKey,
});
@override
ObstructingPreferredSizeWidget appBar(BuildContext context) => DesktopDashboardNavbar(
leading: Padding(
padding: EdgeInsets.only(left: sideMenuWidth),
child: getIt<DesktopWalletSelectionDropDown>(),
),
middle: SyncIndicator(
dashboardViewModel: dashboardViewModel,
onTap: () => Navigator.of(context, rootNavigator: true).pushNamed(Routes.connectionSync),
),
trailing: InkWell(
onTap: () {
Navigator.of(context).pushNamed(
Routes.unlock,
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) {
if (isAuthenticatedSuccessfully) {
auth.close();
}
},
);
},
child: Icon(Icons.lock_outline),
),
);
@override
bool get resizeToAvoidBottomInset => false;
final pageController = PageController();
final selectedIconPath = 'assets/images/desktop_transactions_solid_icon.png';
final unselectedIconPath = 'assets/images/desktop_transactions_outline_icon.png';
double get sideMenuWidth => 76.0;
@override
Widget body(BuildContext context) {
_setEffects();
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Observer(builder: (_) {
return SideMenu(
width: sideMenuWidth,
topItems: [
SideMenuItem(
imagePath: 'assets/images/wallet_outline.png',
isSelected: desktopSidebarViewModel.currentPage == SidebarItem.dashboard,
onTap: () => desktopSidebarViewModel.onPageChange(SidebarItem.dashboard),
),
SideMenuItem(
onTap: () {
String? currentPath;
desktopNavigatorKey.currentState?.popUntil((route) {
currentPath = route.settings.name;
return true;
});
switch (currentPath) {
case Routes.transactionsPage:
desktopSidebarViewModel.resetSidebar();
break;
default:
desktopSidebarViewModel.resetSidebar();
Future.delayed(Duration(milliseconds: 10), () {
desktopSidebarViewModel.onPageChange(SidebarItem.transactions);
desktopNavigatorKey.currentState?.pushNamed(Routes.transactionsPage);
});
}
},
isSelected: desktopSidebarViewModel.currentPage == SidebarItem.transactions,
imagePath: desktopSidebarViewModel.currentPage == SidebarItem.transactions
? selectedIconPath
: unselectedIconPath,
),
],
bottomItems: [
SideMenuItem(
imagePath: 'assets/images/support_icon.png',
isSelected: desktopSidebarViewModel.currentPage == SidebarItem.support,
onTap: () => desktopSidebarViewModel.onPageChange(SidebarItem.support)),
SideMenuItem(
imagePath: 'assets/images/settings_outline.png',
isSelected: desktopSidebarViewModel.currentPage == SidebarItem.settings,
onTap: () => desktopSidebarViewModel.onPageChange(SidebarItem.settings),
),
],
);
}),
Expanded(
child: PageView(
controller: pageController,
physics: NeverScrollableScrollPhysics(),
children: [
child,
Container(
color: Theme.of(context).backgroundColor,
padding: EdgeInsets.all(20),
child: Navigator(
initialRoute: Routes.support,
onGenerateRoute: (settings) => Router.createRoute(settings),
onGenerateInitialRoutes: (NavigatorState navigator, String initialRouteName) {
return [
navigator.widget.onGenerateRoute!(RouteSettings(name: initialRouteName))!
];
},
),
),
Navigator(
initialRoute: Routes.desktop_settings_page,
onGenerateRoute: (settings) => Router.createRoute(settings),
onGenerateInitialRoutes: (NavigatorState navigator, String initialRouteName) {
return [
navigator.widget.onGenerateRoute!(RouteSettings(name: initialRouteName))!
];
},
),
],
),
),
],
);
}
void _setEffects() async {
reaction<SidebarItem>((_) => desktopSidebarViewModel.currentPage, (page) {
String? currentPath;
desktopNavigatorKey.currentState?.popUntil((route) {
currentPath = route.settings.name;
return true;
});
if (page == SidebarItem.transactions) {
return;
}
if (currentPath == Routes.transactionsPage) {
Navigator.of(desktopNavigatorKey.currentContext!).pop();
}
pageController.jumpToPage(page.index);
});
}
}
@@ -0,0 +1,199 @@
import 'package:another_flushbar/flushbar.dart';
import 'package:cake_wallet/entities/desktop_dropdown_item.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/dropdown_item_widget.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
class DesktopWalletSelectionDropDown extends StatefulWidget {
final WalletListViewModel walletListViewModel;
DesktopWalletSelectionDropDown(this.walletListViewModel, {Key? key}) : super(key: key);
@override
State<DesktopWalletSelectionDropDown> createState() => _DesktopWalletSelectionDropDownState();
}
class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionDropDown> {
final moneroIcon = Image.asset('assets/images/monero_logo.png', height: 24, width: 24);
final bitcoinIcon = Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
final litecoinIcon = Image.asset('assets/images/litecoin_icon.png', height: 24, width: 24);
final havenIcon = Image.asset('assets/images/haven_logo.png', height: 24, width: 24);
final nonWalletTypeIcon = Image.asset('assets/images/close.png', height: 24, width: 24);
Image _newWalletImage(BuildContext context) => Image.asset(
'assets/images/new_wallet.png',
height: 12,
width: 12,
color: Theme.of(context).primaryTextTheme.headline6!.color!,
);
Image _restoreWalletImage(BuildContext context) => Image.asset(
'assets/images/restore_wallet.png',
height: 12,
width: 12,
color: Theme.of(context).primaryTextTheme.headline6!.color!,
);
Flushbar<void>? _progressBar;
@override
Widget build(BuildContext context) {
final themeData = Theme.of(context);
return Observer(builder: (context) {
final dropDownItems = [
...widget.walletListViewModel.wallets
.map((wallet) => DesktopDropdownItem(
isSelected: wallet.isCurrent,
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: 500),
child: DropDownItemWidget(
title: wallet.name,
image: wallet.isEnabled ? _imageFor(type: wallet.type) : nonWalletTypeIcon),
),
onSelected: () => _onSelectedWallet(wallet),
))
.toList(),
DesktopDropdownItem(
onSelected: () => _navigateToCreateWallet(),
child: DropDownItemWidget(
title: S.of(context).create_new,
image: _newWalletImage(context),
),
),
DesktopDropdownItem(
onSelected: () => _navigateToRestoreWallet(),
child: DropDownItemWidget(
title: S.of(context).restore_wallet,
image: _restoreWalletImage(context),
),
),
];
return DropdownButton<DesktopDropdownItem>(
items: dropDownItems
.map(
(wallet) => DropdownMenuItem<DesktopDropdownItem>(
child: wallet.child,
value: wallet,
),
)
.toList(),
onChanged: (item) {
item?.onSelected();
},
dropdownColor: themeData.textTheme.bodyText1?.decorationColor,
style: TextStyle(color: themeData.primaryTextTheme.headline6?.color),
selectedItemBuilder: (context) => dropDownItems.map((item) => item.child).toList(),
value: dropDownItems.firstWhere((element) => element.isSelected),
underline: const SizedBox(),
focusColor: Colors.transparent,
borderRadius: BorderRadius.circular(15.0),
);
});
}
void _onSelectedWallet(WalletListItem selectedWallet) async {
if (selectedWallet.isCurrent || !selectedWallet.isEnabled) {
return;
}
final confirmed = await showPopUp<bool>(
context: context,
builder: (dialogContext) {
return AlertWithTwoActions(
alertTitle: S.of(context).change_wallet_alert_title,
alertContent: S.of(context).change_wallet_alert_content(selectedWallet.name),
leftButtonText: S.of(context).cancel,
rightButtonText: S.of(context).change,
actionLeftButton: () => Navigator.of(context).pop(false),
actionRightButton: () => Navigator.of(context).pop(true));
}) ??
false;
if (confirmed) {
await _loadWallet(selectedWallet);
}
}
Image _imageFor({required WalletType type}) {
switch (type) {
case WalletType.bitcoin:
return bitcoinIcon;
case WalletType.monero:
return moneroIcon;
case WalletType.litecoin:
return litecoinIcon;
case WalletType.haven:
return havenIcon;
default:
return nonWalletTypeIcon;
}
}
Future<void> _loadWallet(WalletListItem wallet) async {
if (await widget.walletListViewModel.checkIfAuthRequired()) {
await Navigator.of(context).pushNamed(Routes.auth,
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) async {
if (!isAuthenticatedSuccessfully) {
return;
}
try {
auth.changeProcessText(S.of(context).wallet_list_loading_wallet(wallet.name));
await widget.walletListViewModel.loadWallet(wallet);
auth.hideProgressText();
auth.close();
setState(() {});
} catch (e) {
auth.changeProcessText(
S.of(context).wallet_list_failed_to_load(wallet.name, e.toString()));
}
});
} else {
try {
changeProcessText(S.of(context).wallet_list_loading_wallet(wallet.name));
await widget.walletListViewModel.loadWallet(wallet);
hideProgressText();
setState(() {});
} catch (e) {
changeProcessText(S.of(context).wallet_list_failed_to_load(wallet.name, e.toString()));
}
}
}
void _navigateToCreateWallet() {
if (isSingleCoin) {
Navigator.of(context)
.pushNamed(Routes.newWallet, arguments: widget.walletListViewModel.currentWalletType);
} else {
Navigator.of(context).pushNamed(Routes.newWalletType);
}
}
void _navigateToRestoreWallet() {
if (isSingleCoin) {
Navigator.of(context)
.pushNamed(Routes.restoreWallet, arguments: widget.walletListViewModel.currentWalletType);
} else {
Navigator.of(context).pushNamed(Routes.restoreWalletType);
}
}
void changeProcessText(String text) {
_progressBar = createBar<void>(text, duration: null)..show(context);
}
void hideProgressText() {
_progressBar?.dismiss();
_progressBar = null;
}
}
@@ -0,0 +1,36 @@
import 'package:flutter/material.dart';
class DropDownItemWidget extends StatelessWidget {
const DropDownItemWidget({super.key, required this.title, required this.image});
final double tileHeight = 60;
final Image image;
final String title;
@override
Widget build(BuildContext context) {
return Container(
height: tileHeight,
padding: EdgeInsets.symmetric(horizontal: 20),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
image,
SizedBox(width: 10),
Flexible(
child: Text(
title,
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.w500,
color: Theme.of(context).primaryTextTheme.headline6!.color!,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
)
],
),
);
}
}
@@ -1,74 +0,0 @@
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/screens/dashboard/wallet_menu_item.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/generated/i18n.dart';
// FIXME: terrible design
class WalletMenu {
WalletMenu(this.context, this.reconnect, this.hasRescan) : items = [] {
items.addAll([
WalletMenuItem(
title: S.current.connection_sync,
image: Image.asset('assets/images/nodes_menu.png',
height: 16, width: 16),
handler: () => Navigator.of(context).pushNamed(Routes.connectionSync),
),
WalletMenuItem(
title: S.current.wallets,
image: Image.asset('assets/images/wallet_menu.png',
height: 16, width: 16),
handler: () => Navigator.of(context).pushNamed(Routes.walletList),
),
WalletMenuItem(
title: S.current.address_book_menu,
image: Image.asset('assets/images/open_book_menu.png',
height: 16, width: 16),
handler: () => Navigator.of(context).pushNamed(Routes.addressBook),
),
WalletMenuItem(
title: S.current.security_and_backup,
image:
Image.asset('assets/images/key_menu.png', height: 16, width: 16),
handler: () {
Navigator.of(context).pushNamed(Routes.securityBackupPage);
}),
WalletMenuItem(
title: S.current.privacy_settings,
image:
Image.asset('assets/images/privacy_menu.png', height: 16, width: 16),
handler: () {
Navigator.of(context).pushNamed(Routes.privacyPage);
}),
WalletMenuItem(
title: S.current.display_settings,
image: Image.asset('assets/images/eye_menu.png',
height: 16, width: 16),
handler: () => Navigator.of(context).pushNamed(Routes.displaySettingsPage),
),
WalletMenuItem(
title: S.current.other_settings,
image: Image.asset('assets/images/settings_menu.png',
height: 16, width: 16),
handler: () => Navigator.of(context).pushNamed(Routes.otherSettingsPage),
),
WalletMenuItem(
title: S.current.settings_support,
image: Image.asset('assets/images/question_mark.png',
height: 16, width: 16, color: Palette.darkBlue),
handler: () => Navigator.of(context).pushNamed(Routes.support),
),
]);
}
final List<WalletMenuItem> items;
final BuildContext context;
final Future<void> Function() reconnect;
final bool hasRescan;
void action(int index) {
final item = items[index];
item.handler();
}
}
@@ -1,12 +0,0 @@
import 'package:flutter/material.dart';
class WalletMenuItem {
WalletMenuItem({
required this.title,
required this.image,
required this.handler});
final String title;
final Image image;
final void Function() handler;
}
@@ -24,12 +24,12 @@ import 'package:cake_wallet/di.dart';
class AddressPage extends BasePage {
AddressPage({
required this.addressListViewModel,
required this.walletViewModel,
required this.dashboardViewModel,
required this.receiveOptionViewModel,
}) : _cryptoAmountFocus = FocusNode();
final WalletAddressListViewModel addressListViewModel;
final DashboardViewModel walletViewModel;
final DashboardViewModel dashboardViewModel;
final ReceiveOptionViewModel receiveOptionViewModel;
final FocusNode _cryptoAmountFocus;
@@ -47,28 +47,10 @@ class AddressPage extends BasePage {
bool effectsInstalled = false;
@override
Widget leading(BuildContext context) {
final _backButton = Icon(
Icons.arrow_back_ios,
color: Theme.of(context).accentTextTheme.headline2!.backgroundColor!,
size: 16,
);
Color get titleColor => Colors.white;
return SizedBox(
height: 37,
width: 37,
child: ButtonTheme(
minWidth: double.minPositive,
child: TextButton(
// FIX-ME: Style
//highlightColor: Colors.transparent,
//splashColor: Colors.transparent,
//padding: EdgeInsets.all(0),
onPressed: () => onClose(context),
child: _backButton),
),
);
}
@override
bool get canUseCloseIcon => true;
@override
Widget middle(BuildContext context) =>
@@ -116,8 +98,8 @@ class AddressPage extends BasePage {
_setEffects(context);
autorun((_) async {
if (!walletViewModel.isOutdatedElectrumWallet ||
!walletViewModel.settingsStore.shouldShowReceiveWarning) {
if (!dashboardViewModel.isOutdatedElectrumWallet ||
!dashboardViewModel.settingsStore.shouldShowReceiveWarning) {
return;
}
@@ -133,7 +115,7 @@ class AddressPage extends BasePage {
actionLeftButton: () => Navigator.of(context).pop(),
rightButtonText: S.of(context).do_not_show_me,
actionRightButton: () {
walletViewModel.settingsStore.setShouldShowReceiveWarning(false);
dashboardViewModel.settingsStore.setShouldShowReceiveWarning(false);
Navigator.of(context).pop();
});
});
@@ -163,7 +145,7 @@ class AddressPage extends BasePage {
addressListViewModel: addressListViewModel,
amountTextFieldFocusNode: _cryptoAmountFocus,
isAmountFieldShow: !addressListViewModel.hasAccounts,
isLight: walletViewModel.settingsStore.currentTheme.type == ThemeType.light))
isLight: dashboardViewModel.settingsStore.currentTheme.type == ThemeType.light))
),
Observer(builder: (_) {
return addressListViewModel.hasAddressList
@@ -222,7 +204,6 @@ class AddressPage extends BasePage {
}
reaction((_) => receiveOptionViewModel.selectedReceiveOption, (ReceivePageOption option) {
Navigator.pop(context);
switch (option) {
case ReceivePageOption.anonPayInvoice:
Navigator.pushReplacementNamed(
@@ -1,10 +1,8 @@
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/src/widgets/standard_list.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:cake_wallet/src/widgets/introducing_card.dart';
@@ -16,9 +14,6 @@ class BalancePage extends StatelessWidget{
final DashboardViewModel dashboardViewModel;
final SettingsStore settingsStore;
Color get backgroundLightColor =>
settingsStore.currentTheme.type == ThemeType.bright ? Colors.transparent : Colors.white;
@override
Widget build(BuildContext context) {
@@ -29,7 +24,7 @@ class BalancePage extends StatelessWidget{
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 56),
SizedBox(height: ResponsiveLayoutUtil.instance.isMobile(context) ? 56 : 16),
Container(
margin: const EdgeInsets.only(left: 24, bottom: 16),
child: Observer(builder: (_) {
@@ -1,10 +1,9 @@
import 'dart:ui';
import 'package:cake_wallet/src/widgets/setting_action_button.dart';
import 'package:cake_wallet/src/widgets/setting_actions.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/src/screens/dashboard/wallet_menu.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
// FIXME: terrible design.
@@ -19,18 +18,18 @@ class MenuWidget extends StatefulWidget {
}
class MenuWidgetState extends State<MenuWidget> {
MenuWidgetState()
: this.menuWidth = 0,
this.screenWidth = 0,
this.screenHeight = 0,
this.headerHeight = 120,
this.tileHeight = 60,
this.fromTopEdge = 50,
this.fromBottomEdge = 25,
this.moneroIcon = Image.asset('assets/images/monero_menu.png'),
this.bitcoinIcon = Image.asset('assets/images/bitcoin_menu.png'),
this.litecoinIcon = Image.asset('assets/images/litecoin_menu.png'),
this.havenIcon = Image.asset('assets/images/haven_menu.png');
MenuWidgetState()
: this.menuWidth = 0,
this.screenWidth = 0,
this.screenHeight = 0,
this.headerHeight = 120,
this.tileHeight = 60,
this.fromTopEdge = 50,
this.fromBottomEdge = 25,
this.moneroIcon = Image.asset('assets/images/monero_menu.png'),
this.bitcoinIcon = Image.asset('assets/images/bitcoin_menu.png'),
this.litecoinIcon = Image.asset('assets/images/litecoin_menu.png'),
this.havenIcon = Image.asset('assets/images/haven_menu.png');
final largeScreen = 731;
@@ -82,16 +81,12 @@ class MenuWidgetState extends State<MenuWidget> {
@override
Widget build(BuildContext context) {
final walletMenu = WalletMenu(
context,
() async => widget.dashboardViewModel.reconnect(),
widget.dashboardViewModel.hasRescan);
final itemCount = walletMenu.items.length;
final itemCount = SettingActions.all.length;
moneroIcon = Image.asset('assets/images/monero_menu.png',
color: Theme.of(context).accentTextTheme!.overline!.decorationColor!);
color: Theme.of(context).accentTextTheme.overline!.decorationColor!);
bitcoinIcon = Image.asset('assets/images/bitcoin_menu.png',
color: Theme.of(context).accentTextTheme!.overline!.decorationColor!);
color: Theme.of(context).accentTextTheme.overline!.decorationColor!);
litecoinIcon = Image.asset('assets/images/litecoin_menu.png');
havenIcon = Image.asset('assets/images/haven_menu.png');
@@ -105,17 +100,15 @@ class MenuWidgetState extends State<MenuWidget> {
height: 60,
width: 4,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(2)),
color: PaletteDark.gray),
borderRadius: BorderRadius.all(Radius.circular(2)), color: PaletteDark.gray),
)),
SizedBox(width: 12),
Expanded(
child: ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(24),
bottomLeft: Radius.circular(24)),
topLeft: Radius.circular(24), bottomLeft: Radius.circular(24)),
child: Container(
color: Theme.of(context).textTheme!.bodyText1!.decorationColor!,
color: Theme.of(context).textTheme.bodyText1!.decorationColor!,
child: ListView.separated(
padding: EdgeInsets.only(top: 0),
itemBuilder: (_, index) {
@@ -123,25 +116,13 @@ class MenuWidgetState extends State<MenuWidget> {
return Container(
height: headerHeight,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Theme.of(context)
.accentTextTheme!
.headline4!
.color!,
Theme.of(context)
.accentTextTheme!
.headline4!
.decorationColor!,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight),
gradient: LinearGradient(colors: [
Theme.of(context).accentTextTheme.headline4!.color!,
Theme.of(context).accentTextTheme.headline4!.decorationColor!,
], begin: Alignment.topLeft, end: Alignment.bottomRight),
),
padding: EdgeInsets.only(
left: 24,
top: fromTopEdge,
right: 24,
bottom: fromBottomEdge),
left: 24, top: fromTopEdge, right: 24, bottom: fromBottomEdge),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
@@ -150,13 +131,10 @@ class MenuWidgetState extends State<MenuWidget> {
SingleChildScrollView(
child: Container(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisAlignment:
widget.dashboardViewModel.subname !=
null
? MainAxisAlignment.spaceBetween
: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: widget.dashboardViewModel.subname.isNotEmpty
? MainAxisAlignment.spaceBetween
: MainAxisAlignment.center,
children: <Widget>[
Text(
widget.dashboardViewModel.name,
@@ -165,19 +143,16 @@ class MenuWidgetState extends State<MenuWidget> {
fontSize: 16,
fontWeight: FontWeight.bold),
),
if (widget.dashboardViewModel.subname !=
null)
if (widget.dashboardViewModel.subname.isNotEmpty)
Observer(
builder: (_) => Text(
widget.dashboardViewModel
.subname,
widget.dashboardViewModel.subname,
style: TextStyle(
color: Theme.of(context)
.accentTextTheme!
.accentTextTheme
.overline!
.decorationColor!,
fontWeight:
FontWeight.w500,
fontWeight: FontWeight.w500,
fontSize: 12),
))
],
@@ -190,58 +165,24 @@ class MenuWidgetState extends State<MenuWidget> {
index--;
final item = walletMenu.items[index];
final title = item.title;
final image = item.image ?? Offstage();
final item = SettingActions.all[index];
final isLastTile = index == itemCount - 1;
return GestureDetector(
onTap: () {
Navigator.of(context).pop();
walletMenu.action(index);
},
child: Container(
color: Theme.of(context)
.textTheme!
.bodyText1!
.decorationColor!,
height: isLastTile ? headerHeight : tileHeight,
padding: isLastTile
? EdgeInsets.only(
left: 24,
right: 24,
top: fromBottomEdge,
//bottom: fromTopEdge
)
: EdgeInsets.only(left: 24, right: 24),
alignment: isLastTile ? Alignment.topLeft : null,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
image,
SizedBox(width: 16),
Expanded(
child: Text(
title,
style: TextStyle(
color: Theme.of(context)
.textTheme!
.headline3!
.color!,
fontSize: 16,
fontWeight: FontWeight.bold),
))
],
),
));
return SettingActionButton(
isLastTile: isLastTile,
tileHeight: tileHeight,
selectionActive: false,
fromBottomEdge: fromBottomEdge,
fromTopEdge: fromTopEdge,
onTap: () => item.onTap.call(context),
image: item.image,
title: item.name,
);
},
separatorBuilder: (_, index) => Container(
height: 1,
color: Theme.of(context)
.primaryTextTheme!
.caption!
.decorationColor!,
color: Theme.of(context).primaryTextTheme.caption!.decorationColor!,
),
itemCount: itemCount + 1),
)))
@@ -86,7 +86,11 @@ class PresentReceiveOptionPicker extends StatelessWidget {
itemBuilder: (_, index) {
final option = receiveOptionViewModel.options[index];
return InkWell(
onTap: () => receiveOptionViewModel.selectReceiveOption(option),
onTap: () {
Navigator.pop(popUpContext);
receiveOptionViewModel.selectReceiveOption(option);
},
child: Padding(
padding: const EdgeInsets.only(left: 24, right: 24),
child: Observer(builder: (_) {
@@ -25,6 +25,7 @@ class TransactionsPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
color: Theme.of(context).backgroundColor,
padding: EdgeInsets.only(top: 24, bottom: 24),
child: Column(
children: <Widget>[
+153 -264
View File
@@ -1,13 +1,14 @@
import 'dart:ui';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/desktop_exchange_cards_section.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/mobile_exchange_cards_section.dart';
import 'package:cake_wallet/src/widgets/add_template_button.dart';
import 'package:cake_wallet/utils/debounce.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cw_core/sync_status.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
import 'package:cake_wallet/src/screens/send/widgets/extract_address_from_parsed.dart';
import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
import 'package:dotted_border/dotted_border.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:keyboard_actions/keyboard_actions.dart';
@@ -35,7 +36,14 @@ import 'package:cake_wallet/src/screens/exchange/widgets/present_provider_picker
import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.dart';
class ExchangePage extends BasePage {
ExchangePage(this.exchangeViewModel);
ExchangePage(this.exchangeViewModel) {
depositWalletName = exchangeViewModel.depositCurrency == CryptoCurrency.xmr
? exchangeViewModel.wallet.name
: null;
receiveWalletName = exchangeViewModel.receiveCurrency == CryptoCurrency.xmr
? exchangeViewModel.wallet.name
: null;
}
final ExchangeViewModel exchangeViewModel;
final depositKey = GlobalKey<ExchangeCardState>();
@@ -49,6 +57,20 @@ class ExchangePage extends BasePage {
final _depositAmountDebounce = Debounce(Duration(milliseconds: 500));
var _isReactionsSet = false;
final arrowBottomPurple = Image.asset(
'assets/images/arrow_bottom_purple_icon.png',
color: Colors.white,
height: 8,
);
final arrowBottomCakeGreen = Image.asset(
'assets/images/arrow_bottom_cake_green.png',
color: Colors.white,
height: 8,
);
late final String? depositWalletName;
late final String? receiveWalletName;
@override
String get title => S.current.exchange;
@@ -85,28 +107,11 @@ class ExchangePage extends BasePage {
exchangeViewModel.reset();
});
@override
bool get canUseCloseIcon => true;
@override
Widget body(BuildContext context) {
final arrowBottomPurple = Image.asset(
'assets/images/arrow_bottom_purple_icon.png',
color: Colors.white,
height: 8,
);
final arrowBottomCakeGreen = Image.asset(
'assets/images/arrow_bottom_cake_green.png',
color: Colors.white,
height: 8,
);
final depositWalletName =
exchangeViewModel.depositCurrency == CryptoCurrency.xmr
? exchangeViewModel.wallet.name
: null;
final receiveWalletName =
exchangeViewModel.receiveCurrency == CryptoCurrency.xmr
? exchangeViewModel.wallet.name
: null;
WidgetsBinding.instance
.addPostFrameCallback((_) => _setReactions(context, exchangeViewModel));
@@ -137,201 +142,7 @@ class ExchangePage extends BasePage {
contentPadding: EdgeInsets.only(bottom: 24),
content: Observer(builder: (_) => Column(
children: <Widget>[
Container(
padding: EdgeInsets.only(bottom: 32),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24),
bottomRight: Radius.circular(24)),
gradient: LinearGradient(
colors: [
Theme.of(context).primaryTextTheme!.bodyText2!.color!,
Theme.of(context)
.primaryTextTheme!
.bodyText2!
.decorationColor!,
],
stops: [
0.35,
1.0
],
begin: Alignment.topLeft,
end: Alignment.bottomRight),
),
child: Column(
children: <Widget>[
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24),
bottomRight: Radius.circular(24)),
gradient: LinearGradient(
colors: [
Theme.of(context)
.primaryTextTheme!
.subtitle2!
.color!,
Theme.of(context)
.primaryTextTheme!
.subtitle2!
.decorationColor!,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight),
),
padding: EdgeInsets.fromLTRB(24, 100, 24, 32),
child: Observer(
builder: (_) => ExchangeCard(
onDispose: disposeBestRateSync,
hasAllAmount: exchangeViewModel.hasAllAmount,
allAmount: exchangeViewModel.hasAllAmount
? () => exchangeViewModel
.calculateDepositAllAmount()
: null,
amountFocusNode: _depositAmountFocus,
addressFocusNode: _depositAddressFocus,
key: depositKey,
title: S.of(context).you_will_send,
initialCurrency:
exchangeViewModel.depositCurrency,
initialWalletName: depositWalletName ?? '',
initialAddress:
exchangeViewModel.depositCurrency ==
exchangeViewModel.wallet.currency
? exchangeViewModel.wallet.walletAddresses.address
: exchangeViewModel.depositAddress,
initialIsAmountEditable: true,
initialIsAddressEditable:
exchangeViewModel.isDepositAddressEnabled,
isAmountEstimated: false,
hasRefundAddress: true,
isMoneroWallet: exchangeViewModel.isMoneroWallet,
currencies: exchangeViewModel.depositCurrencies,
onCurrencySelected: (currency) {
// FIXME: need to move it into view model
if (currency == CryptoCurrency.xmr &&
exchangeViewModel.wallet.type !=
WalletType.monero) {
showPopUp<void>(
context: context,
builder: (dialogContext) {
return AlertWithOneAction(
alertTitle: S.of(context).error,
alertContent: S
.of(context)
.exchange_incorrect_current_wallet_for_xmr,
buttonText: S.of(context).ok,
buttonAction: () =>
Navigator.of(dialogContext)
.pop());
});
return;
}
exchangeViewModel.changeDepositCurrency(
currency: currency);
},
imageArrow: arrowBottomPurple,
currencyButtonColor: Colors.transparent,
addressButtonsColor:
Theme.of(context).focusColor!,
borderColor: Theme.of(context)
.primaryTextTheme!
.bodyText1!
.color!,
currencyValueValidator: AmountValidator(
currency: exchangeViewModel.depositCurrency),
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);
},
onPushAddressBookButton: (context) async {
final domain =
exchangeViewModel.depositAddress;
final ticker = exchangeViewModel
.depositCurrency.title.toLowerCase();
exchangeViewModel.depositAddress =
await fetchParsedAddress(
context, domain, ticker);
},
),
),
),
Padding(
padding:
EdgeInsets.only(top: 29, left: 24, right: 24),
child: Observer(
builder: (_) => ExchangeCard(
onDispose: disposeBestRateSync,
amountFocusNode: _receiveAmountFocus,
addressFocusNode: _receiveAddressFocus,
key: receiveKey,
title: S.of(context).you_will_get,
initialCurrency:
exchangeViewModel.receiveCurrency,
initialWalletName: receiveWalletName ?? '',
initialAddress: exchangeViewModel
.receiveCurrency ==
exchangeViewModel.wallet.currency
? exchangeViewModel.wallet.walletAddresses.address
: exchangeViewModel.receiveAddress,
initialIsAmountEditable: exchangeViewModel
.isReceiveAmountEditable,
initialIsAddressEditable:
exchangeViewModel
.isReceiveAddressEnabled,
isAmountEstimated: true,
isMoneroWallet: exchangeViewModel.isMoneroWallet,
currencies:
exchangeViewModel.receiveCurrencies,
onCurrencySelected: (currency) =>
exchangeViewModel
.changeReceiveCurrency(
currency: currency),
imageArrow: arrowBottomCakeGreen,
currencyButtonColor: Colors.transparent,
addressButtonsColor:
Theme.of(context).focusColor!,
borderColor: Theme.of(context)
.primaryTextTheme!
.bodyText1!
.decorationColor!,
currencyValueValidator: AmountValidator(
currency: exchangeViewModel.receiveCurrency),
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);
},
onPushAddressBookButton: (context) async {
final domain =
exchangeViewModel.receiveAddress;
final ticker = exchangeViewModel
.receiveCurrency.title.toLowerCase();
exchangeViewModel.receiveAddress =
await fetchParsedAddress(
context, domain, ticker);
},
)),
)
],
),
),
_exchangeCardsSection(context),
Padding(
padding: EdgeInsets.only(top: 12, left: 24),
child: Row(
@@ -427,50 +238,9 @@ class ExchangePage extends BasePage {
return Row(
children: <Widget>[
GestureDetector(
onTap: () =>
Navigator.of(context).pushNamed(Routes.exchangeTemplate),
child: Container(
padding: EdgeInsets.only(left: 1, right: 10),
child: DottedBorder(
borderType: BorderType.RRect,
dashPattern: [6, 4],
color: Theme.of(context)
.primaryTextTheme!
.headline3!
.decorationColor!,
strokeWidth: 2,
radius: Radius.circular(20),
child: Container(
height: 34,
padding: EdgeInsets.only(left: 10, right: 10),
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20)),
color: Colors.transparent,
),
child: templates.length >= 1
? Icon(
Icons.add,
color: Theme.of(context)
.primaryTextTheme!
.headline2!
.color!,
)
: Text(
S.of(context).new_template,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Theme.of(context)
.primaryTextTheme!
.headline2!
.color!,
),
),
),
),
),
AddTemplateButton(
onTap: () => Navigator.of(context).pushNamed(Routes.exchangeTemplate),
currentTemplatesLength: templates.length,
),
ListView.builder(
scrollDirection: Axis.horizontal,
@@ -808,5 +578,124 @@ class ExchangePage extends BasePage {
}
}
void disposeBestRateSync() => exchangeViewModel.bestRateSync?.cancel();
void disposeBestRateSync() => exchangeViewModel.bestRateSync.cancel();
Widget _exchangeCardsSection(BuildContext context) {
final firstExchangeCard = Observer(builder: (_) => ExchangeCard(
onDispose: disposeBestRateSync,
hasAllAmount: exchangeViewModel.hasAllAmount,
allAmount: exchangeViewModel.hasAllAmount
? () => exchangeViewModel.calculateDepositAllAmount()
: null,
amountFocusNode: _depositAmountFocus,
addressFocusNode: _depositAddressFocus,
key: depositKey,
title: S.of(context).you_will_send,
initialCurrency: exchangeViewModel.depositCurrency,
initialWalletName: depositWalletName ?? '',
initialAddress:
exchangeViewModel.depositCurrency == exchangeViewModel.wallet.currency
? exchangeViewModel.wallet.walletAddresses.address
: exchangeViewModel.depositAddress,
initialIsAmountEditable: true,
initialIsAddressEditable: exchangeViewModel.isDepositAddressEnabled,
isAmountEstimated: false,
hasRefundAddress: true,
isMoneroWallet: exchangeViewModel.isMoneroWallet,
currencies: exchangeViewModel.depositCurrencies,
onCurrencySelected: (currency) {
// FIXME: need to move it into view model
if (currency == CryptoCurrency.xmr &&
exchangeViewModel.wallet.type != WalletType.monero) {
showPopUp<void>(
context: context,
builder: (dialogContext) {
return AlertWithOneAction(
alertTitle: S.of(context).error,
alertContent:
S.of(context).exchange_incorrect_current_wallet_for_xmr,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(dialogContext).pop());
});
return;
}
exchangeViewModel.changeDepositCurrency(currency: currency);
},
imageArrow: arrowBottomPurple,
currencyButtonColor: Colors.transparent,
addressButtonsColor: Theme.of(context).focusColor!,
borderColor: Theme.of(context).primaryTextTheme!.bodyText1!.color!,
currencyValueValidator:
AmountValidator(currency: exchangeViewModel.depositCurrency),
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);
},
onPushAddressBookButton: (context) async {
final domain = exchangeViewModel.depositAddress;
final ticker = exchangeViewModel.depositCurrency.title.toLowerCase();
exchangeViewModel.depositAddress =
await fetchParsedAddress(context, domain, ticker);
},
));
final secondExchangeCard = Observer(builder: (_) => ExchangeCard(
onDispose: disposeBestRateSync,
amountFocusNode: _receiveAmountFocus,
addressFocusNode: _receiveAddressFocus,
key: receiveKey,
title: S.of(context).you_will_get,
initialCurrency: exchangeViewModel.receiveCurrency,
initialWalletName: receiveWalletName ?? '',
initialAddress:
exchangeViewModel.receiveCurrency == exchangeViewModel.wallet.currency
? exchangeViewModel.wallet.walletAddresses.address
: exchangeViewModel.receiveAddress,
initialIsAmountEditable: exchangeViewModel.isReceiveAmountEditable,
initialIsAddressEditable: exchangeViewModel.isReceiveAddressEnabled,
isAmountEstimated: true,
isMoneroWallet: exchangeViewModel.isMoneroWallet,
currencies: exchangeViewModel.receiveCurrencies,
onCurrencySelected: (currency) =>
exchangeViewModel.changeReceiveCurrency(currency: currency),
imageArrow: arrowBottomCakeGreen,
currencyButtonColor: Colors.transparent,
addressButtonsColor: Theme.of(context).focusColor!,
borderColor:
Theme.of(context).primaryTextTheme!.bodyText1!.decorationColor!,
currencyValueValidator:
AmountValidator(currency: exchangeViewModel.receiveCurrency),
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);
},
onPushAddressBookButton: (context) async {
final domain = exchangeViewModel.receiveAddress;
final ticker = exchangeViewModel.receiveCurrency.title.toLowerCase();
exchangeViewModel.receiveAddress =
await fetchParsedAddress(context, domain, ticker);
},
));
if (ResponsiveLayoutUtil.instance.isMobile(context)) {
return MobileExchangeCardsSection(
firstExchangeCard: firstExchangeCard,
secondExchangeCard: secondExchangeCard,
);
}
return DesktopExchangeCardsSection(
firstExchangeCard: firstExchangeCard,
secondExchangeCard: secondExchangeCard,
);
}
}
@@ -1,13 +1,9 @@
import 'dart:ui';
import 'package:cake_wallet/exchange/exchange_provider.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:keyboard_actions/keyboard_actions.dart';
import 'package:keyboard_actions/keyboard_actions_config.dart';
import 'package:keyboard_actions/keyboard_actions_item.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cw_core/crypto_currency.dart';
@@ -78,7 +74,7 @@ class ExchangeTemplatePage extends BasePage {
config: KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
keyboardBarColor:
Theme.of(context).accentTextTheme!.bodyText1!.backgroundColor!,
Theme.of(context).accentTextTheme.bodyText1!.backgroundColor!,
nextFocus: false,
actions: [
KeyboardActionsItem(
@@ -103,115 +99,114 @@ class ExchangeTemplatePage extends BasePage {
),
gradient: LinearGradient(
colors: [
Theme.of(context).primaryTextTheme!.bodyText2!.color!,
Theme.of(context).primaryTextTheme!.bodyText2!.decorationColor!,
Theme.of(context).primaryTextTheme.bodyText2!.color!,
Theme.of(context).primaryTextTheme.bodyText2!.decorationColor!,
],
stops: [0.35, 1.0],
begin: Alignment.topLeft,
end: Alignment.bottomRight),
),
child: Column(
children: <Widget>[
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24),
bottomRight: Radius.circular(24)
child: FocusTraversalGroup(
policy: OrderedTraversalPolicy(),
child: Column(
children: <Widget>[
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24),
bottomRight: Radius.circular(24)
),
gradient: LinearGradient(
colors: [
Theme.of(context)
.primaryTextTheme.subtitle2!
.color!,
Theme.of(context)
.primaryTextTheme.subtitle2!
.decorationColor!,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight),
),
gradient: LinearGradient(
colors: [
Theme.of(context)
.primaryTextTheme!
.subtitle2!
.color!,
Theme.of(context)
.primaryTextTheme!
.subtitle2!
.decorationColor!,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight),
),
padding: EdgeInsets.fromLTRB(24, 100, 24, 32),
child: Observer(
builder: (_) => ExchangeCard(
amountFocusNode: _depositAmountFocus,
key: depositKey,
title: S.of(context).you_will_send,
initialCurrency:
exchangeViewModel.depositCurrency,
initialWalletName: depositWalletName ?? '',
initialAddress: exchangeViewModel
.depositCurrency ==
exchangeViewModel.wallet.currency
? exchangeViewModel.wallet.walletAddresses.address
: exchangeViewModel.depositAddress,
initialIsAmountEditable: true,
initialIsAddressEditable: exchangeViewModel
.isDepositAddressEnabled,
isAmountEstimated: false,
hasRefundAddress: true,
isMoneroWallet: exchangeViewModel.isMoneroWallet,
currencies: CryptoCurrency.all,
onCurrencySelected: (currency) =>
exchangeViewModel.changeDepositCurrency(
currency: currency),
imageArrow: arrowBottomPurple,
currencyButtonColor: Colors.transparent,
addressButtonsColor:
Theme.of(context).focusColor!,
borderColor: Theme.of(context)
.primaryTextTheme!
.bodyText1!
.color!,
currencyValueValidator: AmountValidator(
currency: exchangeViewModel.depositCurrency),
//addressTextFieldValidator: AddressValidator(
// type: exchangeViewModel.depositCurrency),
),
),
),
Padding(
padding: EdgeInsets.only(top: 29, left: 24, right: 24),
child: Observer(
padding: EdgeInsets.fromLTRB(24, 100, 24, 32),
child: Observer(
builder: (_) => ExchangeCard(
amountFocusNode: _receiveAmountFocus,
key: receiveKey,
title: S.of(context).you_will_get,
amountFocusNode: _depositAmountFocus,
key: depositKey,
title: S.of(context).you_will_send,
initialCurrency:
exchangeViewModel.receiveCurrency,
initialWalletName: receiveWalletName ?? '',
initialAddress:
exchangeViewModel.receiveCurrency ==
exchangeViewModel.depositCurrency,
initialWalletName: depositWalletName ?? '',
initialAddress: exchangeViewModel
.depositCurrency ==
exchangeViewModel.wallet.currency
? exchangeViewModel.wallet.walletAddresses.address
: exchangeViewModel.receiveAddress,
initialIsAmountEditable:
exchangeViewModel.provider is
XMRTOExchangeProvider ? true : false,
initialIsAddressEditable:
exchangeViewModel.isReceiveAddressEnabled,
isAmountEstimated: true,
: exchangeViewModel.depositAddress,
initialIsAmountEditable: true,
initialIsAddressEditable: exchangeViewModel
.isDepositAddressEnabled,
isAmountEstimated: false,
hasRefundAddress: true,
isMoneroWallet: exchangeViewModel.isMoneroWallet,
currencies: exchangeViewModel.receiveCurrencies,
currencies: CryptoCurrency.all,
onCurrencySelected: (currency) =>
exchangeViewModel.changeReceiveCurrency(
exchangeViewModel.changeDepositCurrency(
currency: currency),
imageArrow: arrowBottomCakeGreen,
imageArrow: arrowBottomPurple,
currencyButtonColor: Colors.transparent,
addressButtonsColor:
Theme.of(context).focusColor!,
Theme.of(context).focusColor,
borderColor: Theme.of(context)
.primaryTextTheme!
.bodyText1!
.decorationColor!,
.primaryTextTheme.bodyText1!
.color!,
currencyValueValidator: AmountValidator(
currency: exchangeViewModel.receiveCurrency),
currency: exchangeViewModel.depositCurrency),
//addressTextFieldValidator: AddressValidator(
// type: exchangeViewModel.receiveCurrency),
)),
)
],
// type: exchangeViewModel.depositCurrency),
),
),
),
Padding(
padding: EdgeInsets.only(top: 29, left: 24, right: 24),
child: Observer(
builder: (_) => ExchangeCard(
amountFocusNode: _receiveAmountFocus,
key: receiveKey,
title: S.of(context).you_will_get,
initialCurrency:
exchangeViewModel.receiveCurrency,
initialWalletName: receiveWalletName ?? '',
initialAddress:
exchangeViewModel.receiveCurrency ==
exchangeViewModel.wallet.currency
? exchangeViewModel.wallet.walletAddresses.address
: exchangeViewModel.receiveAddress,
initialIsAmountEditable:
exchangeViewModel.provider is
XMRTOExchangeProvider ? true : false,
initialIsAddressEditable:
exchangeViewModel.isReceiveAddressEnabled,
isAmountEstimated: true,
isMoneroWallet: exchangeViewModel.isMoneroWallet,
currencies: exchangeViewModel.receiveCurrencies,
onCurrencySelected: (currency) =>
exchangeViewModel.changeReceiveCurrency(
currency: currency),
imageArrow: arrowBottomCakeGreen,
currencyButtonColor: Colors.transparent,
addressButtonsColor:
Theme.of(context).focusColor,
borderColor: Theme.of(context)
.primaryTextTheme.bodyText1!
.decorationColor!,
currencyValueValidator: AmountValidator(
currency: exchangeViewModel.receiveCurrency),
//addressTextFieldValidator: AddressValidator(
// type: exchangeViewModel.receiveCurrency),
)),
)
],
),
),
),
bottomSectionPadding:
@@ -230,8 +225,7 @@ class ExchangeTemplatePage extends BasePage {
textAlign: TextAlign.center,
style: TextStyle(
color: Theme.of(context)
.primaryTextTheme!
.headline1!
.primaryTextTheme.headline1!
.decorationColor!,
fontWeight: FontWeight.w500,
fontSize: 12),
@@ -1,9 +1,8 @@
import 'dart:ui';
import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker_item_widget.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/picker_item.dart';
import 'package:cake_wallet/src/widgets/alert_close_button.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cw_core/currency.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cake_wallet/src/widgets/alert_background.dart';
@@ -68,11 +67,9 @@ class CurrencyPickerState extends State<CurrencyPicker> {
@override
Widget build(BuildContext context) {
return AlertBackground(
child: Stack(
alignment: Alignment.center,
children: [
Column(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
if (widget.title?.isNotEmpty ?? false)
Container(
@@ -94,10 +91,11 @@ class CurrencyPickerState extends State<CurrencyPicker> {
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(30)),
child: Container(
color: Theme.of(context).accentTextTheme!.headline6!.color!,
color: Theme.of(context).accentTextTheme.headline6!.color!,
child: ConstrainedBox(
constraints: BoxConstraints(
maxHeight: MediaQuery.of(context).size.height * 0.65,
maxWidth: ResponsiveLayoutUtil.kPopupWidth
),
child: Column(
mainAxisSize: MainAxisSize.min,
@@ -133,7 +131,7 @@ class CurrencyPickerState extends State<CurrencyPicker> {
),
),
Divider(
color: Theme.of(context).accentTextTheme!.headline6!.backgroundColor!,
color: Theme.of(context).accentTextTheme.headline6!.backgroundColor!,
height: 1,
),
if (widget.selectedAtIndex != -1)
@@ -171,8 +169,7 @@ class CurrencyPickerState extends State<CurrencyPicker> {
),
),
),
],
),
SizedBox(height: ResponsiveLayoutUtil.kPopupSpaceHeight),
AlertCloseButton(),
],
),
@@ -0,0 +1,31 @@
import 'package:flutter/material.dart';
class DesktopExchangeCardsSection extends StatelessWidget {
final Widget firstExchangeCard;
final Widget secondExchangeCard;
const DesktopExchangeCardsSection({
Key? key,
required this.firstExchangeCard,
required this.secondExchangeCard,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return FocusTraversalGroup(
policy: OrderedTraversalPolicy(),
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 55, left: 24, right: 24),
child: firstExchangeCard,
),
Padding(
padding: EdgeInsets.only(top: 29, left: 24, right: 24),
child: secondExchangeCard,
),
],
),
);
}
}
@@ -160,7 +160,7 @@ class ExchangeCardState extends State<ExchangeCard> {
final copyImage = Image.asset('assets/images/copy_content.png',
height: 16,
width: 16,
color: Theme.of(context).primaryTextTheme!.headline3!.color!);
color: Theme.of(context).primaryTextTheme.headline3!.color!);
return Container(
width: double.infinity,
@@ -175,7 +175,7 @@ class ExchangeCardState extends State<ExchangeCard> {
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Theme.of(context).textTheme!.headline5!.color!),
color: Theme.of(context).textTheme.headline5!.color!),
)
],
),
@@ -210,7 +210,7 @@ class ExchangeCardState extends State<ExchangeCard> {
child: Container(
height: 32,
decoration: BoxDecoration(
color: widget.addressButtonsColor ?? Theme.of(context).primaryTextTheme!.headline4!.color!,
color: widget.addressButtonsColor ?? Theme.of(context).primaryTextTheme.headline4!.color!,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Center(
@@ -221,8 +221,7 @@ class ExchangeCardState extends State<ExchangeCard> {
fontSize: 12,
fontWeight: FontWeight.bold,
color: Theme.of(context)
.primaryTextTheme!
.headline4!
.primaryTextTheme.headline4!
.decorationColor!)),
),
),
@@ -241,34 +240,36 @@ class ExchangeCardState extends State<ExchangeCard> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: BaseTextFormField(
focusNode: widget.amountFocusNode,
controller: amountController,
enabled: _isAmountEditable,
textAlign: TextAlign.left,
keyboardType: TextInputType.numberWithOptions(
signed: false, decimal: true),
inputFormatters: [
FilteringTextInputFormatter.deny(
RegExp('[\\-|\\ ]'))
],
hintText: '0.0000',
borderColor: Colors.transparent,
//widget.borderColor,
textStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.white),
placeholderTextStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Theme.of(context)
.accentTextTheme!
.headline1!
.decorationColor!),
validator: _isAmountEditable
? widget.currencyValueValidator
: null),
child: FocusTraversalOrder(
order: NumericFocusOrder(1),
child: BaseTextFormField(
focusNode: widget.amountFocusNode,
controller: amountController,
enabled: _isAmountEditable,
textAlign: TextAlign.left,
keyboardType: TextInputType.numberWithOptions(
signed: false, decimal: true),
inputFormatters: [
FilteringTextInputFormatter.deny(
RegExp('[\\-|\\ ]'))
],
hintText: '0.0000',
borderColor: Colors.transparent,
//widget.borderColor,
textStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.white),
placeholderTextStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Theme.of(context)
.accentTextTheme.headline1!
.decorationColor!),
validator: _isAmountEditable
? widget.currencyValueValidator
: null),
),
),
if (widget.hasAllAmount)
Container(
@@ -276,8 +277,7 @@ class ExchangeCardState extends State<ExchangeCard> {
width: 32,
decoration: BoxDecoration(
color: Theme.of(context)
.primaryTextTheme!
.headline4!
.primaryTextTheme.headline4!
.color!,
borderRadius:
BorderRadius.all(Radius.circular(6))),
@@ -290,8 +290,7 @@ class ExchangeCardState extends State<ExchangeCard> {
fontSize: 12,
fontWeight: FontWeight.bold,
color: Theme.of(context)
.primaryTextTheme!
.headline4!
.primaryTextTheme.headline4!
.decorationColor!)),
),
),
@@ -302,8 +301,7 @@ class ExchangeCardState extends State<ExchangeCard> {
],
)),
Divider(height: 1,color: Theme.of(context)
.primaryTextTheme!
.headline5!
.primaryTextTheme.headline5!
.decorationColor!),
Padding(
padding: EdgeInsets.only(top: 5),
@@ -321,8 +319,7 @@ class ExchangeCardState extends State<ExchangeCard> {
fontSize: 10,
height: 1.2,
color: Theme.of(context)
.accentTextTheme!
.headline1!
.accentTextTheme.headline1!
.decorationColor!),
)
: Offstage(),
@@ -336,8 +333,7 @@ class ExchangeCardState extends State<ExchangeCard> {
fontSize: 10,
height: 1.2,
color: Theme.of(context)
.accentTextTheme!
.headline1!
.accentTextTheme.headline1!
.decorationColor!))
: Offstage(),
])),
@@ -351,71 +347,75 @@ class ExchangeCardState extends State<ExchangeCard> {
fontSize: 14,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.accentTextTheme!
.headline1!
.accentTextTheme.headline1!
.decorationColor!),
))
: Offstage(),
_isAddressEditable
? Padding(
padding: EdgeInsets.only(top: 20),
child: AddressTextField(
focusNode: widget.addressFocusNode,
controller: addressController,
onURIScanned: (uri) {
final paymentRequest = PaymentRequest.fromUri(uri);
addressController.text = paymentRequest.address;
if (amountController.text.isNotEmpty) {
_showAmountPopup(context, paymentRequest);
return;
}
widget.amountFocusNode?.requestFocus();
amountController.text = paymentRequest.amount;
},
placeholder: widget.hasRefundAddress
? S.of(context).refund_address
: null,
options: [
AddressTextFieldOption.paste,
AddressTextFieldOption.qrCode,
AddressTextFieldOption.addressBook,
],
isBorderExist: false,
textStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.white),
hintStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Theme.of(context)
.accentTextTheme!
.headline1!
.decorationColor!),
buttonColor: widget.addressButtonsColor,
validator: widget.addressTextFieldValidator,
onPushPasteButton: widget.onPushPasteButton,
onPushAddressBookButton: widget.onPushAddressBookButton,
selectedCurrency: _selectedCurrency
? FocusTraversalOrder(
order: NumericFocusOrder(2),
child: Padding(
padding: EdgeInsets.only(top: 20),
child: AddressTextField(
focusNode: widget.addressFocusNode,
controller: addressController,
onURIScanned: (uri) {
final paymentRequest = PaymentRequest.fromUri(uri);
addressController.text = paymentRequest.address;
if (amountController.text.isNotEmpty) {
_showAmountPopup(context, paymentRequest);
return;
}
widget.amountFocusNode?.requestFocus();
amountController.text = paymentRequest.amount;
},
placeholder: widget.hasRefundAddress
? S.of(context).refund_address
: null,
options: [
AddressTextFieldOption.paste,
AddressTextFieldOption.qrCode,
AddressTextFieldOption.addressBook,
],
isBorderExist: false,
textStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.white),
hintStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Theme.of(context)
.accentTextTheme.headline1!
.decorationColor!),
buttonColor: widget.addressButtonsColor,
validator: widget.addressTextFieldValidator,
onPushPasteButton: widget.onPushPasteButton,
onPushAddressBookButton: widget.onPushAddressBookButton,
selectedCurrency: _selectedCurrency
),
),
)
)
: Padding(
padding: EdgeInsets.only(top: 10),
child: Builder(
builder: (context) => Stack(children: <Widget>[
BaseTextFormField(
controller: addressController,
readOnly: true,
borderColor: Colors.transparent,
suffixIcon:
SizedBox(width: _isMoneroWallet ? 80 : 36),
textStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.white),
validator: widget.addressTextFieldValidator),
FocusTraversalOrder(
order: NumericFocusOrder(3),
child: BaseTextFormField(
controller: addressController,
readOnly: true,
borderColor: Colors.transparent,
suffixIcon:
SizedBox(width: _isMoneroWallet ? 80 : 36),
textStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.white),
validator: widget.addressTextFieldValidator),
),
Positioned(
top: 2,
right: 0,
@@ -432,10 +432,8 @@ class ExchangeCardState extends State<ExchangeCard> {
child: InkWell(
onTap: () async {
final contact =
await Navigator.of(context,
rootNavigator: true)
.pushNamed(Routes
.pickerAddressBook);
await Navigator.of(context)
.pushNamed(Routes.pickerAddressBook);
if (contact is ContactBase &&
contact.address != null) {
@@ -458,8 +456,7 @@ class ExchangeCardState extends State<ExchangeCard> {
child: Image.asset(
'assets/images/open_book.png',
color: Theme.of(context)
.primaryTextTheme!
.headline4!
.primaryTextTheme.headline4!
.decorationColor!,
)),
)),
@@ -0,0 +1,58 @@
import 'package:flutter/material.dart';
class MobileExchangeCardsSection extends StatelessWidget {
final Widget firstExchangeCard;
final Widget secondExchangeCard;
const MobileExchangeCardsSection({
Key? key,
required this.firstExchangeCard,
required this.secondExchangeCard,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.only(bottom: 32),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24),
bottomRight: Radius.circular(24),
),
gradient: LinearGradient(
colors: [
Theme.of(context).primaryTextTheme.bodyText2!.color!,
Theme.of(context).primaryTextTheme.bodyText2!.decorationColor!,
],
stops: [0.35, 1.0],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: Column(
children: <Widget>[
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)),
gradient: LinearGradient(
colors: [
Theme.of(context).primaryTextTheme.subtitle2!.color!,
Theme.of(context).primaryTextTheme.subtitle2!.decorationColor!,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
padding: EdgeInsets.fromLTRB(24, 100, 24, 32),
child: firstExchangeCard,
),
Padding(
padding: EdgeInsets.only(top: 29, left: 24, right: 24),
child: secondExchangeCard,
)
],
),
);
}
}
@@ -66,6 +66,7 @@ class IoniaCreateAccountPage extends BasePage {
validator: EmailValidator(),
keyboardType: TextInputType.emailAddress,
controller: _emailController,
onSubmit: (_) => _createAccount(),
),
),
bottomSectionPadding: EdgeInsets.symmetric(vertical: 36, horizontal: 24),
@@ -77,12 +78,7 @@ class IoniaCreateAccountPage extends BasePage {
Observer(
builder: (_) => LoadingPrimaryButton(
text: S.of(context).create_account,
onPressed: () async {
if (_formKey.currentState != null && !_formKey.currentState!.validate()) {
return;
}
await _authViewModel.createUser(_emailController.text);
},
onPressed: _createAccount,
isLoading: _authViewModel.createUserState is IoniaCreateStateLoading,
color: Theme.of(context).accentTextTheme!.bodyText1!.color!,
textColor: Colors.white,
@@ -151,4 +147,11 @@ class IoniaCreateAccountPage extends BasePage {
Routes.ioniaVerifyIoniaOtpPage,
arguments: [authViewModel.email, false],
);
void _createAccount() async {
if (_formKey.currentState != null && !_formKey.currentState!.validate()) {
return;
}
await _authViewModel.createUser(_emailController.text);
}
}
@@ -57,6 +57,7 @@ class IoniaLoginPage extends BasePage {
keyboardType: TextInputType.emailAddress,
validator: EmailValidator(),
controller: _emailController,
onSubmit: (text) => _login(),
),
),
bottomSectionPadding: EdgeInsets.symmetric(vertical: 36, horizontal: 24),
@@ -68,12 +69,7 @@ class IoniaLoginPage extends BasePage {
Observer(
builder: (_) => LoadingPrimaryButton(
text: S.of(context).login,
onPressed: () async {
if (_formKey.currentState != null && !_formKey.currentState!.validate()) {
return;
}
await _authViewModel.signIn(_emailController.text);
},
onPressed: _login,
isLoading: _authViewModel.signInState is IoniaCreateStateLoading,
color: Theme.of(context).accentTextTheme!.bodyText1!.color!,
textColor: Colors.white,
@@ -106,4 +102,11 @@ class IoniaLoginPage extends BasePage {
Routes.ioniaVerifyIoniaOtpPage,
arguments: [authViewModel.email, true],
);
void _login() async {
if (_formKey.currentState != null && !_formKey.currentState!.validate()) {
return;
}
await _authViewModel.signIn(_emailController.text);
}
}
@@ -82,6 +82,7 @@ class IoniaVerifyIoniaOtp extends BasePage {
keyboardType: TextInputType.numberWithOptions(signed: false, decimal: true),
focusNode: _codeFocus,
controller: _codeController,
onSubmit: (_) => _verify(),
),
SizedBox(height: 14),
Text(
@@ -116,7 +117,7 @@ class IoniaVerifyIoniaOtp extends BasePage {
Observer(
builder: (_) => LoadingPrimaryButton(
text: S.of(context).continue_text,
onPressed: () async => await _authViewModel.verifyEmail(_codeController.text),
onPressed: _verify,
isDisabled: _authViewModel.otpState is IoniaOtpSendDisabled,
isLoading: _authViewModel.otpState is IoniaOtpValidating,
color: Theme.of(context).accentTextTheme!.bodyText1!.color!,
@@ -148,4 +149,6 @@ class IoniaVerifyIoniaOtp extends BasePage {
void _onOtpSuccessful(BuildContext context) =>
Navigator.of(context)
.pushNamedAndRemoveUntil(Routes.ioniaManageCardsPage, (route) => route.isFirst);
void _verify() async => await _authViewModel.verifyEmail(_codeController.text);
}
@@ -6,6 +6,7 @@ import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/view_model/ionia/ionia_buy_card_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@@ -44,7 +45,6 @@ class IoniaBuyGiftCardPage extends BasePage {
@override
Widget body(BuildContext context) {
final _width = MediaQuery.of(context).size.width;
final merchant = ioniaBuyCardViewModel.ioniaMerchant;
return KeyboardActions(
disableScroll: true,
@@ -67,7 +67,10 @@ class IoniaBuyGiftCardPage extends BasePage {
Container(
padding: EdgeInsets.symmetric(horizontal: 25),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24),
bottomRight: Radius.circular(24),
),
gradient: LinearGradient(colors: [
Theme.of(context).primaryTextTheme!.subtitle1!.color!,
Theme.of(context).primaryTextTheme!.subtitle1!.decorationColor!,
@@ -75,35 +78,28 @@ class IoniaBuyGiftCardPage extends BasePage {
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(height: 150),
BaseTextFormField(
controller: _amountController,
focusNode: _amountFieldFocus,
keyboardType: TextInputType.numberWithOptions(signed: false, decimal: true),
inputFormatters: [
FilteringTextInputFormatter.deny(RegExp('[\-|\ ]')),
FilteringTextInputFormatter.allow(RegExp(r'^\d+(\.|\,)?\d{0,2}'))],
hintText: '1000',
placeholderTextStyle: TextStyle(
color: Theme.of(context).primaryTextTheme!.headline5!.color!,
fontWeight: FontWeight.w600,
fontSize: 36,
),
borderColor: Theme.of(context).primaryTextTheme!.headline5!.color!,
textColor: Colors.white,
textStyle: TextStyle(
color: Colors.white,
fontSize: 36,
),
prefixIcon: Padding(
padding: EdgeInsets.only(
top: 5.0,
left: _width / 4,
SizedBox(
width: 200,
child: BaseTextFormField(
controller: _amountController,
focusNode: _amountFieldFocus,
keyboardType: TextInputType.numberWithOptions(signed: false, decimal: true),
inputFormatters: [
FilteringTextInputFormatter.deny(RegExp('[\-|\ ]')),
FilteringTextInputFormatter.allow(
RegExp(r'^\d+(\.|\,)?\d{0,2}'),
),
],
hintText: '1000',
placeholderTextStyle: TextStyle(
color: Theme.of(context).primaryTextTheme.headline5!.color!,
fontWeight: FontWeight.w600,
fontSize: 36,
),
child: Text(
prefixIcon: Text(
'USD: ',
style: TextStyle(
color: Colors.white,
@@ -111,8 +107,17 @@ class IoniaBuyGiftCardPage extends BasePage {
fontSize: 36,
),
),
textColor: Colors.white,
textStyle: TextStyle(
color: Colors.white,
fontSize: 36,
),
),
),
Divider(
color: Theme.of(context).primaryTextTheme.headline5!.color!,
height: 1,
),
SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -140,7 +145,11 @@ class IoniaBuyGiftCardPage extends BasePage {
padding: const EdgeInsets.all(24.0),
child: CardItem(
title: merchant.legalName,
backgroundColor: Theme.of(context).accentTextTheme!.headline1!.backgroundColor!.withOpacity(0.1),
backgroundColor: Theme.of(context)
.accentTextTheme!
.headline1!
.backgroundColor!
.withOpacity(0.1),
discount: merchant.discount,
titleColor: Theme.of(context).accentTextTheme!.headline1!.backgroundColor!,
subtitleColor: Theme.of(context).hintColor,
+111 -114
View File
@@ -1,11 +1,12 @@
import 'package:cake_wallet/entities/generate_name.dart';
import 'package:cake_wallet/main.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:mobx/mobx.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/core/wallet_name_validator.dart';
import 'package:cake_wallet/src/widgets/seed_language_selector.dart';
@@ -24,18 +25,14 @@ class NewWalletPage extends BasePage {
final walletNameImage = Image.asset('assets/images/wallet_name.png');
final walletNameLightImage =
Image.asset('assets/images/wallet_name_light.png');
final walletNameLightImage = Image.asset('assets/images/wallet_name_light.png');
@override
String get title => S.current.new_wallet;
@override
Widget body(BuildContext context) => WalletNameForm(
_walletNewVM,
currentTheme.type == ThemeType.dark
? walletNameImage
: walletNameLightImage);
_walletNewVM, currentTheme.type == ThemeType.dark ? walletNameImage : walletNameLightImage);
}
class WalletNameForm extends StatefulWidget {
@@ -50,9 +47,9 @@ class WalletNameForm extends StatefulWidget {
class _WalletNameFormState extends State<WalletNameForm> {
_WalletNameFormState(this._walletNewVM)
: _formKey = GlobalKey<FormState>(),
_languageSelectorKey = GlobalKey<SeedLanguageSelectorState>(),
_controller = TextEditingController();
: _formKey = GlobalKey<FormState>(),
_languageSelectorKey = GlobalKey<SeedLanguageSelectorState>(),
_controller = TextEditingController();
static const aspectRatioImage = 1.22;
@@ -64,10 +61,9 @@ class _WalletNameFormState extends State<WalletNameForm> {
@override
void initState() {
_stateReaction ??=
reaction((_) => _walletNewVM.state, (ExecutionState state) {
_stateReaction ??= reaction((_) => _walletNewVM.state, (ExecutionState state) async {
if (state is ExecutedSuccessfullyState) {
Navigator.of(context)
Navigator.of(navigatorKey.currentContext!)
.pushNamed(Routes.preSeed, arguments: _walletNewVM.type);
}
@@ -90,117 +86,118 @@ class _WalletNameFormState extends State<WalletNameForm> {
@override
Widget build(BuildContext context) {
return Container(
return Padding(
padding: EdgeInsets.only(top: 24),
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
content:
Column(crossAxisAlignment: CrossAxisAlignment.center, children: [
Padding(
padding: EdgeInsets.only(left: 12, right: 12),
child: AspectRatio(
aspectRatio: aspectRatioImage,
child:
FittedBox(child: widget.walletImage, fit: BoxFit.fill)),
),
Padding(
padding: EdgeInsets.only(top: 24),
child: Form(
key: _formKey,
child: Stack(
alignment: Alignment.centerRight,
children: [
TextFormField(
onChanged: (value) => _walletNewVM.name = value,
controller: _controller,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.w600,
color:
Theme.of(context).primaryTextTheme!.headline6!.color!),
decoration: InputDecoration(
hintStyle: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.accentTextTheme!
.headline2!
.color!),
hintText: S.of(context).wallet_name,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context)
.accentTextTheme!
.headline2!
.decorationColor!,
width: 1.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context)
.accentTextTheme!
.headline2!
.decorationColor!,
width: 1.0),
),
suffixIcon: IconButton(
onPressed: () async {
final rName = await generateName();
FocusManager.instance.primaryFocus?.unfocus();
content: Center(
child: ConstrainedBox(
constraints:
BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.only(left: 12, right: 12),
child: AspectRatio(
aspectRatio: aspectRatioImage,
child: FittedBox(child: widget.walletImage, fit: BoxFit.fill)),
),
Padding(
padding: EdgeInsets.only(top: 24),
child: Form(
key: _formKey,
child: Stack(
alignment: Alignment.centerRight,
children: [
TextFormField(
onChanged: (value) => _walletNewVM.name = value,
controller: _controller,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme!.headline6!.color!),
decoration: InputDecoration(
hintStyle: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.w500,
color: Theme.of(context).accentTextTheme!.headline2!.color!),
hintText: S.of(context).wallet_name,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context)
.accentTextTheme!
.headline2!
.decorationColor!,
width: 1.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context)
.accentTextTheme!
.headline2!
.decorationColor!,
width: 1.0),
),
suffixIcon: IconButton(
onPressed: () async {
final rName = await generateName();
FocusManager.instance.primaryFocus?.unfocus();
setState(() {
_controller.text = rName;
_walletNewVM.name = rName;
_controller.selection = TextSelection.fromPosition(
TextPosition(offset: _controller.text.length));
});
},
icon: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6.0),
color: Theme.of(context).hintColor,
),
width: 34,
height: 34,
child: Image.asset(
'assets/images/refresh_icon.png',
color: Theme.of(context)
.primaryTextTheme!
.headline4!
.decorationColor!,
setState(() {
_controller.text = rName;
_walletNewVM.name = rName;
_controller.selection = TextSelection.fromPosition(
TextPosition(offset: _controller.text.length));
});
},
icon: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6.0),
color: Theme.of(context).hintColor,
),
width: 34,
height: 34,
child: Image.asset(
'assets/images/refresh_icon.png',
color: Theme.of(context)
.primaryTextTheme!
.headline4!
.decorationColor!,
),
),
),
),
validator: WalletNameValidator(),
),
),
],
),
validator: WalletNameValidator(),
),
],
),
),
if (_walletNewVM.hasLanguageSelector) ...[
Padding(
padding: EdgeInsets.only(top: 40),
child: Text(
S.of(context).seed_language_choose,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w500,
color: Theme.of(context).primaryTextTheme!.headline6!.color!),
),
),
Padding(
padding: EdgeInsets.only(top: 24),
child: SeedLanguageSelector(
key: _languageSelectorKey, initialSelected: defaultSeedLanguage),
)
]
],
),
),
if (_walletNewVM.hasLanguageSelector) ...[
Padding(
padding: EdgeInsets.only(top: 40),
child: Text(
S.of(context).seed_language_choose,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w500,
color: Theme.of(context).primaryTextTheme!.headline6!.color!),
),
),
Padding(
padding: EdgeInsets.only(top: 24),
child: SeedLanguageSelector(
key: _languageSelectorKey,
initialSelected: defaultSeedLanguage),
)
]
]),
bottomSectionPadding:
EdgeInsets.all(24),
),
bottomSectionPadding: EdgeInsets.all(24),
bottomSection: Column(
children: [
Observer(
@@ -1,3 +1,4 @@
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:flutter/material.dart';
@@ -13,23 +14,19 @@ class NewWalletTypePage extends BasePage {
final void Function(BuildContext, WalletType) onTypeSelected;
final walletTypeImage = Image.asset('assets/images/wallet_type.png');
final walletTypeLightImage =
Image.asset('assets/images/wallet_type_light.png');
final walletTypeLightImage = Image.asset('assets/images/wallet_type_light.png');
@override
String get title => S.current.wallet_list_restore_wallet;
@override
Widget body(BuildContext context) => WalletTypeForm(
onTypeSelected: onTypeSelected,
walletImage: currentTheme.type == ThemeType.dark
? walletTypeImage
: walletTypeLightImage);
onTypeSelected: onTypeSelected,
walletImage: currentTheme.type == ThemeType.dark ? walletTypeImage : walletTypeLightImage);
}
class WalletTypeForm extends StatefulWidget {
WalletTypeForm({required this.onTypeSelected,
required this.walletImage});
WalletTypeForm({required this.onTypeSelected, required this.walletImage});
final void Function(BuildContext, WalletType) onTypeSelected;
final Image walletImage;
@@ -39,22 +36,16 @@ class WalletTypeForm extends StatefulWidget {
}
class WalletTypeFormState extends State<WalletTypeForm> {
WalletTypeFormState()
: types = availableWalletTypes;
WalletTypeFormState() : types = availableWalletTypes;
static const aspectRatioImage = 1.22;
final moneroIcon =
Image.asset('assets/images/monero_logo.png', height: 24, width: 24);
final bitcoinIcon =
Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
final litecoinIcon =
Image.asset('assets/images/litecoin_icon.png', height: 24, width: 24);
final moneroIcon = Image.asset('assets/images/monero_logo.png', height: 24, width: 24);
final bitcoinIcon = Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
final litecoinIcon = Image.asset('assets/images/litecoin_icon.png', height: 24, width: 24);
final walletTypeImage = Image.asset('assets/images/wallet_type.png');
final walletTypeLightImage =
Image.asset('assets/images/wallet_type_light.png');
final havenIcon =
Image.asset('assets/images/haven_logo.png', height: 24, width: 24);
final walletTypeLightImage = Image.asset('assets/images/wallet_type_light.png');
final havenIcon = Image.asset('assets/images/haven_logo.png', height: 24, width: 24);
WalletType? selected;
List<WalletType> types;
@@ -69,35 +60,40 @@ class WalletTypeFormState extends State<WalletTypeForm> {
Widget build(BuildContext context) {
return ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
content: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: 12, right: 12),
child: AspectRatio(
aspectRatio: aspectRatioImage,
child: FittedBox(child: widget.walletImage, fit: BoxFit.fill)),
content: Center(
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: 12, right: 12),
child: AspectRatio(
aspectRatio: aspectRatioImage,
child: FittedBox(child: widget.walletImage, fit: BoxFit.fill)),
),
Padding(
padding: EdgeInsets.only(top: 48),
child: Text(
S.of(context).choose_wallet_currency,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context).primaryTextTheme.headline6!.color!),
),
),
...types.map((type) => Padding(
padding: EdgeInsets.only(top: 24),
child: SelectButton(
image: _iconFor(type),
text: walletTypeToDisplayName(type),
isSelected: selected == type,
onTap: () => setState(() => selected = type)),
))
],
),
Padding(
padding: EdgeInsets.only(top: 48),
child: Text(
S.of(context).choose_wallet_currency,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context).primaryTextTheme.headline6!.color!),
),
),
...types.map((type) => Padding(
padding: EdgeInsets.only(top: 24),
child: SelectButton(
image: _iconFor(type),
text: walletTypeToDisplayName(type),
isSelected: selected == type,
onTap: () => setState(() => selected = type)),
))
],
),
),
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
bottomSection: PrimaryButton(
@@ -121,7 +117,8 @@ class WalletTypeFormState extends State<WalletTypeForm> {
case WalletType.haven:
return havenIcon;
default:
throw Exception('_iconFor: Incorrect Wallet Type. Cannot find icon for Wallet Type: ${type.toString()}');
throw Exception(
'_iconFor: Incorrect Wallet Type. Cannot find icon for Wallet Type: ${type.toString()}');
}
}
@@ -4,7 +4,6 @@ import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cw_core/node.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
+192 -165
View File
@@ -1,17 +1,19 @@
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:another_flushbar/flushbar.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter/services.dart';
class PinCodeWidget extends StatefulWidget {
PinCodeWidget(
{required Key key,
required this.onFullPin,
required this.initialPinLength,
required this.onChangedPin,
required this.hasLengthSwitcher,
this.onChangedPinLength,})
: super(key: key);
PinCodeWidget({
required Key key,
required this.onFullPin,
required this.initialPinLength,
required this.onChangedPin,
required this.hasLengthSwitcher,
this.onChangedPinLength,
}) : super(key: key);
final void Function(String pin, PinCodeState state) onFullPin;
final void Function(String pin) onChangedPin;
@@ -25,10 +27,10 @@ class PinCodeWidget extends StatefulWidget {
class PinCodeState<T extends PinCodeWidget> extends State<T> {
PinCodeState()
: _aspectRatio = 0,
pinLength = 0,
pin = '',
title = '';
: _aspectRatio = 0,
pinLength = 0,
pin = '',
title = '';
static const defaultPinLength = fourPinLength;
static const sixPinLength = 6;
static const fourPinLength = 4;
@@ -75,8 +77,7 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
void setDefaultPinLength() => changePinLength(widget.initialPinLength);
void calculateAspectRatio() {
final renderBox =
_gridViewKey.currentContext!.findRenderObject() as RenderBox;
final renderBox = _gridViewKey.currentContext!.findRenderObject() as RenderBox;
final cellWidth = renderBox.size.width / 3;
final cellHeight = renderBox.size.height / 4;
@@ -89,8 +90,7 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
void changeProcessText(String text) {
hideProgressText();
_progressBar = createBar<void>(text, duration: null)
..show(_key.currentContext!);
_progressBar = createBar<void>(text, duration: null)..show(_key.currentContext!);
}
void close() {
@@ -104,8 +104,8 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
}
@override
Widget build(BuildContext context) => Scaffold(
key: _key, body: body(context), resizeToAvoidBottomInset: false);
Widget build(BuildContext context) =>
Scaffold(key: _key, body: body(context), resizeToAvoidBottomInset: false);
Widget body(BuildContext context) {
final deleteIconImage = Image.asset(
@@ -117,157 +117,184 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
color: Theme.of(context).primaryTextTheme!.headline6!.color!,
);
return Container(
color: Theme.of(context).backgroundColor,
padding: EdgeInsets.only(left: 40.0, right: 40.0, bottom: 40.0),
child: Column(children: <Widget>[
Spacer(flex: 2),
Text(title,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
color: Theme.of(context).primaryTextTheme!.headline6!.color!)),
Spacer(flex: 3),
Container(
width: 180,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: List.generate(pinLength, (index) {
const size = 10.0;
final isFilled = pin.length > index ? pin[index] != null : false;
return Container(
width: size,
height: size,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: isFilled
? Theme.of(context).primaryTextTheme!.headline6!.color!
: Theme.of(context)
.accentTextTheme!
.bodyText2!
.color!
.withOpacity(0.25),
));
}),
),
),
Spacer(flex: 2),
if (widget.hasLengthSwitcher) ...[
TextButton(
onPressed: () {
changePinLength(pinLength == PinCodeState.fourPinLength
? PinCodeState.sixPinLength
: PinCodeState.fourPinLength);
},
child: Text(
_changePinLengthText(),
return RawKeyboardListener(
focusNode: FocusNode(),
autofocus: true,
onKey: (keyEvent) {
if (keyEvent is RawKeyDownEvent) {
if (keyEvent.logicalKey.keyLabel == "Backspace") {
_pop();
return;
}
int? number = int.tryParse(keyEvent.character ?? '');
if (number != null) {
_push(number);
}
}
},
child: Container(
color: Theme.of(context).backgroundColor,
padding: EdgeInsets.only(left: 40.0, right: 40.0, bottom: 40.0),
child: Column(
children: <Widget>[
Spacer(flex: 2),
Text(title,
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.normal,
color: Theme.of(context)
.accentTextTheme!
.bodyText2!
.decorationColor!),
))
],
Spacer(flex: 1),
Flexible(
flex: 24,
child: Container(
key: _gridViewKey,
child: _aspectRatio > 0
? GridView.count(
shrinkWrap: true,
crossAxisCount: 3,
childAspectRatio: _aspectRatio,
physics: const NeverScrollableScrollPhysics(),
children: List.generate(12, (index) {
const double marginRight = 15;
const double marginLeft = 15;
fontSize: 20,
fontWeight: FontWeight.w500,
color: Theme.of(context).primaryTextTheme!.headline6!.color!)),
Spacer(flex: 3),
Container(
width: 180,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: List.generate(pinLength, (index) {
const size = 10.0;
final isFilled = pin.length > index ? pin[index] != null : false;
if (index == 9) {
return Container(
margin: EdgeInsets.only(
left: marginLeft, right: marginRight),
child: TextButton(
onPressed: () => null,
// (widget.hasLengthSwitcher ||
// !settingsStore
// .allowBiometricalAuthentication)
// ? null
// : () {
// FIXME
// if (authStore != null) {
// WidgetsBinding.instance.addPostFrameCallback((_) {
// final biometricAuth = BiometricAuth();
// biometricAuth.isAuthenticated().then(
// (isAuth) {
// if (isAuth) {
// authStore.biometricAuth();
// _key.currentState.showSnackBar(
// SnackBar(
// content: Text(S.of(context).authenticated),
// backgroundColor: Colors.green,
// ),
// );
// }
// }
// );
// });
// }
// },
// FIX-ME: Style
//color: Theme.of(context).backgroundColor,
//shape: CircleBorder(),
child: Container()
// (widget.hasLengthSwitcher ||
// !settingsStore
// .allowBiometricalAuthentication)
// ? Offstage()
// : faceImage,
return Container(
width: size,
height: size,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: isFilled
? Theme.of(context).primaryTextTheme!.headline6!.color!
: Theme.of(context)
.accentTextTheme!
.bodyText2!
.color!
.withOpacity(0.25),
));
}),
),
),
Spacer(flex: 2),
if (widget.hasLengthSwitcher) ...[
TextButton(
onPressed: () {
changePinLength(pinLength == PinCodeState.fourPinLength
? PinCodeState.sixPinLength
: PinCodeState.fourPinLength);
},
child: Text(
_changePinLengthText(),
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.normal,
color: Theme.of(context).accentTextTheme!.bodyText2!.decorationColor!),
),
)
],
Spacer(flex: 1),
Flexible(
flex: 24,
child: Center(
child: ConstrainedBox(
constraints: BoxConstraints(
maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint,
),
child: Container(
key: _gridViewKey,
child: _aspectRatio > 0
? ScrollConfiguration(
behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false),
child: GridView.count(
shrinkWrap: true,
crossAxisCount: 3,
childAspectRatio: _aspectRatio,
physics: const NeverScrollableScrollPhysics(),
children: List.generate(12, (index) {
const double marginRight = 15;
const double marginLeft = 15;
if (index == 9) {
return Container(
margin: EdgeInsets.only(left: marginLeft, right: marginRight),
child: TextButton(
onPressed: () => null,
// (widget.hasLengthSwitcher ||
// !settingsStore
// .allowBiometricalAuthentication)
// ? null
// : () {
// FIXME
// if (authStore != null) {
// WidgetsBinding.instance.addPostFrameCallback((_) {
// final biometricAuth = BiometricAuth();
// biometricAuth.isAuthenticated().then(
// (isAuth) {
// if (isAuth) {
// authStore.biometricAuth();
// _key.currentState.showSnackBar(
// SnackBar(
// content: Text(S.of(context).authenticated),
// backgroundColor: Colors.green,
// ),
// );
// }
// }
// );
// });
// }
// },
// FIX-ME: Style
//color: Theme.of(context).backgroundColor,
//shape: CircleBorder(),
child: Container()
// (widget.hasLengthSwitcher ||
// !settingsStore
// .allowBiometricalAuthentication)
// ? Offstage()
// : faceImage,
),
);
} else if (index == 10) {
index = 0;
} else if (index == 11) {
return Container(
margin: EdgeInsets.only(left: marginLeft, right: marginRight),
child: TextButton(
onPressed: () => _pop(),
style: TextButton.styleFrom(
backgroundColor: Theme.of(context).backgroundColor,
shape: CircleBorder(),
),
child: deleteIconImage,
),
);
} else {
index++;
}
return Container(
margin: EdgeInsets.only(left: marginLeft, right: marginRight),
child: TextButton(
onPressed: () => _push(index),
style: TextButton.styleFrom(
backgroundColor: Theme.of(context).backgroundColor,
shape: CircleBorder(),
),
child: Text('$index',
style: TextStyle(
fontSize: 30.0,
fontWeight: FontWeight.w600,
color: Theme.of(context)
.primaryTextTheme!
.headline6!
.color!)),
),
);
} else if (index == 10) {
index = 0;
} else if (index == 11) {
return Container(
margin: EdgeInsets.only(
left: marginLeft, right: marginRight),
child: TextButton(
onPressed: () => _pop(),
// FIX-ME: Style
//color: Theme.of(context).backgroundColor,
//shape: CircleBorder(),
child: deleteIconImage,
),
);
} else {
index++;
}
return Container(
margin: EdgeInsets.only(
left: marginLeft, right: marginRight),
child: TextButton(
onPressed: () => _push(index),
// FIX-ME: Style
//color: Theme.of(context).backgroundColor,
//shape: CircleBorder(),
child: Text('$index',
style: TextStyle(
fontSize: 30.0,
fontWeight: FontWeight.w600,
color: Theme.of(context)
.primaryTextTheme!
.headline6!
.color!)),
);
}),
),
);
}),
)
: null))
]),
)
: null,
),
),
),
)
],
),
),
);
}
@@ -8,6 +8,7 @@ import 'package:cake_wallet/src/screens/dashboard/widgets/present_receive_option
import 'package:cake_wallet/src/screens/receive/widgets/anonpay_input_form.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/view_model/anon_invoice_page_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart';
import 'package:flutter/material.dart';
@@ -85,7 +86,7 @@ class AnonPayInvoicePage extends BasePage {
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(bottom: 24),
content: Container(
decoration: BoxDecoration(
decoration: DeviceInfo.instance.isMobile ? BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)),
gradient: LinearGradient(
@@ -96,7 +97,7 @@ class AnonPayInvoicePage extends BasePage {
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
) : null,
child: Observer(builder: (_) {
return Padding(
padding: EdgeInsets.fromLTRB(24, 120, 24, 0),
@@ -174,7 +175,6 @@ class AnonPayInvoicePage extends BasePage {
}
reaction((_) => receiveOptionViewModel.selectedReceiveOption, (ReceivePageOption option) {
Navigator.pop(context);
switch (option) {
case ReceivePageOption.mainnet:
Navigator.popAndPushNamed(context, Routes.addressPage);
+43 -31
View File
@@ -1,4 +1,5 @@
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:device_display_brightness/device_display_brightness.dart';
@@ -35,7 +36,7 @@ class QRWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final copyImage = Image.asset('assets/images/copy_address.png',
color: Theme.of(context).textTheme!.subtitle1!.decorationColor!);
color: Theme.of(context).textTheme.subtitle1!.decorationColor!);
return Column(
mainAxisSize: MainAxisSize.min,
@@ -51,23 +52,18 @@ class QRWidget extends StatelessWidget {
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!),
color: Theme.of(context).accentTextTheme.headline2!.backgroundColor!),
),
),
Row(
children: <Widget>[
Spacer(flex: 3),
Observer(
builder: (_) {
return Flexible(
flex: 5,
child: GestureDetector(
onTap: () async {
// Get the current brightness:
final double brightness = await DeviceDisplayBrightness.getBrightness();
// ignore: unawaited_futures
DeviceDisplayBrightness.setBrightness(1.0);
builder: (_) => Flexible(
flex: 5,
child: GestureDetector(
onTap: () {
changeBrightnessForRoute(() async {
await Navigator.pushNamed(
context,
Routes.fullscreenQR,
@@ -75,31 +71,28 @@ class QRWidget extends StatelessWidget {
'qrData': addressListViewModel.uri.toString(),
},
);
// ignore: unawaited_futures
DeviceDisplayBrightness.setBrightness(brightness);
},
child: Hero(
tag: Key(addressListViewModel.uri.toString()),
child: Center(
child: AspectRatio(
aspectRatio: 1.0,
child: Container(
padding: EdgeInsets.all(5),
decoration: BoxDecoration(
border: Border.all(
width: 3,
color:
Theme.of(context).accentTextTheme!.headline2!.backgroundColor!,
),
});
},
child: Hero(
tag: Key(addressListViewModel.uri.toString()),
child: Center(
child: AspectRatio(
aspectRatio: 1.0,
child: Container(
padding: EdgeInsets.all(5),
decoration: BoxDecoration(
border: Border.all(
width: 3,
color: Theme.of(context).accentTextTheme.headline2!.backgroundColor!,
),
child: QrImage(data: addressListViewModel.uri.toString(), version: qrVersion),
),
child: QrImage(data: addressListViewModel.uri.toString()),
),
),
),
),
);
}
),
),
),
Spacer(flex: 3)
],
@@ -176,4 +169,23 @@ class QRWidget extends StatelessWidget {
],
);
}
Future<void> changeBrightnessForRoute(Future<void> Function() navigation) async {
// if not mobile, just navigate
if (!DeviceInfo.instance.isMobile) {
navigation();
return;
}
// Get the current brightness:
final brightness = await DeviceDisplayBrightness.getBrightness();
// ignore: unawaited_futures
DeviceDisplayBrightness.setBrightness(1.0);
await navigation();
// ignore: unawaited_futures
DeviceDisplayBrightness.setBrightness(brightness);
}
}
@@ -1,6 +1,7 @@
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/framework.dart';
@@ -39,43 +40,48 @@ class RestoreFromBackupPage extends BasePage {
}
});
return Container(
padding: EdgeInsets.only(bottom: 24, left: 24, right: 24),
child: Column(children: [
Expanded(
child: Container(
child: Center(
child: TextFormField(
obscureText: true,
enableSuggestions: false,
autocorrect: false,
decoration: InputDecoration(
hintText: S.of(context).enter_backup_password),
keyboardType: TextInputType.visiblePassword,
controller: textEditingController,
style: TextStyle(fontSize: 26, color: Colors.black))),
),
),
Container(
child: Row(children: [
Expanded(
child: PrimaryButton(
onPressed: () => presentFilePicker(),
text: S.of(context).select_backup_file,
color: Colors.grey,
textColor: Colors.white)),
SizedBox(width: 20),
Expanded(child: Observer(builder: (_) {
return LoadingPrimaryButton(
isLoading:
restoreFromBackupViewModel.state is IsExecutingState,
onPressed: () => onImportHandler(context),
text: S.of(context).import,
color: Theme.of(context).accentTextTheme!.bodyText1!.color!,
textColor: Colors.white);
}))
])),
]));
return Center(
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint),
child: Padding(
padding: EdgeInsets.only(bottom: 24, left: 24, right: 24),
child: Column(children: [
Expanded(
child: Container(
child: Center(
child: TextFormField(
obscureText: true,
enableSuggestions: false,
autocorrect: false,
decoration: InputDecoration(
hintText: S.of(context).enter_backup_password),
keyboardType: TextInputType.visiblePassword,
controller: textEditingController,
style: TextStyle(fontSize: 26, color: Colors.black))),
),
),
Container(
child: Row(children: [
Expanded(
child: PrimaryButton(
onPressed: () => presentFilePicker(),
text: S.of(context).select_backup_file,
color: Colors.grey,
textColor: Colors.white)),
SizedBox(width: 20),
Expanded(child: Observer(builder: (_) {
return LoadingPrimaryButton(
isLoading:
restoreFromBackupViewModel.state is IsExecutingState,
onPressed: () => onImportHandler(context),
text: S.of(context).import,
color: Theme.of(context).accentTextTheme!.bodyText1!.color!,
textColor: Colors.white);
}))
])),
])),
),
);
}
Future<void> presentFilePicker() async {
@@ -1,3 +1,4 @@
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart';
import 'package:flutter/cupertino.dart';
@@ -18,31 +19,33 @@ class RestoreOptionsPage extends BasePage {
@override
Widget body(BuildContext context) {
return Container(
width: double.infinity,
height: double.infinity,
padding: EdgeInsets.all(24),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
RestoreButton(
onPressed: () =>
Navigator.pushNamed(context, Routes.restoreWalletOptionsFromWelcome),
image: imageSeedKeys,
title: S.of(context).restore_title_from_seed_keys,
description:
S.of(context).restore_description_from_seed_keys),
Padding(
padding: EdgeInsets.only(top: 24),
child: RestoreButton(
return Center(
child: Container(
width: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint,
height: double.infinity,
padding: EdgeInsets.symmetric(vertical: 24),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
RestoreButton(
onPressed: () =>
Navigator.pushNamed(context, Routes.restoreFromBackup),
image: imageBackup,
title: S.of(context).restore_title_from_backup,
description: S.of(context).restore_description_from_backup),
)
],
),
));
Navigator.pushNamed(context, Routes.restoreWalletOptionsFromWelcome),
image: imageSeedKeys,
title: S.of(context).restore_title_from_seed_keys,
description:
S.of(context).restore_description_from_seed_keys),
Padding(
padding: EdgeInsets.only(top: 24),
child: RestoreButton(
onPressed: () =>
Navigator.pushNamed(context, Routes.restoreFromBackup),
image: imageBackup,
title: S.of(context).restore_title_from_backup,
description: S.of(context).restore_description_from_backup),
)
],
),
)),
);
}
}
+115 -127
View File
@@ -1,5 +1,6 @@
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
@@ -23,10 +24,8 @@ import 'package:cake_wallet/core/seed_validator.dart';
class WalletRestorePage extends BasePage {
WalletRestorePage(this.walletRestoreViewModel)
: walletRestoreFromSeedFormKey =
GlobalKey<WalletRestoreFromSeedFormState>(),
walletRestoreFromKeysFormKey =
GlobalKey<WalletRestoreFromKeysFromState>(),
: walletRestoreFromSeedFormKey = GlobalKey<WalletRestoreFromSeedFormState>(),
walletRestoreFromKeysFormKey = GlobalKey<WalletRestoreFromKeysFromState>(),
_pages = [],
_blockHeightFocusNode = FocusNode(),
_controller = PageController(initialPage: 0) {
@@ -36,9 +35,8 @@ class WalletRestorePage extends BasePage {
_pages.add(WalletRestoreFromSeedForm(
displayBlockHeightSelector:
walletRestoreViewModel.hasBlockchainHeightLanguageSelector,
displayLanguageSelector:
walletRestoreViewModel.hasSeedLanguageSelector,
type: walletRestoreViewModel.type!,
displayLanguageSelector: walletRestoreViewModel.hasSeedLanguageSelector,
type: walletRestoreViewModel.type,
key: walletRestoreFromSeedFormKey,
blockHeightFocusNode: _blockHeightFocusNode,
onHeightOrDateEntered: (value) {
@@ -48,26 +46,22 @@ class WalletRestorePage extends BasePage {
},
onSeedChange: (String seed) {
if (walletRestoreViewModel.hasBlockchainHeightLanguageSelector) {
final hasHeight = walletRestoreFromSeedFormKey
.currentState!.blockchainHeightKey.currentState!.restoreHeightController
.text
.isNotEmpty;
final hasHeight = walletRestoreFromSeedFormKey.currentState!.blockchainHeightKey
.currentState!.restoreHeightController.text.isNotEmpty;
if (hasHeight) {
walletRestoreViewModel.isButtonEnabled = _isValidSeed();
}
} else {
walletRestoreViewModel.isButtonEnabled = _isValidSeed();
walletRestoreViewModel.isButtonEnabled = _isValidSeed();
}
},
onLanguageChange: (_) {
if (walletRestoreViewModel.hasBlockchainHeightLanguageSelector) {
final hasHeight = walletRestoreFromSeedFormKey
.currentState!.blockchainHeightKey.currentState!.restoreHeightController
.text
.isNotEmpty;
final hasHeight = walletRestoreFromSeedFormKey.currentState!.blockchainHeightKey
.currentState!.restoreHeightController.text.isNotEmpty;
if (hasHeight) {
walletRestoreViewModel.isButtonEnabled = _isValidSeed();
walletRestoreViewModel.isButtonEnabled = _isValidSeed();
}
} else {
walletRestoreViewModel.isButtonEnabled = _isValidSeed();
@@ -78,8 +72,7 @@ class WalletRestorePage extends BasePage {
_pages.add(WalletRestoreFromKeysFrom(
key: walletRestoreFromKeysFormKey,
walletRestoreViewModel: walletRestoreViewModel,
onHeightOrDateEntered: (value) =>
walletRestoreViewModel.isButtonEnabled = value));
onHeightOrDateEntered: (value) => walletRestoreViewModel.isButtonEnabled = value));
break;
default:
break;
@@ -97,8 +90,7 @@ class WalletRestorePage extends BasePage {
fontSize: 18.0,
fontWeight: FontWeight.bold,
fontFamily: 'Lato',
color: titleColor ??
Theme.of(context).primaryTextTheme!.headline6!.color!),
color: titleColor ?? Theme.of(context).primaryTextTheme.headline6!.color!),
));
final WalletRestoreViewModel walletRestoreViewModel;
@@ -129,134 +121,134 @@ class WalletRestorePage extends BasePage {
reaction((_) => walletRestoreViewModel.mode, (WalletRestoreMode mode) {
walletRestoreViewModel.isButtonEnabled = false;
walletRestoreFromSeedFormKey.currentState!.blockchainHeightKey.currentState
!.restoreHeightController.text = '';
walletRestoreFromSeedFormKey.currentState!.blockchainHeightKey.currentState
!.dateController.text = '';
walletRestoreFromSeedFormKey
.currentState!.blockchainHeightKey.currentState!.restoreHeightController.text = '';
walletRestoreFromSeedFormKey
.currentState!.blockchainHeightKey.currentState!.dateController.text = '';
walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.text = '';
walletRestoreFromKeysFormKey.currentState!.blockchainHeightKey.currentState
!.restoreHeightController.text = '';
walletRestoreFromKeysFormKey.currentState!.blockchainHeightKey.currentState
!.dateController.text = '';
walletRestoreFromKeysFormKey
.currentState!.blockchainHeightKey.currentState!.restoreHeightController.text = '';
walletRestoreFromKeysFormKey
.currentState!.blockchainHeightKey.currentState!.dateController.text = '';
walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.text = '';
});
return KeyboardActions(
config: KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
keyboardBarColor: Theme.of(context).accentTextTheme!.bodyText1!
.backgroundColor!,
nextFocus: false,
actions: [
KeyboardActionsItem(
focusNode: _blockHeightFocusNode,
toolbarButtons: [(_) => KeyboardDoneButton()],
)
]),
child: Container(
height: 0,
color: Theme.of(context).backgroundColor,
child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
Expanded(
child: PageView.builder(
onPageChanged: (page) {
walletRestoreViewModel.mode =
page == 0 ? WalletRestoreMode.seed : WalletRestoreMode.keys;
},
controller: _controller,
itemCount: _pages.length,
itemBuilder: (_, index) =>
SingleChildScrollView(child: _pages[index]))),
if (_pages.length > 1)
Padding(
padding: EdgeInsets.only(top: 10),
child: SmoothPageIndicator(
controller: _controller,
count: _pages.length,
effect: ColorTransitionEffect(
spacing: 6.0,
radius: 6.0,
dotWidth: 6.0,
dotHeight: 6.0,
dotColor: Theme.of(context).hintColor.withOpacity(0.5),
activeDotColor: Theme.of(context).hintColor),
)),
Padding(
padding: EdgeInsets.only(top: 20, bottom: 24, left: 24, right: 24),
child: Observer(
builder: (context) {
return LoadingPrimaryButton(
onPressed: _confirmForm,
text: S.of(context).restore_recover,
color:
Theme.of(context).accentTextTheme!.subtitle2!.decorationColor!,
textColor:
Theme.of(context).accentTextTheme!.headline5!.decorationColor!,
isLoading: walletRestoreViewModel.state is IsExecutingState,
isDisabled: !walletRestoreViewModel.isButtonEnabled,
);
},
))
])));
config: KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
keyboardBarColor: Theme.of(context).accentTextTheme.bodyText1!.backgroundColor!,
nextFocus: false,
actions: [
KeyboardActionsItem(
focusNode: _blockHeightFocusNode,
toolbarButtons: [(_) => KeyboardDoneButton()],
)
],
),
child: Container(
height: 0,
color: Theme.of(context).backgroundColor,
child: Center(
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: PageView.builder(
onPageChanged: (page) {
walletRestoreViewModel.mode =
page == 0 ? WalletRestoreMode.seed : WalletRestoreMode.keys;
},
controller: _controller,
itemCount: _pages.length,
itemBuilder: (_, index) => SingleChildScrollView(child: _pages[index]),
),
),
if (_pages.length > 1)
Padding(
padding: EdgeInsets.only(top: 10),
child: SmoothPageIndicator(
controller: _controller,
count: _pages.length,
effect: ColorTransitionEffect(
spacing: 6.0,
radius: 6.0,
dotWidth: 6.0,
dotHeight: 6.0,
dotColor: Theme.of(context).hintColor.withOpacity(0.5),
activeDotColor: Theme.of(context).hintColor,
),
),
),
Padding(
padding: EdgeInsets.only(top: 20, bottom: 24, left: 24, right: 24),
child: Observer(
builder: (context) {
return LoadingPrimaryButton(
onPressed: _confirmForm,
text: S.of(context).restore_recover,
color: Theme.of(context).accentTextTheme.subtitle2!.decorationColor!,
textColor: Theme.of(context).accentTextTheme.headline5!.decorationColor!,
isLoading: walletRestoreViewModel.state is IsExecutingState,
isDisabled: !walletRestoreViewModel.isButtonEnabled,
);
},
),
)
],
),
),
),
),
);
}
bool _isValidSeed() {
final seedWords = walletRestoreFromSeedFormKey
.currentState
!.seedWidgetStateKey
.currentState
!.text
.split(' ');
final seedWords =
walletRestoreFromSeedFormKey.currentState!.seedWidgetStateKey.currentState!.text.split(' ');
if ((walletRestoreViewModel.type == WalletType.monero || walletRestoreViewModel.type == WalletType.haven) &&
if ((walletRestoreViewModel.type == WalletType.monero ||
walletRestoreViewModel.type == WalletType.haven) &&
seedWords.length != WalletRestoreViewModelBase.moneroSeedMnemonicLength) {
return false;
}
if ((walletRestoreViewModel.type == WalletType.bitcoin ||
walletRestoreViewModel.type == WalletType.litecoin) &&
(seedWords.length != WalletRestoreViewModelBase.electrumSeedMnemonicLength &&
seedWords.length != WalletRestoreViewModelBase.electrumShortSeedMnemonicLength)) {
walletRestoreViewModel.type == WalletType.litecoin) &&
(seedWords.length != WalletRestoreViewModelBase.electrumSeedMnemonicLength &&
seedWords.length != WalletRestoreViewModelBase.electrumShortSeedMnemonicLength)) {
return false;
}
final words = walletRestoreFromSeedFormKey
.currentState
!.seedWidgetStateKey
.currentState
!.words
.toSet();
return seedWords
.toSet()
.difference(words)
.toSet()
.isEmpty;
final words =
walletRestoreFromSeedFormKey.currentState!.seedWidgetStateKey.currentState!.words.toSet();
return seedWords.toSet().difference(words).toSet().isEmpty;
}
Map<String, dynamic> _credentials() {
final credentials = <String, dynamic>{};
if (walletRestoreViewModel.mode == WalletRestoreMode.seed) {
credentials['seed'] = walletRestoreFromSeedFormKey
.currentState!.seedWidgetStateKey.currentState!.text;
credentials['seed'] =
walletRestoreFromSeedFormKey.currentState!.seedWidgetStateKey.currentState!.text;
if (walletRestoreViewModel.hasBlockchainHeightLanguageSelector) {
credentials['height'] = walletRestoreFromSeedFormKey
.currentState!.blockchainHeightKey.currentState!.height;
credentials['height'] =
walletRestoreFromSeedFormKey.currentState!.blockchainHeightKey.currentState!.height;
}
credentials['name'] = walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.text;
credentials['name'] =
walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.text;
} else {
credentials['address'] =
walletRestoreFromKeysFormKey.currentState!.addressController.text;
credentials['viewKey'] =
walletRestoreFromKeysFormKey.currentState!.viewKeyController.text;
credentials['spendKey'] =
walletRestoreFromKeysFormKey.currentState!.spendKeyController.text;
credentials['height'] = walletRestoreFromKeysFormKey
.currentState!.blockchainHeightKey.currentState!.height;
credentials['name'] = walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.text;
credentials['address'] = walletRestoreFromKeysFormKey.currentState!.addressController.text;
credentials['viewKey'] = walletRestoreFromKeysFormKey.currentState!.viewKeyController.text;
credentials['spendKey'] = walletRestoreFromKeysFormKey.currentState!.spendKeyController.text;
credentials['height'] =
walletRestoreFromKeysFormKey.currentState!.blockchainHeightKey.currentState!.height;
credentials['name'] =
walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.text;
}
return credentials;
@@ -272,10 +264,8 @@ class WalletRestorePage extends BasePage {
: walletRestoreFromKeysFormKey.currentState!.formKey;
final name = walletRestoreViewModel.mode == WalletRestoreMode.seed
? walletRestoreFromSeedFormKey
.currentState!.nameTextEditingController.value.text
: walletRestoreFromKeysFormKey
.currentState!.nameTextEditingController.value.text;
? walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.value.text
: walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.value.text;
if (!formKey.currentState!.validate()) {
return;
@@ -301,5 +291,3 @@ class WalletRestorePage extends BasePage {
});
}
}
+4 -1
View File
@@ -1,5 +1,6 @@
import 'dart:async';
import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/payment_request.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart';
@@ -54,7 +55,9 @@ class RootState extends State<Root> with WidgetsBindingObserver {
WidgetsBinding.instance.addObserver(this);
super.initState();
initUniLinks();
if (DeviceInfo.instance.isMobile) {
initUniLinks();
}
}
@override
+41 -36
View File
@@ -1,3 +1,4 @@
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/themes/theme_base.dart';
@@ -33,44 +34,48 @@ class PreSeedPage extends BasePage {
return WillPopScope(
onWillPop: () async => false,
child: Container(
alignment: Alignment.center,
padding: EdgeInsets.all(24),
child: Column(
children: [
Flexible(
flex: 2,
child: AspectRatio(
aspectRatio: 1,
child: FittedBox(child: image, fit: BoxFit.contain))),
Flexible(
flex: 3,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: EdgeInsets.only(top: 70, left: 16, right: 16),
child: Text(
S
.of(context)
.pre_seed_description(wordsCount.toString()),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.normal,
color: Theme.of(context)
.primaryTextTheme!
.caption!
.color!),
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint),
child: Column(
children: [
Flexible(
flex: 2,
child: AspectRatio(
aspectRatio: 1,
child: FittedBox(child: image, fit: BoxFit.contain))),
Flexible(
flex: 3,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: EdgeInsets.only(top: 70, left: 16, right: 16),
child: Text(
S
.of(context)
.pre_seed_description(wordsCount.toString()),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.normal,
color: Theme.of(context)
.primaryTextTheme!
.caption!
.color!),
),
),
),
PrimaryButton(
onPressed: () => Navigator.of(context)
.popAndPushNamed(Routes.seed, arguments: true),
text: S.of(context).pre_seed_button_text,
color: Theme.of(context).accentTextTheme!.bodyText1!.color!,
textColor: Colors.white)
],
))
],
PrimaryButton(
onPressed: () => Navigator.of(context)
.popAndPushNamed(Routes.seed, arguments: true),
text: S.of(context).pre_seed_button_text,
color: Theme.of(context).accentTextTheme!.bodyText1!.color!,
textColor: Colors.white)
],
))
],
),
),
));
}
+101 -95
View File
@@ -2,6 +2,7 @@ import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/utils/share_util.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:flutter/material.dart';
@@ -52,7 +53,7 @@ class WalletSeedPage extends BasePage {
@override
Widget? leading(BuildContext context) =>
isNewWalletCreated ? Offstage() : super.leading(context);
isNewWalletCreated ? null: super.leading(context);
@override
Widget trailing(BuildContext context) {
@@ -85,114 +86,119 @@ class WalletSeedPage extends BasePage {
return WillPopScope(onWillPop: () async => false, child: Container(
padding: EdgeInsets.all(24),
child: Column(
children: <Widget>[
Flexible(
flex: 2,
child: AspectRatio(
aspectRatio: 1,
child: FittedBox(child: image, fit: BoxFit.fill))),
Flexible(
flex: 3,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 33),
child: Observer(builder: (_) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
walletSeedViewModel.name,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
color: Theme.of(context)
.primaryTextTheme!
.headline6!
.color!),
),
Padding(
padding:
EdgeInsets.only(top: 20, left: 16, right: 16),
child: Text(
walletSeedViewModel.seed,
textAlign: TextAlign.center,
alignment: Alignment.center,
child: ConstrainedBox(
constraints:
BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint),
child: Column(
children: <Widget>[
Flexible(
flex: 2,
child: AspectRatio(
aspectRatio: 1,
child: FittedBox(child: image, fit: BoxFit.fill))),
Flexible(
flex: 3,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 33),
child: Observer(builder: (_) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
walletSeedViewModel.name,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.normal,
fontSize: 20,
fontWeight: FontWeight.w600,
color: Theme.of(context)
.primaryTextTheme!
.caption!
.headline6!
.color!),
),
)
],
);
}),
),
Column(
children: <Widget>[
isNewWalletCreated
? Padding(
padding: EdgeInsets.only(
bottom: 52, left: 43, right: 43),
Padding(
padding:
EdgeInsets.only(top: 20, left: 16, right: 16),
child: Text(
S.of(context).seed_reminder,
walletSeedViewModel.seed,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontSize: 14,
fontWeight: FontWeight.normal,
color: Theme.of(context)
.primaryTextTheme!
.overline!
.caption!
.color!),
),
)
: Offstage(),
Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Flexible(
child: Container(
padding: EdgeInsets.only(right: 8.0),
child: PrimaryButton(
onPressed: () {
ShareUtil.share(
text: walletSeedViewModel.seed,
context: context,
);
},
text: S.of(context).save,
color: Colors.green,
textColor: Colors.white),
)),
Flexible(
child: Container(
padding: EdgeInsets.only(left: 8.0),
child: Builder(
builder: (context) => PrimaryButton(
onPressed: () {
Clipboard.setData(ClipboardData(
text: walletSeedViewModel.seed));
showBar<void>(context,
S.of(context).copied_to_clipboard);
},
text: S.of(context).copy,
color: Theme.of(context)
.accentTextTheme!
.bodyText2!
.color!,
textColor: Colors.white)),
))
],
)
],
)
],
))
],
],
);
}),
),
Column(
children: <Widget>[
isNewWalletCreated
? Padding(
padding: EdgeInsets.only(
bottom: 52, left: 43, right: 43),
child: Text(
S.of(context).seed_reminder,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.normal,
color: Theme.of(context)
.primaryTextTheme!
.overline!
.color!),
),
)
: Offstage(),
Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Flexible(
child: Container(
padding: EdgeInsets.only(right: 8.0),
child: PrimaryButton(
onPressed: () {
ShareUtil.share(
text: walletSeedViewModel.seed,
context: context,
);
},
text: S.of(context).save,
color: Colors.green,
textColor: Colors.white),
)),
Flexible(
child: Container(
padding: EdgeInsets.only(left: 8.0),
child: Builder(
builder: (context) => PrimaryButton(
onPressed: () {
Clipboard.setData(ClipboardData(
text: walletSeedViewModel.seed));
showBar<void>(context,
S.of(context).copied_to_clipboard);
},
text: S.of(context).copy,
color: Theme.of(context)
.accentTextTheme!
.bodyText2!
.color!,
textColor: Colors.white)),
))
],
)
],
)
],
))
],
),
)));
}
}
+145 -176
View File
@@ -1,10 +1,12 @@
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.dart';
import 'package:cake_wallet/src/screens/send/widgets/send_card.dart';
import 'package:cake_wallet/src/widgets/add_template_button.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/src/widgets/picker.dart';
import 'package:cake_wallet/src/widgets/template_tile.dart';
import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/view_model/send/output.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
@@ -19,7 +21,6 @@ import 'package:cake_wallet/src/widgets/trail_button.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:dotted_border/dotted_border.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart';
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
@@ -50,9 +51,21 @@ class SendPage extends BasePage {
@override
bool get extendBodyBehindAppBar => true;
@override
bool get canUseCloseIcon => true;
@override
AppBarStyle get appBarStyle => AppBarStyle.transparent;
double _sendCardHeight(BuildContext context) {
final double initialHeight = sendViewModel.isElectrumWallet ? 490 : 465;
if (!ResponsiveLayoutUtil.instance.isMobile(context)) {
return initialHeight - 66;
}
return initialHeight;
}
@override
void onClose(BuildContext context) {
sendViewModel.onClose();
@@ -104,178 +117,137 @@ class SendPage extends BasePage {
key: _formKey,
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(bottom: 24),
content: Column(
children: <Widget>[
Container(
height: sendViewModel.isElectrumWallet ? 490 : 465,
child: Observer(
builder: (_) {
return PageView.builder(
scrollDirection: Axis.horizontal,
controller: controller,
itemCount: sendViewModel.outputs.length,
itemBuilder: (context, index) {
final output = sendViewModel.outputs[index];
return SendCard(
key: output.key,
output: output,
sendViewModel: sendViewModel,
initialPaymentRequest: initialPaymentRequest,
);
});
},
)),
Padding(
padding:
EdgeInsets.only(top: 10, left: 24, right: 24, bottom: 10),
child: Container(
height: 10,
child: Observer(
builder: (_) {
final count = sendViewModel.outputs.length;
return count > 1
? SmoothPageIndicator(
controller: controller,
count: count,
effect: ScrollingDotsEffect(
spacing: 6.0,
radius: 6.0,
dotWidth: 6.0,
dotHeight: 6.0,
dotColor: Theme.of(context)
.primaryTextTheme!
.headline3!
.backgroundColor!,
activeDotColor: Theme.of(context)
.primaryTextTheme!
.headline2!
.backgroundColor!),
)
: Offstage();
},
),
),
),
if (sendViewModel.hasMultiRecipient)
Container(
height: 40,
width: double.infinity,
padding: EdgeInsets.only(left: 24),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Observer(
builder: (_) {
final templates = sendViewModel.templates;
final itemCount = templates.length;
return Row(
children: <Widget>[
GestureDetector(
onTap: () => Navigator.of(context)
.pushNamed(Routes.sendTemplate),
child: Container(
padding: EdgeInsets.only(left: 1, right: 10),
child: DottedBorder(
borderType: BorderType.RRect,
dashPattern: [6, 4],
color: Theme.of(context)
.primaryTextTheme!
.headline2!
.decorationColor!,
strokeWidth: 2,
radius: Radius.circular(20),
child: Container(
height: 34,
padding: EdgeInsets.only(left: 10, right: 10),
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius:
BorderRadius.all(Radius.circular(20)),
color: Colors.transparent,
),
child: templates.length >= 1
? Icon(
Icons.add,
color: Theme.of(context)
.primaryTextTheme!
.headline2!
.color!,
)
: Text(
S.of(context).new_template,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Theme.of(context)
.primaryTextTheme!
.headline2!
.color!,
),
),
),
),
),
),
ListView.builder(
content: FocusTraversalGroup(
policy: OrderedTraversalPolicy(),
child: Column(
children: <Widget>[
Container(
height: _sendCardHeight(context),
child: Observer(
builder: (_) {
return PageView.builder(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: itemCount,
controller: controller,
itemCount: sendViewModel.outputs.length,
itemBuilder: (context, index) {
final template = templates[index];
return TemplateTile(
key: UniqueKey(),
to: template.name,
amount: template.isCurrencySelected ? template.amount : template.amountFiat,
from: template.isCurrencySelected ? template.cryptoCurrency : template.fiatCurrency,
onTap: () async {
final fiatFromTemplate = FiatCurrency.all.singleWhere((element) => element.title == template.fiatCurrency);
final output = _defineCurrentOutput();
output.address = template.address;
if(template.isCurrencySelected){
output.setCryptoAmount(template.amount);
}else{
sendViewModel.setFiatCurrency(fiatFromTemplate);
output.setFiatAmount(template.amountFiat);
}
output.resetParsedAddress();
await output.fetchParsedAddress(context);
},
onRemove: () {
showPopUp<void>(
context: context,
builder: (dialogContext) {
return AlertWithTwoActions(
alertTitle: S.of(context).template,
alertContent: S
.of(context)
.confirm_delete_template,
rightButtonText: S.of(context).delete,
leftButtonText: S.of(context).cancel,
actionRightButton: () {
Navigator.of(dialogContext).pop();
sendViewModel.sendTemplateViewModel
.removeTemplate(
template: template);
},
actionLeftButton: () =>
Navigator.of(dialogContext)
.pop());
},
);
},
final output = sendViewModel.outputs[index];
return SendCard(
key: output.key,
output: output,
sendViewModel: sendViewModel,
initialPaymentRequest: initialPaymentRequest,
);
},
),
],
);
},
});
},
)),
Padding(
padding:
EdgeInsets.only(top: 10, left: 24, right: 24, bottom: 10),
child: Container(
height: 10,
child: Observer(
builder: (_) {
final count = sendViewModel.outputs.length;
return count > 1
? SmoothPageIndicator(
controller: controller,
count: count,
effect: ScrollingDotsEffect(
spacing: 6.0,
radius: 6.0,
dotWidth: 6.0,
dotHeight: 6.0,
dotColor: Theme.of(context)
.primaryTextTheme.headline3!
.backgroundColor!,
activeDotColor: Theme.of(context)
.primaryTextTheme.headline2!
.backgroundColor!),
)
: Offstage();
},
),
),
),
)
],
if (sendViewModel.hasMultiRecipient)
Container(
height: 40,
width: double.infinity,
padding: EdgeInsets.only(left: 24),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Observer(
builder: (_) {
final templates = sendViewModel.templates;
final itemCount = templates.length;
return Row(
children: <Widget>[
AddTemplateButton(
onTap: () => Navigator.of(context).pushNamed(Routes.sendTemplate),
currentTemplatesLength: templates.length,
),
ListView.builder(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: itemCount,
itemBuilder: (context, index) {
final template = templates[index];
return TemplateTile(
key: UniqueKey(),
to: template.name,
amount: template.isCurrencySelected ? template.amount : template.amountFiat,
from: template.isCurrencySelected ? template.cryptoCurrency : template.fiatCurrency,
onTap: () async {
final fiatFromTemplate = FiatCurrency.all.singleWhere((element) => element.title == template.fiatCurrency);
final output = _defineCurrentOutput();
output.address = template.address;
if(template.isCurrencySelected){
output.setCryptoAmount(template.amount);
}else{
sendViewModel.setFiatCurrency(fiatFromTemplate);
output.setFiatAmount(template.amountFiat);
}
output.resetParsedAddress();
await output.fetchParsedAddress(context);
},
onRemove: () {
showPopUp<void>(
context: context,
builder: (dialogContext) {
return AlertWithTwoActions(
alertTitle: S.of(context).template,
alertContent: S
.of(context)
.confirm_delete_template,
rightButtonText: S.of(context).delete,
leftButtonText: S.of(context).cancel,
actionRightButton: () {
Navigator.of(dialogContext).pop();
sendViewModel.sendTemplateViewModel
.removeTemplate(
template: template);
},
actionLeftButton: () =>
Navigator.of(dialogContext)
.pop());
},
);
},
);
},
),
],
);
},
),
),
)
],
),
),
bottomSectionPadding:
EdgeInsets.only(left: 24, right: 24, bottom: 24),
@@ -290,8 +262,7 @@ class SendPage extends BasePage {
text: 'Change your asset (${sendViewModel.selectedCryptoCurrency})',
color: Colors.transparent,
textColor: Theme.of(context)
.accentTextTheme!
.headline3!
.accentTextTheme.headline3!
.decorationColor!,
)
)
@@ -309,13 +280,11 @@ class SendPage extends BasePage {
text: S.of(context).add_receiver,
color: Colors.transparent,
textColor: Theme.of(context)
.accentTextTheme!
.headline3!
.accentTextTheme.headline3!
.decorationColor!,
isDottedBorder: true,
borderColor: Theme.of(context)
.primaryTextTheme!
.headline3!
.primaryTextTheme.headline3!
.decorationColor!,
)),
Observer(
@@ -335,7 +304,7 @@ class SendPage extends BasePage {
item.address.isEmpty || item.cryptoAmount.isEmpty)
.toList();
if (notValidItems?.isNotEmpty ?? false) {
if (notValidItems.isNotEmpty ?? false) {
showErrorValidationAlert(context);
return;
}
@@ -344,7 +313,7 @@ class SendPage extends BasePage {
},
text: S.of(context).send,
color: Theme.of(context).accentTextTheme!.bodyText1!.color!,
color: Theme.of(context).accentTextTheme.bodyText1!.color!,
textColor: Colors.white,
isLoading: sendViewModel.state is IsExecutingState ||
sendViewModel.state is TransactionCommitting,
+9 -3
View File
@@ -1,6 +1,7 @@
import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
@@ -119,7 +120,7 @@ class SendCardState extends State<SendCard>
color: Colors.transparent,
)),
Container(
decoration: BoxDecoration(
decoration: ResponsiveLayoutUtil.instance.isMobile(context) ? BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24),
bottomRight: Radius.circular(24)),
@@ -130,9 +131,14 @@ class SendCardState extends State<SendCard>
.subtitle1!
.decorationColor!,
], begin: Alignment.topLeft, end: Alignment.bottomRight),
),
) : null,
child: Padding(
padding: EdgeInsets.fromLTRB(24, 100, 24, 32),
padding: EdgeInsets.fromLTRB(
24,
ResponsiveLayoutUtil.instance.isMobile(context) ? 100 : 55,
24,
ResponsiveLayoutUtil.instance.isMobile(context) ? 32 : 0,
),
child: SingleChildScrollView(
child: Observer(builder: (_) => Column(
mainAxisSize: MainAxisSize.min,
@@ -0,0 +1,105 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/widgets/setting_action_button.dart';
import 'package:cake_wallet/src/widgets/setting_actions.dart';
import 'package:cake_wallet/typography.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/router.dart' as Router;
final _settingsNavigatorKey = GlobalKey<NavigatorState>();
class DesktopSettingsPage extends StatefulWidget {
const DesktopSettingsPage({super.key});
@override
State<DesktopSettingsPage> createState() => _DesktopSettingsPageState();
}
class _DesktopSettingsPageState extends State<DesktopSettingsPage> {
final int itemCount = SettingActions.desktopSettings.length;
int? currentPage;
void _onItemChange(int index) {
setState(() {
currentPage = index;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(24),
child: Text(
S.current.settings,
style: textXLarge(),
),
),
Expanded(
child: Row(
children: [
Expanded(
flex: 1,
child: ListView.separated(
padding: EdgeInsets.only(top: 0),
itemBuilder: (_, index) {
final item = SettingActions.desktopSettings[index];
final isLastTile = index == itemCount - 1;
return SettingActionButton(
isLastTile: isLastTile,
selectionActive: currentPage != null,
isSelected: currentPage == index,
isArrowVisible: true,
onTap: () {
if (currentPage != index) {
final settingContext =
_settingsNavigatorKey.currentState?.context ?? context;
item.onTap.call(settingContext);
_onItemChange(index);
}
},
image: item.image,
title: item.name,
);
},
separatorBuilder: (_, index) => Container(
height: 1,
color: Theme.of(context).primaryTextTheme.caption!.decorationColor!,
),
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))!
];
},
),
),
)
],
),
),
],
),
),
);
}
}
@@ -7,9 +7,9 @@ import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.da
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/themes/theme_list.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/view_model/settings/choices_list_item.dart';
import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
@@ -65,14 +65,15 @@ class DisplaySettingsPage extends BasePage {
return LanguageService.list[code]?.toLowerCase().contains(searchText) ?? false;
},
),
SettingsChoicesCell(
ChoicesListItem<ThemeBase>(
title: S.current.color_theme,
items: ThemeList.all,
selectedItem: _displaySettingsViewModel.theme,
onItemSelected: (ThemeBase theme) => _displaySettingsViewModel.setTheme(theme),
if (DeviceInfo.instance.isMobile)
SettingsChoicesCell(
ChoicesListItem<ThemeBase>(
title: S.current.color_theme,
items: ThemeList.all,
selectedItem: _displaySettingsViewModel.theme,
onItemSelected: (ThemeBase theme) => _displaySettingsViewModel.setTheme(theme),
),
),
),
],
),
);
+27 -24
View File
@@ -1,6 +1,7 @@
import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_link_provider_cell.dart';
import 'package:cake_wallet/src/widgets/standard_list.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/view_model/settings/link_list_item.dart';
import 'package:cake_wallet/view_model/settings/regular_list_item.dart';
import 'package:cake_wallet/view_model/support_view_model.dart';
@@ -18,32 +19,34 @@ class SupportPage extends BasePage {
@override
Widget body(BuildContext context) {
final iconColor =
Theme.of(context).accentTextTheme!.headline1!.backgroundColor!;
final iconColor = Theme.of(context).accentTextTheme!.headline1!.backgroundColor!;
// FIX-ME: Added `context` it was not used here before, maby bug ?
return SectionStandardList(
context: context,
sectionCount: 1,
itemCounter: (int _) => supportViewModel.items.length,
itemBuilder: (_, __, index) {
final item = supportViewModel.items[index];
return Center(
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: 500),
child: SectionStandardList(
context: context,
sectionCount: 1,
itemCounter: (int _) => supportViewModel.items.length,
itemBuilder: (_, __, index) {
final item = supportViewModel.items[index];
if (item is RegularListItem) {
return SettingsCellWithArrow(
title: item.title, handler: item.handler);
}
if (item is RegularListItem) {
return SettingsCellWithArrow(title: item.title, handler: item.handler);
}
if (item is LinkListItem) {
return SettingsLinkProviderCell(
title: item.title,
icon: item.icon,
iconColor: item.hasIconColor ? iconColor : null,
link: item.link,
linkTitle: item.linkTitle);
}
if (item is LinkListItem) {
return SettingsLinkProviderCell(
title: item.title,
icon: item.icon,
iconColor: item.hasIconColor ? iconColor : null,
link: item.link,
linkTitle: item.linkTitle);
}
return Container();
});
return Container();
}),
),
);
}
}
}
@@ -1,5 +1,6 @@
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
@@ -23,8 +24,7 @@ class WalletListPage extends BasePage {
final WalletListViewModel walletListViewModel;
@override
Widget body(BuildContext context) =>
WalletListBody(walletListViewModel: walletListViewModel);
Widget body(BuildContext context) => WalletListBody(walletListViewModel: walletListViewModel);
}
class WalletListBody extends StatefulWidget {
@@ -37,28 +37,21 @@ class WalletListBody extends StatefulWidget {
}
class WalletListBodyState extends State<WalletListBody> {
final moneroIcon =
Image.asset('assets/images/monero_logo.png', height: 24, width: 24);
final bitcoinIcon =
Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
final litecoinIcon =
Image.asset('assets/images/litecoin_icon.png', height: 24, width: 24);
final nonWalletTypeIcon =
Image.asset('assets/images/close.png', height: 24, width: 24);
final havenIcon =
Image.asset('assets/images/haven_logo.png', height: 24, width: 24);
final moneroIcon = Image.asset('assets/images/monero_logo.png', height: 24, width: 24);
final bitcoinIcon = Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
final litecoinIcon = Image.asset('assets/images/litecoin_icon.png', height: 24, width: 24);
final nonWalletTypeIcon = Image.asset('assets/images/close.png', height: 24, width: 24);
final havenIcon = Image.asset('assets/images/haven_logo.png', height: 24, width: 24);
final scrollController = ScrollController();
final double tileHeight = 60;
Flushbar<void>? _progressBar;
@override
Widget build(BuildContext context) {
final newWalletImage = Image.asset('assets/images/new_wallet.png',
height: 12, width: 12, color: Colors.white);
final newWalletImage =
Image.asset('assets/images/new_wallet.png', height: 12, width: 12, color: Colors.white);
final restoreWalletImage = Image.asset('assets/images/restore_wallet.png',
height: 12,
width: 12,
color: Theme.of(context).primaryTextTheme!.headline6!.color!);
height: 12, width: 12, color: Theme.of(context).primaryTextTheme.headline6!.color!);
return Container(
padding: EdgeInsets.only(top: 16),
@@ -69,16 +62,13 @@ class WalletListBodyState extends State<WalletListBody> {
builder: (_) => ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
separatorBuilder: (_, index) => Divider(
color: Theme.of(context).backgroundColor, height: 32),
separatorBuilder: (_, index) =>
Divider(color: Theme.of(context).backgroundColor, height: 32),
itemCount: widget.walletListViewModel.wallets.length,
itemBuilder: (__, index) {
final wallet = widget.walletListViewModel.wallets[index];
final currentColor = wallet.isCurrent
? Theme.of(context)
.accentTextTheme!
.subtitle2!
.decorationColor!
? Theme.of(context).accentTextTheme.subtitle2!.decorationColor!
: Theme.of(context).backgroundColor;
final row = GestureDetector(
onTap: () async {
@@ -90,19 +80,15 @@ class WalletListBodyState extends State<WalletListBody> {
context: context,
builder: (dialogContext) {
return AlertWithTwoActions(
alertTitle: S
.of(context)
.change_wallet_alert_title,
alertContent: S
.of(context)
.change_wallet_alert_content(
wallet.name),
alertTitle: S.of(context).change_wallet_alert_title,
alertContent:
S.of(context).change_wallet_alert_content(wallet.name),
leftButtonText: S.of(context).cancel,
rightButtonText: S.of(context).change,
actionLeftButton: () =>
Navigator.of(context).pop(false),
Navigator.of(dialogContext).pop(false),
actionRightButton: () =>
Navigator.of(context).pop(true));
Navigator.of(dialogContext).pop(true));
}) ??
false;
@@ -131,12 +117,11 @@ class WalletListBodyState extends State<WalletListBody> {
color: Theme.of(context).backgroundColor,
alignment: Alignment.centerLeft,
child: Row(
crossAxisAlignment:
CrossAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
wallet.isEnabled
? _imageFor(type: wallet.type)
: nonWalletTypeIcon,
? _imageFor(type: wallet.type)
: nonWalletTypeIcon,
SizedBox(width: 10),
Text(
wallet.name,
@@ -144,8 +129,7 @@ class WalletListBodyState extends State<WalletListBody> {
fontSize: 22,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.primaryTextTheme!
.headline6!
.primaryTextTheme.headline6!
.color!),
)
],
@@ -163,43 +147,40 @@ class WalletListBodyState extends State<WalletListBody> {
startActionPane: _actionPane(wallet),
endActionPane: _actionPane(wallet),
child: row,
);
);
}),
),
),
bottomSectionPadding:
EdgeInsets.only(bottom: 24, right: 24, left: 24),
bottomSectionPadding: EdgeInsets.only(bottom: 24, right: 24, left: 24),
bottomSection: Column(children: <Widget>[
PrimaryImageButton(
onPressed: () {
if (isSingleCoin) {
Navigator.of(context).pushNamed(Routes.newWallet, arguments: widget.walletListViewModel.currentWalletType);
} else {
Navigator.of(context).pushNamed(Routes.newWalletType);
}
},
if (isSingleCoin) {
Navigator.of(context).pushNamed(Routes.newWallet,
arguments: widget.walletListViewModel.currentWalletType);
} else {
Navigator.of(context).pushNamed(Routes.newWalletType);
}
},
image: newWalletImage,
text: S.of(context).wallet_list_create_new_wallet,
color: Theme.of(context).accentTextTheme!.bodyText1!.color!,
color: Theme.of(context).accentTextTheme.bodyText1!.color!,
textColor: Colors.white,
),
SizedBox(height: 10.0),
PrimaryImageButton(
onPressed: () {
if (isSingleCoin) {
Navigator
.of(context)
.pushNamed(
Routes.restoreWallet,
arguments: widget.walletListViewModel.currentWalletType);
} else {
Navigator.of(context).pushNamed(Routes.restoreWalletType);
}
},
if (isSingleCoin) {
Navigator.of(context).pushNamed(Routes.restoreWallet,
arguments: widget.walletListViewModel.currentWalletType);
} else {
Navigator.of(context).pushNamed(Routes.restoreWalletType);
}
},
image: restoreWalletImage,
text: S.of(context).wallet_list_restore_wallet,
color: Theme.of(context).accentTextTheme!.caption!.color!,
textColor: Theme.of(context).primaryTextTheme!.headline6!.color!)
color: Theme.of(context).accentTextTheme.caption!.color!,
textColor: Theme.of(context).primaryTextTheme.headline6!.color!)
])),
);
}
@@ -232,9 +213,13 @@ class WalletListBodyState extends State<WalletListBody> {
await widget.walletListViewModel.loadWallet(wallet);
auth.hideProgressText();
auth.close();
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.of(context).pop();
});
// only pop the wallets route in mobile as it will go back to dashboard page
// in desktop platforms the navigation tree is different
if (DeviceInfo.instance.isMobile) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.of(context).pop();
});
}
} catch (e) {
auth.changeProcessText(
S.of(context).wallet_list_failed_to_load(wallet.name, e.toString()));
@@ -245,7 +230,11 @@ class WalletListBodyState extends State<WalletListBody> {
changeProcessText(S.of(context).wallet_list_loading_wallet(wallet.name));
await widget.walletListViewModel.loadWallet(wallet);
hideProgressText();
Navigator.of(context).pop();
// only pop the wallets route in mobile as it will go back to dashboard page
// in desktop platforms the navigation tree is different
if (DeviceInfo.instance.isMobile) {
Navigator.of(context).pop();
}
} catch (e) {
changeProcessText(S.of(context).wallet_list_failed_to_load(wallet.name, e.toString()));
}
@@ -290,6 +279,7 @@ class WalletListBodyState extends State<WalletListBody> {
? auth.changeProcessText(S.of(context).wallet_list_removing_wallet(wallet.name))
: changeProcessText(S.of(context).wallet_list_removing_wallet(wallet.name));
await widget.walletListViewModel.remove(wallet);
hideProgressText();
} catch (e) {
auth != null
? auth.changeProcessText(
@@ -309,8 +299,10 @@ class WalletListBodyState extends State<WalletListBody> {
}
void hideProgressText() {
_progressBar?.dismiss();
_progressBar = null;
Future.delayed(Duration(milliseconds: 50), () {
_progressBar?.dismiss();
_progressBar = null;
});
}
ActionPane _actionPane(WalletListItem wallet) => ActionPane(
+111 -145
View File
@@ -1,4 +1,5 @@
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
@@ -19,7 +20,7 @@ class WelcomePage extends BasePage {
if (isHaven) {
return S.of(context).haven_app;
}
return S.of(context).cake_wallet;
}
@@ -31,172 +32,137 @@ class WelcomePage extends BasePage {
if (isHaven) {
return S.of(context).haven_app_wallet_text;
}
return S.of(context).first_wallet_text;
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme
.of(context)
.backgroundColor,
backgroundColor: Theme.of(context).backgroundColor,
resizeToAvoidBottomInset: false,
body: body(context));
}
@override
Widget body(BuildContext context) {
final welcomeImage = currentTheme.type == ThemeType.dark
? welcomeImageDark : welcomeImageLight;
final welcomeImage = currentTheme.type == ThemeType.dark ? welcomeImageDark : welcomeImageLight;
final newWalletImage = Image.asset('assets/images/new_wallet.png',
height: 12,
width: 12,
color: Theme
.of(context)
.accentTextTheme!
.headline5!
.decorationColor!);
color: Theme.of(context).accentTextTheme!.headline5!.decorationColor!);
final restoreWalletImage = Image.asset('assets/images/restore_wallet.png',
height: 12,
width: 12,
color: Theme.of(context)
.primaryTextTheme!
.headline6!
.color!);
height: 12, width: 12, color: Theme.of(context).primaryTextTheme!.headline6!.color!);
return WillPopScope(onWillPop: () async => false, child: Container(
padding: EdgeInsets.only(top: 64, bottom: 24, left: 24, right: 24),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Flexible(
flex: 2,
child: AspectRatio(
aspectRatio: aspectRatioImage,
child: FittedBox(child: welcomeImage, fit: BoxFit.fill)
)
),
Flexible(
flex: 3,
return WillPopScope(
onWillPop: () async => false,
child: Container(
padding: EdgeInsets.only(top: 64, bottom: 24, left: 24, right: 24),
child: Center(
child: ConstrainedBox(
constraints:
BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 24),
child: Text(
S
.of(context)
.welcome,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
color: Theme
.of(context)
.accentTextTheme!
.headline2!
.color!,
Flexible(
flex: 2,
child: AspectRatio(
aspectRatio: aspectRatioImage,
child: FittedBox(child: welcomeImage, fit: BoxFit.fill))),
Flexible(
flex: 3,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 24),
child: Text(
S.of(context).welcome,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
color: Theme.of(context).accentTextTheme!.headline2!.color!,
),
textAlign: TextAlign.center,
),
),
Padding(
padding: EdgeInsets.only(top: 5),
child: Text(
appTitle(context),
style: TextStyle(
fontSize: 36,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme!.headline6!.color!,
),
textAlign: TextAlign.center,
),
),
Padding(
padding: EdgeInsets.only(top: 5),
child: Text(
appDescription(context),
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context).accentTextTheme!.headline2!.color!,
),
textAlign: TextAlign.center,
),
),
],
),
textAlign: TextAlign.center,
),
),
Padding(
padding: EdgeInsets.only(top: 5),
child: Text(
appTitle(context),
style: TextStyle(
fontSize: 36,
fontWeight: FontWeight.bold,
color: Theme.of(context)
.primaryTextTheme!
.headline6!
.color!,
),
textAlign: TextAlign.center,
),
),
Padding(
padding: EdgeInsets.only(top: 5),
child: Text(
appDescription(context),
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme
.of(context)
.accentTextTheme!
.headline2!
.color!,
),
textAlign: TextAlign.center,
),
),
],
),
Column(
children: <Widget>[
Text(
S
.of(context)
.please_make_selection,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.normal,
color: Theme.of(context)
.accentTextTheme!
.headline2!
.color!,
),
textAlign: TextAlign.center,
),
Padding(
padding: EdgeInsets.only(top: 24),
child: PrimaryImageButton(
onPressed: () =>
Navigator.pushNamed(context,
Routes.newWalletFromWelcome),
image: newWalletImage,
text: S.of(context).create_new,
color: Theme.of(context)
.accentTextTheme!
.subtitle2!
.decorationColor!,
textColor: Theme
.of(context)
.accentTextTheme!
.headline5!
.decorationColor!,
),
),
Padding(
padding: EdgeInsets.only(top: 10),
child: PrimaryImageButton(
onPressed: () {
Navigator.pushNamed(context, Routes.restoreOptions);
},
image: restoreWalletImage,
text: S
.of(context)
.restore_wallet,
color: Theme.of(context)
.accentTextTheme!
.caption!
.color!,
textColor: Theme.of(context)
.primaryTextTheme!
.headline6!
.color!),
)
],
)
Column(
children: <Widget>[
Text(
S.of(context).please_make_selection,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.normal,
color: Theme.of(context).accentTextTheme!.headline2!.color!,
),
textAlign: TextAlign.center,
),
Padding(
padding: EdgeInsets.only(top: 24),
child: PrimaryImageButton(
onPressed: () =>
Navigator.pushNamed(context, Routes.newWalletFromWelcome),
image: newWalletImage,
text: S.of(context).create_new,
color: Theme.of(context)
.accentTextTheme!
.subtitle2!
.decorationColor!,
textColor: Theme.of(context)
.accentTextTheme!
.headline5!
.decorationColor!,
),
),
Padding(
padding: EdgeInsets.only(top: 10),
child: PrimaryImageButton(
onPressed: () {
Navigator.pushNamed(context, Routes.restoreOptions);
},
image: restoreWalletImage,
text: S.of(context).restore_wallet,
color: Theme.of(context).accentTextTheme!.caption!.color!,
textColor:
Theme.of(context).primaryTextTheme!.headline6!.color!),
)
],
)
],
))
],
)
)
],
)
));
),
),
)));
}
}
+52
View File
@@ -0,0 +1,52 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:dotted_border/dotted_border.dart';
import 'package:flutter/material.dart';
class AddTemplateButton extends StatelessWidget {
final Function() onTap;
final int currentTemplatesLength;
const AddTemplateButton({Key? key, required this.onTap, required this.currentTemplatesLength})
: super(key: key);
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
padding: EdgeInsets.only(left: 1, right: 10),
child: DottedBorder(
borderType: BorderType.RRect,
dashPattern: [6, 4],
color: Theme.of(context).primaryTextTheme.headline3!.decorationColor!,
strokeWidth: 2,
radius: Radius.circular(20),
child: Container(
height: 34,
padding: EdgeInsets.symmetric(
horizontal: ResponsiveLayoutUtil.instance.isMobile(context) ? 10 : 30),
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20)),
color: Colors.transparent,
),
child: currentTemplatesLength >= 1
? Icon(
Icons.add,
color: Theme.of(context).primaryTextTheme.headline2!.color!,
)
: Text(
S.of(context).new_template,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.headline2!.color!,
),
),
),
),
),
);
}
}
+16 -17
View File
@@ -1,3 +1,6 @@
import 'dart:io';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart';
@@ -65,7 +68,7 @@ class AddressTextField extends StatelessWidget {
style: textStyle ??
TextStyle(
fontSize: 16,
color: Theme.of(context).primaryTextTheme!.headline6!.color!),
color: Theme.of(context).primaryTextTheme.headline6!.color!),
decoration: InputDecoration(
suffixIcon: SizedBox(
width: prefixIconWidth * options.length +
@@ -102,7 +105,8 @@ class AddressTextField extends StatelessWidget {
width: prefixIconWidth * options.length +
(spaceBetweenPrefixIcons * options.length),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment: DeviceInfo.instance.isMobile
? MainAxisAlignment.spaceBetween : MainAxisAlignment.end,
children: [
SizedBox(width: 5),
if (this.options.contains(AddressTextFieldOption.paste)) ...[
@@ -117,8 +121,7 @@ class AddressTextField extends StatelessWidget {
decoration: BoxDecoration(
color: buttonColor ??
Theme.of(context)
.accentTextTheme!
.headline6!
.accentTextTheme.headline6!
.color!,
borderRadius:
BorderRadius.all(Radius.circular(6))),
@@ -126,13 +129,13 @@ class AddressTextField extends StatelessWidget {
'assets/images/paste_ios.png',
color: iconColor ??
Theme.of(context)
.primaryTextTheme!
.headline4!
.primaryTextTheme.headline4!
.decorationColor!,
)),
)),
],
if (this.options.contains(AddressTextFieldOption.qrCode)) ...[
if (this.options.contains(AddressTextFieldOption.qrCode) && DeviceInfo.instance.isMobile)
...[
Container(
width: prefixIconWidth,
height: prefixIconHeight,
@@ -144,8 +147,7 @@ class AddressTextField extends StatelessWidget {
decoration: BoxDecoration(
color: buttonColor ??
Theme.of(context)
.accentTextTheme!
.headline6!
.accentTextTheme.headline6!
.color!,
borderRadius:
BorderRadius.all(Radius.circular(6))),
@@ -153,12 +155,11 @@ class AddressTextField extends StatelessWidget {
'assets/images/qr_code_icon.png',
color: iconColor ??
Theme.of(context)
.primaryTextTheme!
.headline4!
.primaryTextTheme.headline4!
.decorationColor!,
)),
))
],
] else SizedBox(width: 5),
if (this
.options
.contains(AddressTextFieldOption.addressBook)) ...[
@@ -173,8 +174,7 @@ class AddressTextField extends StatelessWidget {
decoration: BoxDecoration(
color: buttonColor ??
Theme.of(context)
.accentTextTheme!
.headline6!
.accentTextTheme.headline6!
.color!,
borderRadius:
BorderRadius.all(Radius.circular(6))),
@@ -182,8 +182,7 @@ class AddressTextField extends StatelessWidget {
'assets/images/open_book.png',
color: iconColor ??
Theme.of(context)
.primaryTextTheme!
.headline4!
.primaryTextTheme.headline4!
.decorationColor!,
)),
))
@@ -211,7 +210,7 @@ class AddressTextField extends StatelessWidget {
}
Future<void> _presetAddressBookPicker(BuildContext context) async {
final contact = await Navigator.of(context, rootNavigator: true)
final contact = await Navigator.of(context)
.pushNamed(Routes.pickerAddressBook,arguments: selectedCurrency);
if (contact is ContactBase && contact.address != null) {
+8 -3
View File
@@ -1,5 +1,5 @@
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart';
@@ -21,10 +21,15 @@ class AlertBackground extends StatelessWidget {
filter: ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0),
child: Container(
decoration: BoxDecoration(color: PaletteDark.darkNightBlue.withOpacity(0.75)),
child: child,
child: Center(
child: Container(
width: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint,
child: child,
),
),
),
),
),
);
}
}
}
+1 -4
View File
@@ -13,9 +13,7 @@ class AlertCloseButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Positioned(
bottom: 60,
child: GestureDetector(
return GestureDetector(
onTap: () => Navigator.of(context).pop(),
child: Container(
height: 42,
@@ -28,7 +26,6 @@ class AlertCloseButton extends StatelessWidget {
child: image ?? closeButton,
),
),
)
);
}
}
+7 -4
View File
@@ -27,6 +27,7 @@ class BaseTextFormField extends StatelessWidget {
this.maxLength,
this.focusNode,
this.initialValue,
this.onSubmit,
this.borderWidth = 1.0});
final TextEditingController? controller;
@@ -54,6 +55,7 @@ class BaseTextFormField extends StatelessWidget {
final bool? enableInteractiveSelection;
final String? initialValue;
final double borderWidth;
final void Function(String)? onSubmit;
@override
Widget build(BuildContext context) {
@@ -71,11 +73,12 @@ class BaseTextFormField extends StatelessWidget {
inputFormatters: inputFormatters,
enabled: enabled,
maxLength: maxLength,
onFieldSubmitted: onSubmit,
style: textStyle ??
TextStyle(
fontSize: 16.0,
color:
textColor ?? Theme.of(context).primaryTextTheme!.headline6!.color!),
textColor ?? Theme.of(context).primaryTextTheme.headline6!.color!),
decoration: InputDecoration(
prefix: prefix,
prefixIcon: prefixIcon,
@@ -89,17 +92,17 @@ class BaseTextFormField extends StatelessWidget {
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: borderColor ??
Theme.of(context).primaryTextTheme!.headline6!.backgroundColor!,
Theme.of(context).primaryTextTheme.headline6!.backgroundColor!,
width: borderWidth)),
disabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: borderColor ??
Theme.of(context).primaryTextTheme!.headline6!.backgroundColor!,
Theme.of(context).primaryTextTheme.headline6!.backgroundColor!,
width: borderWidth)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: borderColor ??
Theme.of(context).primaryTextTheme!.headline6!.backgroundColor!,
Theme.of(context).primaryTextTheme.headline6!.backgroundColor!,
width: borderWidth))),
validator: validator,
);
+50 -50
View File
@@ -1,4 +1,5 @@
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/src/widgets/alert_background.dart';
import 'package:cake_wallet/src/widgets/alert_close_button.dart';
@@ -32,63 +33,62 @@ class CheckBoxPickerState extends State<CheckBoxPicker> {
@override
Widget build(BuildContext context) {
return AlertBackground(
child: Stack(
alignment: Alignment.center,
children: <Widget>[
Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
if (widget.title.isNotEmpty)
Container(
padding: EdgeInsets.symmetric(horizontal: 24),
child: Text(
widget.title,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
fontFamily: 'Lato',
fontWeight: FontWeight.bold,
decoration: TextDecoration.none,
color: Colors.white,
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
if (widget.title.isNotEmpty)
Container(
padding: EdgeInsets.symmetric(horizontal: 24),
child: Text(
widget.title,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
fontFamily: 'Lato',
fontWeight: FontWeight.bold,
decoration: TextDecoration.none,
color: Colors.white,
),
),
),
),
Padding(
padding: EdgeInsets.only(left: 24, right: 24, top: 24),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(30)),
child: Container(
color: Theme.of(context).accentTextTheme.headline6!.color!,
child: ConstrainedBox(
constraints: BoxConstraints(
maxHeight: MediaQuery.of(context).size.height * 0.65,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Flexible(
child: Stack(
alignment: Alignment.center,
children: <Widget>[
(items.length) > 3
? Scrollbar(
controller: controller,
child: itemsList(),
)
: itemsList(),
],
Padding(
padding: EdgeInsets.only(left: 24, right: 24, top: 24),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(30)),
child: Container(
color: Theme.of(context).accentTextTheme.headline6!.color!,
child: ConstrainedBox(
constraints: BoxConstraints(
maxHeight: MediaQuery.of(context).size.height * 0.65,
maxWidth: ResponsiveLayoutUtil.kPopupWidth,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Flexible(
child: Stack(
alignment: Alignment.center,
children: <Widget>[
items.length > 3
? Scrollbar(
controller: controller,
child: itemsList(),
)
: itemsList(),
],
),
),
),
],
],
),
),
),
),
),
)
],
),
AlertCloseButton(),
],
SizedBox(height: ResponsiveLayoutUtil.kPopupSpaceHeight),
AlertCloseButton(),
],
),
),
);
}
+3
View File
@@ -17,6 +17,9 @@ class MarketPlaceItem extends StatelessWidget {
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
hoverColor: Colors.transparent,
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
child: Stack(
children: [
Container(
+16 -21
View File
@@ -1,13 +1,7 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class NavBar extends StatelessWidget implements ObstructingPreferredSizeWidget {
factory NavBar(
{Widget? leading,
Widget? middle,
Widget? trailing,
Color? backgroundColor}) {
factory NavBar({Widget? leading, Widget? middle, Widget? trailing, Color? backgroundColor}) {
return NavBar._internal(
leading: leading,
middle: middle,
@@ -17,11 +11,7 @@ class NavBar extends StatelessWidget implements ObstructingPreferredSizeWidget {
}
factory NavBar.withShadow(
{Widget? leading,
Widget? middle,
Widget? trailing,
Color? backgroundColor}) {
{Widget? leading, Widget? middle, Widget? trailing, Color? backgroundColor}) {
return NavBar._internal(
leading: leading,
middle: middle,
@@ -29,13 +19,15 @@ class NavBar extends StatelessWidget implements ObstructingPreferredSizeWidget {
height: 80,
backgroundColor: backgroundColor,
decoration: BoxDecoration(
color: backgroundColor,
boxShadow: [
BoxShadow(
color: Color.fromRGBO(132, 141, 198, 0.11),
blurRadius: 8,
offset: Offset(0, 2))
]),
color: backgroundColor,
boxShadow: [
BoxShadow(
color: Color.fromRGBO(132, 141, 198, 0.11),
blurRadius: 8,
offset: Offset(0, 2),
),
],
),
);
}
@@ -59,14 +51,17 @@ class NavBar extends StatelessWidget implements ObstructingPreferredSizeWidget {
@override
Widget build(BuildContext context) {
if (leading == null && middle == null && trailing == null) {
return const SizedBox();
}
final pad = height - _originalHeight;
final paddingTop = pad / 2;
final _paddingBottom = (pad / 2);
return Container(
decoration: decoration ?? BoxDecoration(color: backgroundColor),
padding:
EdgeInsetsDirectional.only(bottom: _paddingBottom, top: paddingTop),
padding: EdgeInsetsDirectional.only(bottom: _paddingBottom, top: paddingTop),
child: CupertinoNavigationBar(
leading: leading,
automaticallyImplyLeading: false,
+91 -92
View File
@@ -1,5 +1,6 @@
// ignore_for_file: deprecated_member_use
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/src/widgets/alert_background.dart';
import 'package:cake_wallet/src/widgets/alert_close_button.dart';
@@ -70,108 +71,106 @@ class _PickerState<Item> extends State<Picker<Item>> {
@override
Widget build(BuildContext context) {
return AlertBackground(
child: Stack(
alignment: Alignment.center,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
if (widget.title?.isNotEmpty ?? false)
Container(
padding: EdgeInsets.symmetric(horizontal: 24),
child: Text(
widget.title!,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
fontFamily: 'Lato',
fontWeight: FontWeight.bold,
decoration: TextDecoration.none,
color: Colors.white,
),
),
if (widget.title?.isNotEmpty ?? false)
Container(
padding: EdgeInsets.symmetric(horizontal: 24),
child: Text(
widget.title!,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
fontFamily: 'Lato',
fontWeight: FontWeight.bold,
decoration: TextDecoration.none,
color: Colors.white,
),
Padding(
padding: EdgeInsets.only(left: 24, right: 24, top: 24),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(30)),
child: Container(
color: Theme.of(context).accentTextTheme.headline6!.color!,
child: ConstrainedBox(
constraints: BoxConstraints(
maxHeight: MediaQuery.of(context).size.height * 0.65,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (widget.hintText != null)
Padding(
padding: const EdgeInsets.all(16),
child: TextFormField(
controller: searchController,
style: TextStyle(color: Theme.of(context).primaryTextTheme.headline6!.color!),
decoration: InputDecoration(
hintText: widget.hintText,
prefixIcon: Image.asset("assets/images/search_icon.png"),
filled: true,
fillColor: Theme.of(context).accentTextTheme.headline3!.color!,
alignLabelWithHint: false,
contentPadding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide: const BorderSide(
color: Colors.transparent,
)),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide: const BorderSide(
color: Colors.transparent,
)),
),
),
),
Padding(
padding: EdgeInsets.only(left: 24, right: 24, top: 24),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(30)),
child: Container(
color: Theme.of(context).accentTextTheme.headline6!.color!,
child: ConstrainedBox(
constraints: BoxConstraints(
maxHeight: MediaQuery.of(context).size.height * 0.65,
maxWidth: ResponsiveLayoutUtil.kPopupWidth,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (widget.hintText != null)
Padding(
padding: const EdgeInsets.all(16),
child: TextFormField(
controller: searchController,
style: TextStyle(color: Theme.of(context).primaryTextTheme.headline6!.color!),
decoration: InputDecoration(
hintText: widget.hintText,
prefixIcon: Image.asset("assets/images/search_icon.png"),
filled: true,
fillColor: Theme.of(context).accentTextTheme.headline3!.color!,
alignLabelWithHint: false,
contentPadding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide: const BorderSide(
color: Colors.transparent,
)),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide: const BorderSide(
color: Colors.transparent,
)),
),
),
Divider(
color: Theme.of(context).accentTextTheme.headline6!.backgroundColor!,
height: 1,
),
if (widget.selectedAtIndex != -1) buildSelectedItem(),
Flexible(
child: Stack(
alignment: Alignment.center,
children: <Widget>[
items.length > 3 ? Scrollbar(
controller: controller,
child: itemsList(),
) : itemsList(),
(widget.description?.isNotEmpty ?? false)
? Positioned(
bottom: 24,
left: 24,
right: 24,
child: Text(
widget.description!,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
fontFamily: 'Lato',
decoration: TextDecoration.none,
color: Theme.of(context).primaryTextTheme.headline6!.color!,
),
Divider(
color: Theme.of(context).accentTextTheme.headline6!.backgroundColor!,
height: 1,
),
if (widget.selectedAtIndex != -1) buildSelectedItem(),
Flexible(
child: Stack(
alignment: Alignment.center,
children: <Widget>[
items.length > 3 ? Scrollbar(
controller: controller,
child: itemsList(),
) : itemsList(),
(widget.description?.isNotEmpty ?? false)
? Positioned(
bottom: 24,
left: 24,
right: 24,
child: Text(
widget.description!,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
fontFamily: 'Lato',
decoration: TextDecoration.none,
color: Theme.of(context).primaryTextTheme.headline6!.color!,
),
)
: Offstage(),
],
),
),
)
: Offstage(),
],
),
],
),
),
),
],
),
),
)
],
),
),
),
SizedBox(height: ResponsiveLayoutUtil.kPopupSpaceHeight),
AlertCloseButton(),
],
),
+121 -108
View File
@@ -1,3 +1,4 @@
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:dotted_border/dotted_border.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
@@ -24,28 +25,31 @@ class PrimaryButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
final content = SizedBox(
width: double.infinity,
height: 52.0,
child: TextButton(
onPressed: isDisabled
? (onDisabledPressed != null ? onDisabledPressed : null) : onPressed,
style: ButtonStyle(backgroundColor: MaterialStateProperty.all(isDisabled ? color.withOpacity(0.5) : color),
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(26.0),
final content = ConstrainedBox(
constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint),
child: SizedBox(
width: double.infinity,
height: 52.0,
child: TextButton(
onPressed: isDisabled
? (onDisabledPressed != null ? onDisabledPressed : null) : onPressed,
style: ButtonStyle(backgroundColor: MaterialStateProperty.all(isDisabled ? color.withOpacity(0.5) : color),
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(26.0),
),
),
),
overlayColor: MaterialStateProperty.all(Colors.transparent)),
child: Text(text,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15.0,
fontWeight: FontWeight.w600,
color: isDisabled
? textColor.withOpacity(0.5)
: textColor)),
));
overlayColor: MaterialStateProperty.all(Colors.transparent)),
child: Text(text,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15.0,
fontWeight: FontWeight.w600,
color: isDisabled
? textColor.withOpacity(0.5)
: textColor)),
)),
);
return isDottedBorder
? DottedBorder(
@@ -77,29 +81,32 @@ class LoadingPrimaryButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SizedBox(
width: double.infinity,
height: 52.0,
child: TextButton(
onPressed: (isLoading || isDisabled) ? null : onPressed,
style: ButtonStyle(backgroundColor: MaterialStateProperty.all(isDisabled ? color.withOpacity(0.5) : color),
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(26.0),
),
)),
child: isLoading
? CupertinoActivityIndicator(animating: true)
: Text(text,
style: TextStyle(
fontSize: 15.0,
fontWeight: FontWeight.w600,
color: isDisabled
? textColor.withOpacity(0.5)
: textColor
return ConstrainedBox(
constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint),
child: SizedBox(
width: double.infinity,
height: 52.0,
child: TextButton(
onPressed: (isLoading || isDisabled) ? null : onPressed,
style: ButtonStyle(backgroundColor: MaterialStateProperty.all(isDisabled ? color.withOpacity(0.5) : color),
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(26.0),
),
)),
));
child: isLoading
? CupertinoActivityIndicator(animating: true)
: Text(text,
style: TextStyle(
fontSize: 15.0,
fontWeight: FontWeight.w600,
color: isDisabled
? textColor.withOpacity(0.5)
: textColor
)),
)),
);
}
}
@@ -130,45 +137,48 @@ class PrimaryIconButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SizedBox(
width: double.infinity,
height: 52.0,
child: TextButton(
onPressed: onPressed,
style: ButtonStyle(backgroundColor: MaterialStateProperty.all(color),
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radius),
),
)),
child: Stack(
children: <Widget>[
Row(
mainAxisAlignment: mainAxisAlignment,
children: <Widget>[
Container(
width: 26.0,
height: 52.0,
decoration: BoxDecoration(
shape: BoxShape.circle, color: iconBackgroundColor),
child: Center(
child: Icon(iconData, color: iconColor, size: 22.0)
),
return ConstrainedBox(
constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint),
child: SizedBox(
width: double.infinity,
height: 52.0,
child: TextButton(
onPressed: onPressed,
style: ButtonStyle(backgroundColor: MaterialStateProperty.all(color),
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radius),
),
],
),
Container(
height: 52.0,
child: Center(
child: Text(text,
style: TextStyle(
fontSize: 16.0,
color: textColor)),
)),
child: Stack(
children: <Widget>[
Row(
mainAxisAlignment: mainAxisAlignment,
children: <Widget>[
Container(
width: 26.0,
height: 52.0,
decoration: BoxDecoration(
shape: BoxShape.circle, color: iconBackgroundColor),
child: Center(
child: Icon(iconData, color: iconColor, size: 22.0)
),
),
],
),
)
],
),
));
Container(
height: 52.0,
child: Center(
child: Text(text,
style: TextStyle(
fontSize: 16.0,
color: textColor)),
),
)
],
),
)),
);
}
}
@@ -190,34 +200,37 @@ class PrimaryImageButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SizedBox(
width: double.infinity,
height: 52.0,
child: TextButton(
onPressed: onPressed,
style: ButtonStyle(backgroundColor: MaterialStateProperty.all(color),
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(26.0),
),
)),
child:Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
image,
SizedBox(width: 15),
Text(
text,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: textColor
return ConstrainedBox(
constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint),
child: SizedBox(
width: double.infinity,
height: 52.0,
child: TextButton(
onPressed: onPressed,
style: ButtonStyle(backgroundColor: MaterialStateProperty.all(color),
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(26.0),
),
)
],
),
)
));
)),
child:Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
image,
SizedBox(width: 15),
Text(
text,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: textColor
),
)
],
),
)
)),
);
}
}
@@ -0,0 +1,81 @@
import 'package:cake_wallet/palette.dart';
import 'package:flutter/material.dart';
class SettingActionButton extends StatelessWidget {
final bool isLastTile;
final bool isSelected;
final bool isArrowVisible;
final bool selectionActive;
final VoidCallback onTap;
final String image;
final String title;
final double fromBottomEdge;
final double fromTopEdge;
final double tileHeight;
const SettingActionButton({
super.key,
this.isLastTile = false,
this.isSelected = false,
this.selectionActive = true,
this.isArrowVisible = false,
required this.onTap,
required this.image,
required this.title,
this.tileHeight = 60,
this.fromTopEdge = 50,
this.fromBottomEdge = 25,
});
@override
Widget build(BuildContext context) {
Color? color = isSelected
? Theme.of(context).textTheme.headline3!.color
: selectionActive
? Palette.darkBlue
: Theme.of(context).textTheme.headline3!.color;
return InkWell(
onTap: onTap,
hoverColor: Colors.transparent,
child: Container(
height: tileHeight,
padding: isLastTile
? EdgeInsets.only(
left: 24,
right: 24,
top: fromBottomEdge,
)
: EdgeInsets.only(left: 24, right: 24),
alignment: isLastTile ? Alignment.topLeft : null,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Image.asset(
image,
height: 16,
width: 16,
color: Palette.darkBlue,
),
SizedBox(width: 16),
Expanded(
child: Text(
title,
style: TextStyle(
color: color,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
if (isArrowVisible)
Icon(
Icons.arrow_forward_ios,
color: color,
size: 16,
)
],
),
),
);
}
}
+109
View File
@@ -0,0 +1,109 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:flutter/material.dart';
class SettingActions {
final String name;
final String image;
final void Function(BuildContext) onTap;
SettingActions._({
required this.name,
required this.image,
required this.onTap,
});
static List<SettingActions> all = [
connectionSettingAction,
walletSettingAction,
addressBookSettingAction,
securityBackupSettingAction,
privacySettingAction,
displaySettingAction,
otherSettingAction,
supportSettingAction,
];
static List<SettingActions> desktopSettings = [
connectionSettingAction,
walletSettingAction,
addressBookSettingAction,
securityBackupSettingAction,
privacySettingAction,
displaySettingAction,
otherSettingAction,
supportSettingAction,
];
static SettingActions connectionSettingAction = SettingActions._(
name: S.current.connection_sync,
image: 'assets/images/nodes_menu.png',
onTap: (BuildContext context) {
Navigator.pop(context);
Navigator.of(context).pushNamed(Routes.connectionSync);
},
);
static SettingActions walletSettingAction = SettingActions._(
name: S.current.wallets,
image: 'assets/images/wallet_menu.png',
onTap: (BuildContext context) {
Navigator.pop(context);
Navigator.of(context).pushNamed(Routes.walletList);
},
);
static SettingActions addressBookSettingAction = SettingActions._(
name: S.current.address_book_menu,
image: 'assets/images/open_book_menu.png',
onTap: (BuildContext context) {
Navigator.pop(context);
Navigator.of(context).pushNamed(Routes.addressBook);
},
);
static SettingActions securityBackupSettingAction = SettingActions._(
name: S.current.security_and_backup,
image: 'assets/images/key_menu.png',
onTap: (BuildContext context) {
Navigator.pop(context);
Navigator.of(context).pushNamed(Routes.securityBackupPage);
},
);
static SettingActions privacySettingAction = SettingActions._(
name: S.current.privacy,
image: 'assets/images/privacy_menu.png',
onTap: (BuildContext context) {
Navigator.pop(context);
Navigator.of(context).pushNamed(Routes.privacyPage);
},
);
static SettingActions displaySettingAction = SettingActions._(
name: S.current.display_settings,
image: 'assets/images/eye_menu.png',
onTap: (BuildContext context) {
Navigator.pop(context);
Navigator.of(context).pushNamed(Routes.displaySettingsPage);
},
);
static SettingActions otherSettingAction = SettingActions._(
name: S.current.other_settings,
image: 'assets/images/settings_menu.png',
onTap: (BuildContext context) {
Navigator.pop(context);
Navigator.of(context).pushNamed(Routes.otherSettingsPage);
},
);
static SettingActions supportSettingAction = SettingActions._(
name: S.current.settings_support,
image: 'assets/images/question_mark.png',
onTap: (BuildContext context) {
Navigator.pop(context);
Navigator.of(context).pushNamed(Routes.support);
},
);
}